Init submit all file include maven build file

This commit is contained in:
YuCheng Hu 2023-10-02 00:10:21 -04:00
parent 75080199d5
commit d4b35c4400
65 changed files with 14986 additions and 0 deletions

10
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Zeppelin ignored files
/ZeppelinRemoteNotebooks/

16
.idea/checkstyle-idea.xml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA" serialisationVersion="2">
<checkstyleVersion>10.12.3</checkstyleVersion>
<scanScope>JavaOnly</scanScope>
<copyLibs>true</copyLibs>
<option name="thirdPartyClasspath" />
<option name="activeLocationIds" />
<option name="locations">
<list>
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
</list>
</option>
</component>
</project>

19
.idea/compiler.xml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="discourse-j-client" />
<module name="discourse-j-common" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
<module name="wechat-j-common" target="11" />
<module name="wechat-j-oa" target="11" />
</bytecodeTargetLevel>
</component>
</project>

11
.idea/encodings.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/client/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/client/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

30
.idea/jarRepositories.xml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="ossez-repo-snapshots" />
<option name="name" value="OSSEZ Private Snapshots" />
<option name="url" value="https://repo.ossez.com/repository/maven-snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="ossez-repo-releases" />
<option name="name" value="OSSEZ Private Releases" />
<option name="url" value="https://repo.ossez.com/repository/maven-releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.ossez.com/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

6
.idea/jpa-buddy.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JpaBuddyIdeaProjectConfig">
<option name="renamerInitialized" value="true" />
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

49
README.md Normal file
View File

@ -0,0 +1,49 @@
# Discourse-J - Discourse API Java SDK
<p align="center">
<a href="https://github.com/honeymoose">
<img height=85 src="https://avatars1.githubusercontent.com/u/45009982?s=200&v=4">
</a>
<br>This project builds by JDK 11 and OpenJ9 for JVM.
</p>
* [社区和讨论 (community)](https://www.isharkfly.com/c/open-source/discourse/8)
Discourse-J is to implement simple & readable Java code, reusable to communicate with Discourse API endpoints.
Document for Discourse API located at :https://docs.discourse.org/
# 联系方式
请使用下面的联系方式和我们联系。
* [社区和讨论](https://www.ossez.com/tag/chat-gpt)
| 联系方式名称 | 联系方式 |
|------------------|-----------------------------------------------|
| 电子邮件Email | [yhu@ossez.com](mailto:yhu@ossez.com) |
| QQ 或微信WeChat | 103899765 |
| QQ 交流群 | 15186112 |
| 社区论坛 Community | https://www.ossez.com/c/computer-technology/7 |
# 公众平台
我们建议您通过社区论坛来和我们进行沟通,请关注我们公众平台上的账号
## 微信公众号
![](https://cdn.ossez.com/img/cwikius/cwikius-qr-wechat-search-w400.png)
## 头条号
我们也在头条号上创建了我们的公众号,请扫描下面的 QR 关注我们的头条号。
![](https://cdn.ossez.com/img/cwikius/cwikus-qr-toutiao.png)
## 知乎
请关注我们的知乎https://www.zhihu.com/people/huyuchengus
# License
[WeChat-J is licensed under the MIT License](https://src.ossez.com/honeymoose/WeChat-J/src/branch/main/LICENSE)

9
client/README.md Normal file
View File

@ -0,0 +1,9 @@
# WeChat Java Official Account
本模块是针对微信的公众号的。
根据微信官方网站https://mp.weixin.qq.com/ 中的内容,微信公众平台有 3 个账号。
* 订阅号
* 服务号
* 小程序
小程序的实现通常是单独处理的,所以这个模块主要为订阅号和服务号的接口提供服务。

129
client/pom.xml Normal file
View File

@ -0,0 +1,129 @@
<?xml version="1.0"?>
<project
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ossez.discourse</groupId>
<artifactId>discourse-j-client</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.ossez.discourse</groupId>
<artifactId>discourse-j</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<name>Discourse Java Client</name>
<description>The module is Discourse API Client</description>
<dependencies>
<dependency>
<groupId>com.ossez.discourse</groupId>
<artifactId>discourse-j-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-guava</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.11.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native-image</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<annotationProcessors>
com.github.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor
</annotationProcessors>
<annotationProcessorPaths>
<path>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-graal</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,25 @@
package com.ossez.discourse.client;
import okhttp3.HttpUrl;
import okhttp3.Request;
/**
*
*/
public abstract class DiscourseClient {
public static String site_url = "https://www.zchub.net/";// base url. e.g. http://your_discourse_domain.com
public static String api_key = "";
public static String api_username = "";
public Request getRequest(String path) {
HttpUrl.Builder urlBuilder = HttpUrl.parse(site_url + path).newBuilder();
Request request = new Request.Builder().url(urlBuilder.build().toString())
.addHeader("api-username", api_username)
.addHeader("api-key", api_key)
.build();
return request;
}
}

View File

@ -0,0 +1,26 @@
package com.ossez.discourse.client.interceptor;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
/**
* OkHttp Interceptor that adds an authorization token header
*/
public class AuthenticationInterceptor implements Interceptor {
private final String token;
public AuthenticationInterceptor(String token) {
this.token = token;
}
@Override
public Response intercept(Chain chain) throws IOException {
HttpUrl url = chain.request().url().newBuilder().addQueryParameter("access_token", token).build();
Request request = chain.request().newBuilder().url(url).build();
// .header("Authorization", "Bearer " + token)
return chain.proceed(request);
}
}

View File

@ -0,0 +1,50 @@
package com.ossez.discourse.client.interceptor;
import com.ossez.discourse.common.constant.HttpClientMediaType;
import okhttp3.*;
import okio.BufferedSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* The reason we have Interceptor here was because will return code 200 with error message.
* <p>
* For example, if we have error secret and try to get access token.
* WeChat will return code 200 success with json error message like:
*
* <pre>
* {"errcode":40125,"errmsg":"invalid appsecret rid: 63cf14c3-1af7da21-37efbc86"}
* </pre>
* <p>
* We need to check the response content, if the response content has errcode.
* We Interceptor response with error code 400 and add error json format to response body.
*
* @author YuCheng Hu
*/
public class ErrorInterceptor implements Interceptor {
final static Logger log = LoggerFactory.getLogger(ErrorInterceptor.class);
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
String responseStr = source.getBuffer().clone().readString(StandardCharsets.UTF_8).toString();
log.debug("WeChat Response String - {}",responseStr);
// if (hasError(responseStr) ) {
// responseBody = ResponseBody.create(MediaType.get(HttpClientMediaType.APPLICATION_JSON), responseStr);
// return response.newBuilder().code(400).body(responseBody).build();
// }
return response;
}
}

View File

@ -0,0 +1,57 @@
package com.ossez.discourse.client.service;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ossez.discourse.common.model.dto.Topics;
import com.ossez.discourse.client.DiscourseClient;
import okhttp3.*;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Optional;
/**
* WeChat Official Account Platform related Service
*
* @author YuCheng
*/
public class TopicService extends DiscourseClient {
private final Logger log = LoggerFactory.getLogger(TopicService.class);
private WeChatOfficialAccountApi weChatOfficialAccountApi;
private OkHttpClient client = new OkHttpClient();
public TopicService(String siteUrl, String apiName, String apiKey) {
DiscourseClient.site_url = siteUrl;
DiscourseClient.api_username = apiName;
DiscourseClient.api_key = apiKey;
}
public Optional<Topics> getTopic(Long topicId) {
String path = "t/" + String.valueOf(topicId) + ".json";
Optional<Topics> discourseTopic = Optional.ofNullable(new Topics());
try {
Response response = client.newCall(getRequest(path)).execute();
String responseStr = response.body().string();
if (response.code() == HttpStatus.SC_OK) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
discourseTopic = Optional.of(objectMapper.readValue(responseStr, Topics.class));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return discourseTopic;
}
}

View File

@ -0,0 +1,63 @@
package com.ossez.discourse.client.service;
import com.ossez.discourse.common.model.WeChatApiDomainIp;
import com.ossez.discourse.common.model.WeChatStatus;
import com.ossez.discourse.common.model.req.CustomMessage;
import com.ossez.discourse.common.model.req.DataCubeRequest;
import com.ossez.discourse.common.model.req.NetworkCheck;
import com.ossez.discourse.common.model.req.QueryQuota;
import com.ossez.discourse.common.model.res.DataCubeArticle;
import com.ossez.discourse.common.model.res.DataCubeUser;
import com.ossez.discourse.common.model.res.NetworkCheckResponse;
import com.ossez.discourse.common.model.res.QueryQuotaResponse;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
public interface WeChatOfficialAccountApi {
@GET("/cgi-bin/get_api_domain_ip")
Single<WeChatApiDomainIp> getDomainIPs();
@POST("/cgi-bin/callback/check")
Single<NetworkCheckResponse> checkNetwork(@Body NetworkCheck request);
@POST("/cgi-bin/clear_quota")
Single<NetworkCheckResponse> clearQuota(@Body NetworkCheck request);
@POST("/cgi-bin/openapi/quota/get")
Single<QueryQuotaResponse> queryQuota(@Body QueryQuota request);
@POST("/cgi-bin/message/custom/send")
Single<WeChatStatus> sendMessage(@Body CustomMessage customMessage);
// MENU
@GET("/cgi-bin/get_current_selfmenu_info")
Single<WeChatStatus> getMenu();
// DATA ANALYST
@POST("/datacube/getusersummary")
Single<DataCubeUser> getUserSummary(@Body DataCubeRequest dataCubeRequest);
@POST("/datacube/getusercumulate")
Single<DataCubeUser> getUserCumulate(@Body DataCubeRequest dataCubeRequest);
@POST("/datacube/getarticlesummary")
Single<DataCubeArticle> getarticlesummary(@Body DataCubeRequest dataCubeRequest);
@POST("/datacube/getarticletotal")
Single<DataCubeUser> getarticletotal(@Body DataCubeRequest dataCubeRequest);
@POST("/datacube/getuserread")
Single<DataCubeUser> getuserread(@Body DataCubeRequest dataCubeRequest);
@POST("/datacube/getuserreadhour")
Single<DataCubeUser> getuserreadhour(@Body DataCubeRequest dataCubeRequest);
@POST("/datacube/getusershare")
Single<DataCubeUser> getusershare(@Body DataCubeRequest dataCubeRequest);
@POST("/datacube/getusersharehour")
Single<DataCubeUser> getusersharehour(@Body DataCubeRequest dataCubeRequest);
}

View File

@ -0,0 +1,29 @@
package com.ossez.discourse.client.util;
/**
* @author yd
* created on 2019-03-20 22:06
*/
public class WxMpConfigStorageHolder {
private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "default";
}
};
public static String get() {
return THREAD_LOCAL.get();
}
public static void set(String label) {
THREAD_LOCAL.set(label);
}
/**
* 此方法需要用户根据自己程序代码在适当位置手动触发调用本SDK里无法判断调用时机
*/
public static void remove() {
THREAD_LOCAL.remove();
}
}

View File

@ -0,0 +1,79 @@
package com.ossez.discourse.client.test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.ossez.discourse.client.DiscourseClient;
import org.apache.commons.lang3.ObjectUtils;
import org.junit.jupiter.api.BeforeAll;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Init Guice DI
*
* @author YuCheng
*/
public class TestBase {
private static final Logger log = LoggerFactory.getLogger(TestBase.class);
private static final String TEST_CONFIG_PROPERTIES= "test-config.properties";
@BeforeAll
public void setup() {
injector.injectMembers(this);
}
private static final Injector injector = Guice.createInjector(new AbstractModule() {
@Override
public void configure() {
try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(TEST_CONFIG_PROPERTIES)) {
if (ObjectUtils.isEmpty(inputStream)) {
// throw new WxRuntimeException("测试配置文件【" + TEST_CONFIG_XML + "】未找到请参照test-config-sample.xml文件生成");
}
Properties prop = new Properties();
prop.load(inputStream);
DiscourseClient.site_url = prop.getProperty("api.site.url");
DiscourseClient.api_username = prop.getProperty("api.username");
DiscourseClient.api_key = prop.getProperty("api.key");
// Init WeChat config for testing
// Document document = new SAXReader().read(inputStream);
// TestConfigStorage config = new TestConfigStorage();
// config.setAppId(document.getRootElement().element("appId").getText());
// config.setSecret(document.getRootElement().element("secret").getText());
// config.setToken(document.getRootElement().element("token").getText());
// config.setOpenid(document.getRootElement().element("openid").getText());
// config.setAccessTokenLock(new ReentrantLock());
// Init WeChat Service
// WeChatOfficialAccountService weChatOfficialAccountService = new WeChatOfficialAccountServiceOkHttp();
// weChatOfficialAccountService.setWxMpConfigStorage(config);
// weChatOfficialAccountService.addConfigStorage("another", config);
// Init WeChatMsgService
// WeChatMsgService weChatMsgService = new WeChatMsgService(weChatOfficialAccountService);
// WeChatDataCubeService weChatDataCubeService = new WeChatDataCubeService(weChatOfficialAccountService);
// WeChatMenuService weChatMenuService = new WeChatMenuService(weChatOfficialAccountService);
// bind(TestConfigStorage.class).toInstance(config);
// bind(WeChatOfficialAccountService.class).toInstance(weChatOfficialAccountService);
// bind(WeChatMsgService.class).toInstance(weChatMsgService);
// bind(WeChatDataCubeService.class).toInstance(weChatDataCubeService);
// bind(WeChatMenuService.class).toInstance(weChatMenuService);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
});
}

View File

@ -0,0 +1,17 @@
package com.ossez.discourse.client.test;
/**
* <pre>
* 仅供测试使用的一些常量
* Created by Binary Wang on 2017-3-9.
* </pre>
*/
public class TestConstants {
///////////////////////
// 文件类型
///////////////////////
public static final String FILE_JPG = "jpeg";
public static final String FILE_MP3 = "mp3";
public static final String FILE_AMR = "amr";
public static final String FILE_MP4 = "mp4";
}

View File

@ -0,0 +1,36 @@
package com.ossez.discourse.client.test;
import com.google.inject.Inject;
import com.ossez.discourse.client.service.TopicService;
import com.ossez.discourse.common.exception.WxErrorException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test for datacube API
*
* @author YuCheng
*/
@TestInstance(Lifecycle.PER_CLASS)
public class TopicsServiceTest extends TestBase {
private static final Logger log = LoggerFactory.getLogger(TopicsServiceTest.class);
@Inject
protected TopicService topicService;
/**
* Test Create Menu
*
* @throws WxErrorException
*/
@Test
public void testCreate() throws WxErrorException {
log.debug("Create WeChat Offical Account Menun Test");
log.debug("{}", topicService.getTopic(Long.valueOf("1245")).get().getTitle());
}
}

View File

@ -0,0 +1,4 @@
# --- APP ---
api.site.url=https://www.zchub.net/
api.username=******
api.key=******

186
common/pom.xml Normal file
View File

@ -0,0 +1,186 @@
<?xml version="1.0"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ossez.discourse</groupId>
<artifactId>discourse-j-common</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.ossez.discourse</groupId>
<artifactId>discourse-j</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<name>Discourse Java Common</name>
<description>The module is common for all other package</description>
<dependencies>
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-guava</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
<exclusions>
<exclusion>
<groupId>pull-parser</groupId>
<artifactId>pull-parser</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>com.github.jedis-lock</groupId>
<artifactId>jedis-lock</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native-image</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<annotationProcessors>
com.github.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor
</annotationProcessors>
<annotationProcessorPaths>
<path>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-graal</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,20 @@
package com.ossez.discourse.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <pre>
* 标识某个字段是否是必填的
* Created by Binary Wang on 2016/9/25.
* </pre>
*
* @author binarywang (https://github.com/binarywang)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Required {
}

View File

@ -0,0 +1,68 @@
package com.ossez.discourse.common.config;
import com.ossez.discourse.common.constant.WeChatConstant;
/**
* 微信接口地址域名部分的自定义设置信息.
*/
public class WxMpHostConfig {
/**
* 对应于https://api.weixin.qq.com
*/
private String apiHost;
/**
* 对应于https://open.weixin.qq.com
*/
private String openHost;
/**
* 对应于https://mp.weixin.qq.com
*/
private String mpHost;
public static String buildUrl(WxMpHostConfig hostConfig, String prefix, String path) {
if (hostConfig == null) {
return prefix + path;
}
if (hostConfig.getApiHost() != null && prefix.equals(WeChatConstant.ENDPOINT_WECHAT)) {
return hostConfig.getApiHost() + path;
}
if (hostConfig.getMpHost() != null && prefix.equals(WeChatConstant.ENDPOINT_MP)) {
return hostConfig.getMpHost() + path;
}
if (hostConfig.getOpenHost() != null && prefix.equals(WeChatConstant.ENDPOINT_OPEN)) {
return hostConfig.getOpenHost() + path;
}
return prefix + path;
}
public String getApiHost() {
return apiHost;
}
public void setApiHost(String apiHost) {
this.apiHost = apiHost;
}
public String getOpenHost() {
return openHost;
}
public void setOpenHost(String openHost) {
this.openHost = openHost;
}
public String getMpHost() {
return mpHost;
}
public void setMpHost(String mpHost) {
this.mpHost = mpHost;
}
}

View File

@ -0,0 +1,16 @@
package com.ossez.discourse.common.constant;
import com.google.common.net.MediaType;
/**
* HttpClient send request the type constant.
* Because okHttp need to use MediaType to construct object, but if we use guava MediaType, the two package will get conflict
* at the same class.
* <p>
* So, we get MediaType String first before we convert to okHttp MediaType.
*
* @author YuCheng Hu
*/
public final class HttpClientMediaType {
public static final String APPLICATION_JSON = MediaType.JSON_UTF_8.toString();
}

View File

@ -0,0 +1,154 @@
package com.ossez.discourse.common.constant;
/**
* <pre>
* 微信公众号事件的相关常量
* Created by Binary Wang on 2017-5-10.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class OfficialAccountEventConstants {
/**
* 门店审核事件.
*/
public static final String POI_CHECK_NOTIFY = "poi_check_notify";
/**
* 接收会员信息事件.
*/
public static final String SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info";
/**
* 微信摇一摇周边>>摇一摇事件通知.
*/
public static final String SHAKEAROUND_USER_SHAKE = "ShakearoundUserShake";
/**
* 卡券相关事件.
*/
public static class Card {
public static final String CARD_PASS_CHECK = "card_pass_check";
public static final String CARD_NOT_PASS_CHECK = "card_not_pass_check";
public static final String USER_GET_CARD = "user_get_card";
public static final String USER_DEL_CARD = "user_del_card";
public static final String USER_CONSUME_CARD = "user_consume_card";
public static final String USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell";
public static final String USER_VIEW_CARD = "user_view_card";
public static final String USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card";
/**
* 卡券转赠事件.
*/
public static final String USER_GIFTING_CARD = "user_gifting_card";
/**
* 库存报警.
*/
public static final String CARD_SKU_REMIND = "card_sku_remind";
/**
* 会员卡内容更新事件.
*/
public static final String UPDATE_MEMBER_CARD = "update_member_card";
/**
* 券点流水详情事件.
*/
public static final String CARD_PAY_ORDER = "card_pay_order";
/**
* 用户购买礼品卡付款成功事件.
*/
public static final String GIFTCARD_PAY_DONE = "giftcard_pay_done";
/**
* 用户购买后赠送事件.
*/
public static final String GIFTCARD_SEND_TO_FRIEND = "giftcard_send_to_friend";
/**
* 用户领取礼品卡成功事件.
*/
public static final String GIFTCARD_USER_ACCEPT = "giftcard_user_accept";
}
/**
* 客服相关事件.
*/
public static class CustomerService {
/**
* 客服接入会话.
*/
public static final String KF_CREATE_SESSION = "kf_create_session";
/**
* 客服关闭会话.
*/
public static final String KF_CLOSE_SESSION = "kf_close_session";
/**
* 客服转接会话.
*/
public static final String KF_SWITCH_SESSION = "kf_switch_session";
}
/**
* 微信认证事件.
*/
public static class Qualification {
/**
* 资质认证成功.
*/
public static final String QUALIFICATION_VERIFY_SUCCESS = "qualification_verify_success";
/**
* 资质认证失败.
*/
public static final String QUALIFICATION_VERIFY_FAIL = "qualification_verify_fail";
/**
* 名称认证成功.
*/
public static final String NAMING_VERIFY_SUCCESS = "naming_verify_success";
/**
* 名称认证失败.
*/
public static final String NAMING_VERIFY_FAIL = "naming_verify_fail";
/**
* 年审通知.
*/
public static final String ANNUAL_RENEW = "annual_renew";
/**
* 认证过期失效通知.
*/
public static final String VERIFY_EXPIRED = "verify_expired";
}
/**
* 电子发票.
*/
public static class Invoice {
/**
* 用户授权事件.
*/
public static final String USER_AUTHORIZE_INVOICE = "user_authorize_invoice";
/**
* 统一开票接口-异步通知开票结果.
*/
public static final String CLOUD_INVOICE_INVOICERESULT_EVENT = "cloud_invoice_invoiceresult_event";
}
/**
* 对话助手相关事件
*/
public static class Guide {
/**
* 顾问邀请结果通知事件.
*/
public static final String GUIDE_INVITE_RESULT_EVENT = "guide_invite_result_event";
}
}

View File

@ -0,0 +1,5 @@
package com.ossez.discourse.common.constant;
public class WeChatApiParameter {
public static final String ACCESS_TOKEN_GRANT_TYPE_CLIENT_CREDENTIAL = "client_credential";
}

View File

@ -0,0 +1,556 @@
package com.ossez.discourse.common.constant;
import com.ossez.discourse.common.enums.WeChatErrorCode;
import java.util.*;
/**
* The constant of WeChat
*
* @author YuCheng Hu
*/
public class WeChatConstant {
public static final String ENDPOINT_WECHAT = "https://api.weixin.qq.com";
public static final String ENDPOINT_MP = "https://mp.weixin.qq.com";
public static final String ENDPOINT_OPEN = "https://open.weixin.qq.com";
/**
* access_token 相关错误代码
* <pre>
* 发生以下情况时尝试刷新access_token
* 40001 获取access_token时AppSecret错误或者access_token无效
* 42001 access_token超时
* 40014 不合法的access_token请开发者认真比对access_token的有效性如是否过期或查看是否正在为恰当的公众号调用接口
* </pre>
*/
public static final EnumSet<WeChatErrorCode> ACCESS_TOKEN_ERROR_CODES_SET = EnumSet.of(WeChatErrorCode.CODE_40001, WeChatErrorCode.CODE_40014, WeChatErrorCode.CODE_42001);
@Deprecated
public static final List<Integer> ACCESS_TOKEN_ERROR_CODES = Arrays.asList(WeChatErrorCode.CODE_40001.getCode(),
WeChatErrorCode.CODE_40014.getCode(), WeChatErrorCode.CODE_42001.getCode());
/**
* 微信推送过来的消息的类型和发送给微信xml格式消息的消息类型.
*/
public static class XmlMsgType {
public static final String TEXT = "text";
public static final String IMAGE = "image";
public static final String VOICE = "voice";
public static final String SHORTVIDEO = "shortvideo";
public static final String VIDEO = "video";
public static final String NEWS = "news";
public static final String MUSIC = "music";
public static final String LOCATION = "location";
public static final String LINK = "link";
public static final String EVENT = "event";
public static final String DEVICE_TEXT = "device_text";
public static final String DEVICE_EVENT = "device_event";
public static final String DEVICE_STATUS = "device_status";
public static final String HARDWARE = "hardware";
public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service";
public static final String UPDATE_TASKCARD = "update_taskcard";
public static final String UPDATE_BUTTON = "update_button";
}
/**
* 主动发送消息(即客服消息)的消息类型.
*/
public static class MsgType {
/**
* 消息类型:
* text(文本)
* image(图片)
* voice(语音)
* video(视频)
* music(音乐)
* news(图文消息 - 点击跳转到外链)
* mpnews(图文消息 - 点击跳转到图文消息页面)
* wxcard(卡券)
* miniprogrampage(小程序)
* markdown(目前仅支持markdown语法的子集微工作台原企业号不支持展示markdown消息)
* file(发送文件 - CP专用)
* textcard(文本卡片消息 - CP专用)
* wxcard(卡券消息)
*/
public static final String TEXT = "text";
public static final String IMAGE = "image";
public static final String VOICE = "voice";
public static final String VIDEO = "video";
public static final String MUSIC = "music";
public static final String NEWS = "news";
public static final String MPNEWS = "mpnews";
public static final String MARKDOWN = "markdown";
public static final String FILE = "file";
public static final String TEXTCARD = "textcard";
public static final String WXCARD = "wxcard";
/**
* 转发到客服的消息.
*/
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";
/**
* 模板卡片消息.
*/
public static final String TEMPLATE_CARD = "template_card";
/**
* 发送图文消息点击跳转到图文消息页面使用通过 发布 系列接口得到的 article_id(草稿箱功能上线后不再支持客服接口中带 media_id mpnews 类型的图文消息)
*/
public static final String MP_NEWS_ARTICLE = "mpnewsarticle";
}
/**
* 发送学校通知类型
* https://developer.work.weixin.qq.com/document/path/92321
*/
public static class SchoolContactMsgType {
/**
* 文本消息.
*/
public static final String TEXT = "text";
/**
* 图片消息.
*/
public static final String IMAGE = "image";
/**
* 语音消息.
*/
public static final String VOICE = "voice";
/**
* 视频消息.
*/
public static final String VIDEO = "video";
/**
* 文件消息
*/
public static final String FILE = "file";
/**
* 图文消息
*/
public static final String NEWS = "news";
/**
* 图文消息mpnews
*/
public static final String MPNEWS = "mpnews";
/**
* 小程序消息
*/
public static final String MINIPROGRAM = "miniprogram";
}
/**
* 企业微信模板卡片消息的卡片类型
*/
public static class TemplateCardType {
/**
* 文本通知型卡片
*/
public static final String TEXT_NOTICE = "text_notice";
/**
* 图文展示型卡片
*/
public static final String NEWS_NOTICE = "news_notice";
/**
* 按钮交互型卡片
*/
public static final String BUTTON_INTERACTION = "button_interaction";
/**
* 投票选择型卡片
*/
public static final String VOTE_INTERACTION = "vote_interaction";
/**
* 多项选择型卡片
*/
public static final String MULTIPLE_INTERACTION = "multiple_interaction";
}
/**
* 表示是否是保密消息0表示否1表示是默认0.
*/
public static class KefuMsgSafe {
public static final String NO = "0";
public static final String YES = "1";
}
/**
* 群发消息的消息类型.
*/
public static class MassMsgType {
public static final String MPNEWS = "mpnews";
public static final String TEXT = "text";
public static final String VOICE = "voice";
public static final String IMAGE = "image";
public static final String IMAGES = "images";
public static final String MPVIDEO = "mpvideo";
}
/**
* 群发消息后微信端推送给服务器的反馈消息.
*/
public static class MassMsgStatus {
public static final String SEND_SUCCESS = "send success";
public static final String SEND_FAIL = "send fail";
public static final String ERR_10001 = "err(10001)";
public static final String ERR_20001 = "err(20001)";
public static final String ERR_20004 = "err(20004)";
public static final String ERR_20002 = "err(20002)";
public static final String ERR_20006 = "err(20006)";
public static final String ERR_20008 = "err(20008)";
public static final String ERR_20013 = "err(20013)";
public static final String ERR_22000 = "err(22000)";
public static final String ERR_21000 = "err(21000)";
public static final String ERR_30001 = "err(30001)";
public static final String ERR_30002 = "err(30002)";
public static final String ERR_30003 = "err(30003)";
public static final String ERR_40001 = "err(40001)";
public static final String ERR_40002 = "err(40002)";
/**
* 群发反馈消息代码所对应的文字描述.
*/
public static final Map<String, String> STATUS_DESC = new HashMap<>();
static {
STATUS_DESC.put(SEND_SUCCESS, "发送成功");
STATUS_DESC.put(SEND_FAIL, "发送失败");
STATUS_DESC.put(ERR_10001, "涉嫌广告");
STATUS_DESC.put(ERR_20001, "涉嫌政治");
STATUS_DESC.put(ERR_20004, "涉嫌社会");
STATUS_DESC.put(ERR_20002, "涉嫌色情");
STATUS_DESC.put(ERR_20006, "涉嫌违法犯罪");
STATUS_DESC.put(ERR_20008, "涉嫌欺诈");
STATUS_DESC.put(ERR_20013, "涉嫌版权");
STATUS_DESC.put(ERR_22000, "涉嫌互推_互相宣传");
STATUS_DESC.put(ERR_21000, "涉嫌其他");
STATUS_DESC.put(ERR_30001, "原创校验出现系统错误且用户选择了被判为转载就不群发");
STATUS_DESC.put(ERR_30002, "原创校验被判定为不能群发");
STATUS_DESC.put(ERR_30003, "原创校验被判定为转载文且用户选择了被判为转载就不群发");
STATUS_DESC.put(ERR_40001, "管理员拒绝");
STATUS_DESC.put(ERR_40002, "管理员30分钟内无响应超时");
}
}
/**
* 微信端推送过来的事件类型.
*/
public static class EventType {
public static final String SUBSCRIBE = "subscribe";
public static final String UNSUBSCRIBE = "unsubscribe";
public static final String SCAN = "SCAN";
public static final String LOCATION = "LOCATION";
public static final String CLICK = "CLICK";
public static final String VIEW = "VIEW";
public static final String MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH";
/**
* 扫码推事件的事件推送
*/
public static final String SCANCODE_PUSH = "scancode_push";
/**
* 扫码推事件且弹出消息接收中提示框的事件推送.
*/
public static final String SCANCODE_WAITMSG = "scancode_waitmsg";
/**
* 弹出系统拍照发图的事件推送.
*/
public static final String PIC_SYSPHOTO = "pic_sysphoto";
/**
* 弹出拍照或者相册发图的事件推送.
*/
public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album";
/**
* 弹出微信相册发图器的事件推送.
*/
public static final String PIC_WEIXIN = "pic_weixin";
/**
* 弹出地理位置选择器的事件推送.
*/
public static final String LOCATION_SELECT = "location_select";
/**
* 授权用户资料变更事件
* 1 当部分用户的资料存在风险时平台会对用户资料进行清理并通过消息推送服务器通知最近30天授权过的公众号开发者我们建议开发者留意响应该事件及时主动更新或清理用户的头像及昵称降低风险
* 2 当用户撤回授权信息时平台会通过消息推送服务器通知给公众号开发者请开发者注意及时删除用户信息
*/
public static final String USER_INFO_MODIFIED = "user_info_modified";
/**
* 用户撤回授权事件
*/
public static final String USER_AUTHORIZATION_REVOKE = "user_authorization_revoke";
/**
* 群发模板回调事件
*/
public static final String TEMPLATE_SEND_JOB_FINISH = "TEMPLATESENDJOBFINISH";
/**
* 微信小店 订单付款通知.
*/
public static final String MERCHANT_ORDER = "merchant_order";
/**
* 卡券事件卡券通过审核
*/
public static final String CARD_PASS_CHECK = "card_pass_check";
/**
* 卡券事件卡券未通过审核
*/
public static final String CARD_NOT_PASS_CHECK = "card_not_pass_check";
/**
* 卡券事件用户领取卡券
*/
public static final String CARD_USER_GET_CARD = "user_get_card";
/**
* 卡券事件用户转赠卡券
*/
public static final String CARD_USER_GIFTING_CARD = "user_gifting_card";
/**
* 异步安全校验事件
*/
public static final String WXA_MEDIA_CHECK = "wxa_media_check";
/**
* 卡券事件用户核销卡券
*/
public static final String CARD_USER_CONSUME_CARD = "user_consume_card";
/**
* 卡券事件用户通过卡券的微信买单完成时推送
*/
public static final String CARD_USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell";
/**
* 卡券事件用户提交会员卡开卡信息
*/
public static final String CARD_SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info";
/**
* 卡券事件用户打开查看卡券
*/
public static final String CARD_USER_VIEW_CARD = "user_view_card";
/**
* 卡券事件用户删除卡券
*/
public static final String CARD_USER_DEL_CARD = "user_del_card";
/**
* 卡券事件用户在卡券里点击查看公众号进入会话时需要用户已经关注公众号
*/
public static final String CARD_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card";
/**
* 卡券事件当用户的会员卡积分余额发生变动时
*/
public static final String CARD_UPDATE_MEMBER_CARD = "update_member_card";
/**
* 卡券事件当某个card_id的初始库存数大于200且当前库存小于等于100时用户尝试领券会触发发送事件给商户事件每隔12h发送一次
*/
public static final String CARD_SKU_REMIND = "card_sku_remind";
/**
* 卡券事件当商户朋友的券券点发生变动时
*/
public static final String CARD_PAY_ORDER = "card_pay_order";
/**
* 小程序审核事件审核通过
*/
public static final String WEAPP_AUDIT_SUCCESS = "weapp_audit_success";
/**
* 小程序审核事件审核不通过
*/
public static final String WEAPP_AUDIT_FAIL = "weapp_audit_fail";
/**
* 小程序自定义交易组件支付通知
*/
public static final String OPEN_PRODUCT_ORDER_PAY = "open_product_order_pay";
/**
* 点击菜单跳转小程序的事件推送
*/
public static final String VIEW_MINIPROGRAM = "view_miniprogram";
/**
* 订阅通知事件用户操作订阅通知弹窗
*/
public static final String SUBSCRIBE_MSG_POPUP_EVENT = "subscribe_msg_popup_event";
/**
* 订阅通知事件用户管理订阅通知
*/
public static final String SUBSCRIBE_MSG_CHANGE_EVENT = "subscribe_msg_change_event";
/**
* 订阅通知事件发送订阅通知回调
*/
public static final String SUBSCRIBE_MSG_SENT_EVENT = "subscribe_msg_sent_event";
}
/**
* 上传多媒体临时素材文件的类型.
*/
public static class MediaFileType {
public static final String IMAGE = "image";
public static final String VOICE = "voice";
public static final String VIDEO = "video";
public static final String THUMB = "thumb";
public static final String FILE = "file";
}
/**
* 自定义菜单的按钮类型.
*/
public static class MenuButtonType {
/**
* 点击推事件.
*/
public static final String CLICK = "click";
/**
* 跳转URL.
*/
public static final String VIEW = "view";
/**
* 跳转到小程序.
*/
public static final String MINIPROGRAM = "miniprogram";
/**
* 扫码推事件.
*/
public static final String SCANCODE_PUSH = "scancode_push";
/**
* 扫码推事件且弹出消息接收中提示框.
*/
public static final String SCANCODE_WAITMSG = "scancode_waitmsg";
/**
* 弹出系统拍照发图.
*/
public static final String PIC_SYSPHOTO = "pic_sysphoto";
/**
* 弹出拍照或者相册发图.
*/
public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album";
/**
* 弹出微信相册发图器.
*/
public static final String PIC_WEIXIN = "pic_weixin";
/**
* 弹出地理位置选择器.
*/
public static final String LOCATION_SELECT = "location_select";
/**
* 下发消息除文本消息.
*/
public static final String MEDIA_ID = "media_id";
/**
* 跳转图文消息URL.
*/
public static final String VIEW_LIMITED = "view_limited";
}
/**
* oauth2网页授权的scope.
*/
public static class OAuth2Scope {
/**
* 不弹出授权页面直接跳转只能获取用户openid.
*/
public static final String SNSAPI_BASE = "snsapi_base";
/**
* 弹出授权页面可通过openid拿到昵称性别所在地并且即使在未关注的情况下只要用户授权也能获取其信息.
*/
public static final String SNSAPI_USERINFO = "snsapi_userinfo";
/**
* 手动授权,可获取成员的详细信息,包含手机邮箱只适用于企业微信或企业号.
*/
public static final String SNSAPI_PRIVATEINFO = "snsapi_privateinfo";
}
/**
* 网页应用登录授权作用域.
*/
public static class QrConnectScope {
public static final String SNSAPI_LOGIN = "snsapi_login";
}
/**
* 永久素材类型.
*/
public static class MaterialType {
public static final String NEWS = "news";
public static final String VOICE = "voice";
public static final String IMAGE = "image";
public static final String VIDEO = "video";
}
/**
* 网络检测入参.
*/
public static class NetCheckArgs {
public static final String ACTIONDNS = "dns";
public static final String ACTIONPING = "ping";
public static final String ACTIONALL = "all";
public static final String OPERATORUNICOM = "UNICOM";
public static final String OPERATORCHINANET = "CHINANET";
public static final String OPERATORCAP = "CAP";
public static final String OPERATORDEFAULT = "DEFAULT";
}
/**
* appId 类型
*/
public static class AppIdType {
/**
* 公众号appId类型
*/
public static final String MP_TYPE = "mp";
/**
* 小程序appId类型
*/
public static final String MINI_TYPE = "mini";
}
}

View File

@ -0,0 +1,20 @@
package com.ossez.discourse.common.enums;
/**
* The language for WeChat API can support
*
* @author YuCheng Hu
*/
public enum Language {
ZH_CN("zh_CN"), EN_US("en_US");
private String code;
Language(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}

View File

@ -0,0 +1,35 @@
package com.ossez.discourse.common.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* <pre>
* ticket类型枚举
* Created by Binary Wang on 2018/11/18.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Getter
@RequiredArgsConstructor
public enum TicketType {
/**
* jsapi
*/
JSAPI("jsapi"),
/**
* sdk
*/
SDK("2"),
/**
* 微信卡券
*/
WX_CARD("wx_card");
/**
* type代码
*/
private final String code;
}

View File

@ -0,0 +1,207 @@
package com.ossez.discourse.common.enums;
import com.google.common.collect.Maps;
import java.util.Map;
/**
* WeChat Error Code
* <pre>
* 微信公众平台全局返回码.
* 参考文档<a href="https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Global_Return_Code.html">公众平台全局返回码</a>
* </pre>
*
* @author YuCheng Hu
*/
public enum WeChatErrorCode {
CODE_1(-1, "系统繁忙,此时请开发者稍候再试"),
CODE_0(0, "请求成功"),
CODE_40001(40001, "获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口"),
CODE_40002(40002, "不合法的凭证类型"),
CODE_40003(40003, "不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID"),
CODE_40004(40004, "不合法的媒体文件类型"),
CODE_40005(40005, "不合法的文件类型"),
CODE_40006(40006, "不合法的文件大小"),
CODE_40007(40007, "不合法的媒体文件 id"),
CODE_40008(40008, "不合法的消息类型"),
CODE_40009(40009, "不合法的图片文件大小"),
CODE_40010(40010, "不合法的语音文件大小"),
CODE_40011(40011, "不合法的视频文件大小"),
CODE_40012(40012, "不合法的缩略图文件大小"),
CODE_40013(40013, "不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写"),
CODE_40014(40014, "不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口"),
CODE_40015(40015, "不合法的菜单类型"),
CODE_40016(40016, "不合法的按钮个数"),
CODE_40017(40017, "不合法的按钮类型"),
CODE_40018(40018, "不合法的按钮名字长度"),
CODE_40019(40019, "不合法的按钮 KEY 长度"),
CODE_40020(40020, "不合法的按钮 URL 长度"),
CODE_40021(40021, "不合法的菜单版本号"),
CODE_40022(40022, "不合法的子菜单级数"),
CODE_40023(40023, "不合法的子菜单按钮个数"),
CODE_40024(40024, "不合法的子菜单按钮类型"),
CODE_40025(40025, "不合法的子菜单按钮名字长度"),
CODE_40026(40026, "不合法的子菜单按钮 KEY 长度"),
CODE_40027(40027, "不合法的子菜单按钮 URL 长度"),
CODE_40028(40028, "不合法的自定义菜单使用用户"),
CODE_40029(40029, "不合法的 oauth_code"),
CODE_40030(40030, "不合法的 refresh_token"),
CODE_40031(40031, "不合法的 openid 列表"),
CODE_40032(40032, "不合法的 openid 列表长度"),
CODE_40033(40033, "不合法的请求字符,不能包含\\uxxxx 格式的字符"),
CODE_40035(40035, "不合法的参数"),
CODE_40038(40038, "不合法的请求格式"),
CODE_40039(40039, "不合法的 URL 长度"),
CODE_40050(40050, "不合法的分组 id"),
CODE_40051(40051, "分组名字不合法"),
CODE_40060(40060, "删除单篇图文时,指定的 article_idx 不合法"),
CODE_40117(40117, "分组名字不合法"),
CODE_40118(40118, "media_id 大小不合法"),
CODE_40119(40119, "button 类型错误"),
CODE_40120(40120, "button 类型错误"),
CODE_40121(40121, "不合法的 media_id 类型"),
CODE_40132(40132, "微信号不合法"),
CODE_40137(40137, "不支持的图片格式"),
CODE_40155(40155, "请勿添加其他公众号的主页链接"),
CODE_40163(40163, "oauth_code已使用"),
CODE_41001(41001, "缺少 access_token 参数"),
CODE_41002(41002, "缺少 appid 参数"),
CODE_41003(41003, "缺少 refresh_token 参数"),
CODE_41004(41004, "缺少 secret 参数"),
CODE_41005(41005, "缺少多媒体文件数据"),
CODE_41006(41006, "缺少 media_id 参数"),
CODE_41007(41007, "缺少子菜单数据"),
CODE_41008(41008, "缺少 oauth code"),
CODE_41009(41009, "缺少 openid"),
CODE_42001(42001, "access_token 超时,请检查 access_token 的有效期,请参考基础支持 - 获取 access_token 中,对 access_token 的详细机制说明"),
CODE_42002(42002, "refresh_token 超时"),
CODE_42003(42003, "oauth_code 超时"),
CODE_42007(42007, "用户修改微信密码, accesstoken 和 refreshtoken 失效,需要重新授权"),
CODE_43001(43001, "需要 GET 请求"),
CODE_43002(43002, "需要 POST 请求"),
CODE_43003(43003, "需要 HTTPS 请求"),
CODE_43004(43004, "需要接收者关注"),
CODE_43005(43005, "需要好友关系"),
CODE_43019(43019, "需要将接收者从黑名单中移除"),
CODE_44001(44001, "多媒体文件为空"),
CODE_44002(44002, "POST 的数据包为空"),
CODE_44003(44003, "图文消息内容为空"),
CODE_44004(44004, "文本消息内容为空"),
CODE_45001(45001, "多媒体文件大小超过限制"),
CODE_45002(45002, "消息内容超过限制"),
CODE_45003(45003, "标题字段超过限制"),
CODE_45004(45004, "描述字段超过限制"),
CODE_45005(45005, "链接字段超过限制"),
CODE_45006(45006, "图片链接字段超过限制"),
CODE_45007(45007, "语音播放时间超过限制"),
CODE_45008(45008, "图文消息超过限制"),
CODE_45009(45009, "接口调用超过限制"),
CODE_45010(45010, "创建菜单个数超过限制"),
CODE_45011(45011, "API 调用太频繁,请稍候再试"),
CODE_45015(45015, "回复时间超过限制"),
CODE_45016(45016, "系统分组,不允许修改"),
CODE_45017(45017, "分组名字过长"),
CODE_45018(45018, "分组数量超过上限"),
CODE_45047(45047, "客服接口下行条数超过上限"),
CODE_45159(45159, "非法的tag_id"),
CODE_45065(45065, "相同 clientmsgid 已存在群发记录,返回数据中带有已存在的群发任务的 msgid"),
CODE_45066(45066, "相同 clientmsgid 重试速度过快请间隔1分钟重试"),
CODE_45067(45067, "clientmsgid 长度超过限制"),
CODE_46001(46001, "不存在媒体数据"),
CODE_46002(46002, "不存在的菜单版本"),
CODE_46003(46003, "不存在的菜单数据"),
CODE_46004(46004, "不存在的用户"),
CODE_47001(47001, "解析 JSON/XML 内容错误"),
CODE_48001(48001, "api 功能未授权,请确认公众号已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限"),
CODE_48002(48002, "粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” "),
CODE_48004(48004, "api 接口被封禁,请登录 mp.weixin.qq.com 查看详情"),
CODE_48005(48005, "api 禁止删除被自动回复和自定义菜单引用的素材"),
CODE_48006(48006, "api 禁止清零调用次数,因为清零次数达到上限"),
CODE_48008(48008, "没有该类型消息的发送权限"),
CODE_50001(50001, "用户未授权该 api"),
CODE_50002(50002, "用户受限,可能是违规后接口被封禁"),
CODE_50005(50005, "用户未关注公众号"),
CODE_61451(61451, "参数错误 (invalid parameter)"),
CODE_61452(61452, "无效客服账号 (invalid kf_account)"),
CODE_61453(61453, "客服帐号已存在 (kf_account exsited)"),
CODE_61454(61454, "客服帐号名长度超过限制 ( 仅允许 10 个英文字符,不包括 @ 及 @ 后的公众号的微信号 )(invalid kf_acount length)"),
CODE_61455(61455, "客服帐号名包含非法字符 ( 仅允许英文 + 数字 )(illegal character in kf_account)"),
CODE_61456(61456, "客服帐号个数超过限制 (10 个客服账号 )(kf_account count exceeded)"),
CODE_61457(61457, "无效头像文件类型 (invalid file type)"),
CODE_61450(61450, "系统错误 (system error)"),
CODE_61500(61500, "日期格式错误"),
CODE_65301(65301, "不存在此 menuid 对应的个性化菜单"),
CODE_65302(65302, "没有相应的用户"),
CODE_65303(65303, "没有默认菜单,不能创建个性化菜单"),
CODE_65304(65304, "MatchRule 信息为空"),
CODE_65305(65305, "个性化菜单数量受限"),
CODE_65306(65306, "不支持个性化菜单的帐号"),
CODE_65307(65307, "个性化菜单信息为空"),
CODE_65308(65308, "包含没有响应类型的 button"),
CODE_65309(65309, "个性化菜单开关处于关闭状态"),
CODE_65310(65310, "填写了省份或城市信息,国家信息不能为空"),
CODE_65311(65311, "填写了城市信息,省份信息不能为空"),
CODE_65312(65312, "不合法的国家信息"),
CODE_65313(65313, "不合法的省份信息"),
CODE_65314(65314, "不合法的城市信息"),
CODE_65316(65316, "该公众号的菜单设置了过多的域名外跳(最多跳转到 3 个域名的链接)"),
CODE_65317(65317, "不合法的 URL"),
CODE_9001001(9001001, "POST 数据参数不合法"),
CODE_9001002(9001002, "远端服务不可用"),
CODE_9001003(9001003, "Ticket 不合法"),
CODE_9001004(9001004, "获取摇周边用户信息失败"),
CODE_9001005(9001005, "获取商户信息失败"),
CODE_9001006(9001006, "获取 OpenID 失败"),
CODE_9001007(9001007, "上传文件缺失"),
CODE_9001008(9001008, "上传素材的文件类型不合法"),
CODE_9001009(9001009, "上传素材的文件尺寸不合法"),
CODE_9001010(9001010, "上传失败"),
CODE_9001020(9001020, "帐号不合法"),
CODE_9001021(9001021, "已有设备激活率低于 50% ,不能新增设备"),
CODE_9001022(9001022, "设备申请数不合法,必须为大于 0 的数字"),
CODE_9001023(9001023, "已存在审核中的设备 ID 申请"),
CODE_9001024(9001024, "一次查询设备 ID 数量不能超过 50"),
CODE_9001025(9001025, "设备 ID 不合法"),
CODE_9001026(9001026, "页面 ID 不合法"),
CODE_9001027(9001027, "页面参数不合法"),
CODE_9001028(9001028, "一次删除页面 ID 数量不能超过 10"),
CODE_9001029(9001029, "页面已应用在设备中,请先解除应用关系再删除"),
CODE_9001030(9001030, "一次查询页面 ID 数量不能超过 50"),
CODE_9001032(9001032, "保存设备与页面的绑定关系参数错误"),
CODE_9001033(9001033, "门店 ID 不合法"),
CODE_9001034(9001034, "设备备注信息过长"),
CODE_9001035(9001035, "设备申请参数不合法"),
CODE_9001036(9001036, "查询起始值 begin 不合法"),
CODE_45083(45083, "设置的 speed 参数不在0到4的范围内"),
CODE_45084(45084, "没有设置 speed 参数");
private final int code;
private final String msg;
WeChatErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
static final Map<Integer, String> valueMap = Maps.newHashMap();
static {
for (WeChatErrorCode value : WeChatErrorCode.values()) {
valueMap.put(value.code, value.msg);
}
}
/**
* 通过错误代码查找其中文含义..
*/
public static String findMsgByCode(int code) {
return valueMap.getOrDefault(code, null);
}
}

View File

@ -0,0 +1,12 @@
package com.ossez.discourse.common.enums;
/**
* WeChat's Storage Category
*
* We provide implement for MEM(RAM) and REDIS
*
* @author YuCheng Hu
*/
public enum WeChatStorageCategory {
MEM, REDIS
}

View File

@ -0,0 +1,25 @@
package com.ossez.discourse.common.enums;
/**
* 微信卡券
*
* @author YuCheng
*/
public enum WxCardType {
MEMBER_CARD("MEMBER_CARD"),
GROUPON("GROUPON"),
CASH("CASH"),
DISCOUNT("DISCOUNT"),
GIFT("GIFT"),
GENERAL_COUPON("GENERAL_COUPON");
private String code;
WxCardType(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}

View File

@ -0,0 +1,32 @@
package com.ossez.discourse.common.enums;
/**
* <pre>
* 微信类型枚举.
* Created by BinaryWang on 2018/5/14.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public enum WxType {
/**
* 企业微信.
*/
CP,
/**
* 微信公众号.
*/
MP,
/**
* 微信小程序.
*/
MiniApp,
/**
* 微信开放平台.
*/
Open,
/**
* 微信支付.
*/
Pay;
}

View File

@ -0,0 +1,26 @@
package com.ossez.discourse.common.exception;
/**
* @author Daniel Qian
*/
public class DataStructureException extends WxErrorException {
private static final long serialVersionUID = -6357149550353160810L;
private final DiscourseError error;
private static final int DEFAULT_ERROR_CODE = -99;
public DataStructureException(String message) {
this(DiscourseError.builder().errorCode(DEFAULT_ERROR_CODE).errorMsg(message).build());
}
public DataStructureException(DiscourseError error) {
super(error.toString());
this.error = error;
}
public DiscourseError getError() {
return this.error;
}
}

View File

@ -0,0 +1,62 @@
package com.ossez.discourse.common.exception;
import com.ossez.discourse.common.enums.WeChatErrorCode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.ossez.discourse.common.enums.WxType;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
/**
* 微信错误码.
* 请阅读
* 公众平台<a href="https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Global_Return_Code.html">全局返回码说明</a>
* 企业微信<a href="https://work.weixin.qq.com/api/doc#10649">全局错误码</a>
*
* @author Daniel Qian & Binary Wang
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DiscourseError implements Serializable {
private static final long serialVersionUID = -2696724276555657960L;
/**
* 微信错误代码.
*/
private int errorCode;
/**
* 微信错误信息.
* 如果可以翻译为中文就为中文
*/
private String errorMsg;
/**
* 微信接口返回的错误原始信息英文.
*/
private String errorMsgEn;
private String json;
public DiscourseError(int errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
@Override
public String toString() {
if (this.json == null) {
return "错误代码:" + this.errorCode + ", 错误信息:" + this.errorMsg;
}
return "错误代码:" + this.errorCode + ", 错误信息:" + this.errorMsg + ",微信原始报文:" + this.json;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
package com.ossez.discourse.common.exception;
/**
* @author Daniel Qian
*/
public class WxErrorException extends Exception {
private static final long serialVersionUID = -6357149550353160810L;
private final DiscourseError error;
private static final int DEFAULT_ERROR_CODE = -99;
public WxErrorException(String message) {
this(DiscourseError.builder().errorCode(DEFAULT_ERROR_CODE).errorMsg(message).build());
}
public WxErrorException(DiscourseError error) {
super(error.toString());
this.error = error;
}
public WxErrorException(DiscourseError error, Throwable cause) {
super(error.toString(), cause);
this.error = error;
}
public WxErrorException(Throwable cause) {
super(cause.getMessage(), cause);
this.error = DiscourseError.builder().errorCode(DEFAULT_ERROR_CODE).errorMsg(cause.getMessage()).build();
}
public DiscourseError getError() {
return this.error;
}
}

View File

@ -0,0 +1,684 @@
package com.ossez.discourse.common.exception;
import com.google.common.collect.Maps;
import lombok.Getter;
import java.util.Map;
/**
* 微信小程序错误码
*
* @author <a href="https://github.com/biggates">biggates</a>
*/
@Getter
public enum WxMaErrorMsgEnum {
/**
* <pre>
* 获取 access_token AppSecret 错误
* 或者 access_token 无效请开发者认真比对 AppSecret 的正确性或查看是否正在为恰当的小程序调用接口
* 对应操作<code>sendCustomerMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
* </pre>
*/
CODE_40001(40001, "access_token 无效或 AppSecret 错误"),
/**
* <pre>
* 不合法的凭证类型
* 对应操作<code>sendCustomerMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
* </pre>
*/
CODE_40002(40002, "不合法的凭证类型"),
/**
* <pre>
* touser不是正确的openid.
* 对应操作<code>sendCustomerMessage</code>, <code>sendUniformMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
* </pre>
*/
CODE_40003(40003, "openid 不正确"),
/**
* <pre>
* 无效媒体文件类型
* 对应操作<code>uploadTempMedia</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/uploadTempMedia.html
* </pre>
*/
CODE_40004(40004, "无效媒体文件类型"),
/**
* <pre>
* 无效媒体文件 ID.
* 对应操作<code>getTempMedia</code>
* 对应地址
* GET https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/getTempMedia.html
* </pre>
*/
CODE_40007(40007, "无效媒体文件 ID"),
/**
* <pre>
* appid不正确或者不符合绑定关系要求.
* 对应操作<code>sendUniformMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
* </pre>
*/
CODE_40013(40013, "appid不正确或者不符合绑定关系要求"),
/**
* <pre>
* template_id 不正确.
* 对应操作<code>sendUniformMessage</code>, <code>sendTemplateMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html
* </pre>
*/
CODE_40037(40037, "template_id 不正确"),
/**
* <pre>
* form_id不正确或者过期.
* 对应操作<code>sendUniformMessage</code>, <code>sendTemplateMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html
* </pre>
*/
CODE_41028(41028, "form_id 不正确,或者过期"),
/**
* <pre>
* code template_id 不正确.
* 对应操作<code>code2Session</code>, <code>sendUniformMessage</code>, <code>sendTemplateMessage</code>
* 对应地址
* GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html
* </pre>
*/
CODE_41029(41029, "请求的参数不正确"),
/**
* <pre>
* form_id 已被使用或者所传page页面不存在或者小程序没有发布
* 对应操作<code>sendUniformMessage</coce>, <code>getWXACodeUnlimit</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACodeUnlimit.html
* </pre>
*/
CODE_41030(41030, "请求的参数不正确"),
/**
* <pre>
* 调用分钟频率受限.
* 对应操作<code>getWXACodeUnlimit</code>, <code>sendUniformMessage</code>, <code>sendTemplateMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACodeUnlimit.html
* </pre>
*/
CODE_45009(45009, "调用分钟频率受限"),
/**
* <pre>
* 频率限制每个用户每分钟100次.
* 对应操作<code>code2Session</code>
* 对应地址
* GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html
* </pre>
*/
CODE_45011(45011, "频率限制每个用户每分钟100次"),
/**
* <pre>
* 回复时间超过限制.
* 对应操作<code>sendCustomerMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
* </pre>
*/
CODE_45015(45015, "回复时间超过限制"),
/**
* <pre>
* 接口调用超过限额 或生成码个数总和到达最大个数限制.
* 对应操作<code>createWXAQRCode</code>, <code>sendTemplateMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACode.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html
* </pre>
*/
CODE_45029(45029, "接口调用超过限额"),
/**
* <pre>
* 客服接口下行条数超过上限.
* 对应操作<code>sendCustomerMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
* </pre>
*/
CODE_45047(45047, "客服接口下行条数超过上限"),
/**
* <pre>
* command字段取值不对
* 对应操作<code>customerTyping</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html
* </pre>
*/
CODE_45072(45072, "command字段取值不对"),
/**
* <pre>
* 下发输入状态需要之前30秒内跟用户有过消息交互.
* 对应操作<code>customerTyping</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html
*/
CODE_45080(45080, "下发输入状态需要之前30秒内跟用户有过消息交互"),
/**
* <pre>
* 已经在输入状态不可重复下发.
* 对应操作<code>customerTyping</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html
* </pre>
*/
CODE_45081(45081, "已经在输入状态,不可重复下发"),
/**
* <pre>
* API 功能未授权请确认小程序已获得该接口.
* 对应操作<code>sendCustomerMessage</code>
* 对应地址
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html
* </pre>
*/
CODE_48001(48001, "API 功能未授权"),
/**
* <pre>
* 内容含有违法违规内容.
* 对应操作<code>imgSecCheck</code>, <code>msgSecCheck</code>
* 对应地址
* POST https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN
* POST https://api.weixin.qq.com/wxa/msg_sec_check?access_token=ACCESS_TOKEN
* 参考文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/imgSecCheck.html
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/msgSecCheck.html
* </pre>
*/
CODE_87014(87014, "内容含有违法违规内容"),
/**
* 系统繁忙此时请开发者稍候再试.
*/
CODE_MINUS_1(-1, "系统繁忙,此时请开发者稍候再试"),
/**
* code 无效.
*/
CODE_40029(40029, "code 无效"),
/**
* access_token 过期.
*/
CODE_42001(42001, "access_token 过期"),
/**
* post 数据为空.
*/
CODE_44002(44002, "post 数据为空"),
/**
* post 数据中参数缺失.
*/
CODE_47001(47001, "post 数据中参数缺失"),
/**
* 参数 activity_id 错误.
*/
CODE_47501(47501, "参数 activity_id 错误"),
/**
* 参数 target_state 错误.
*/
CODE_47502(47502, "参数 target_state 错误"),
/**
* 参数 version_type 错误.
*/
CODE_47503(47503, "参数 version_type 错误"),
/**
* activity_id 过期.
*/
CODE_47504(47504, "activity_id 过期"),
/**
* 没有绑定开放平台帐号.
*/
CODE_89002(89002, "没有绑定开放平台帐号"),
/**
* 订单无效.
*/
CODE_89300(89300, "订单无效"),
/**
* 代小程序实现业务的错误码部分和小程序业务一致
* https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Intro.html
*/
CODE_85060(85060, "无效的taskid"),
CODE_85027(85027, "身份证绑定管理员名额达到上限"),
CODE_85061(85061, "手机号绑定管理员名额达到上限"),
CODE_85026(85026, "微信号绑定管理员名额达到上限"),
CODE_85063(85063, "身份证黑名单"),
CODE_85062(85062, "手机号黑名单"),
CODE_85016(85016, "域名数量超过限制"),
CODE_85017(85017, "没有新增域名,请确认小程序已经添加了域名或该域名是否没有在第三方平台添加"),
CODE_85018(85018, "域名没有在第三方平台设置"),
CODE_89019(89019, "业务域名无更改,无需重复设置"),
CODE_89020(89020, "尚未设置小程序业务域名,请先在第三方平台中设置小程序业务域名后在调用本接口"),
CODE_89021(89021, "请求保存的域名不是第三方平台中已设置的小程序业务域名或子域名"),
CODE_89029(89029, "业务域名数量超过限制"),
CODE_89231(89231, "个人小程序不支持调用 setwebviewdomain 接口"),
CODE_91001(91001, "不是公众号快速创建的小程序"),
CODE_91002(91002, "小程序发布后不可改名"),
CODE_91003(91003, "改名状态不合法"),
CODE_91004(91004, "昵称不合法"),
CODE_91005(91005, "昵称 15 天主体保护"),
CODE_91006(91006, "昵称命中微信号"),
CODE_91007(91007, "昵称已被占用"),
CODE_91008(91008, "昵称命中 7 天侵权保护期"),
CODE_91009(91009, "需要提交材料"),
CODE_91010(91010, "其他错误"),
CODE_91011(91011, "查不到昵称修改审核单信息"),
CODE_91012(91012, "其他错误"),
CODE_91013(91013, "占用名字过多"),
CODE_91014(91014, "+号规则 同一类型关联名主体不一致"),
CODE_91015(91015, "原始名不同类型主体不一致"),
CODE_91016(91016, "名称占用者 ≥2"),
CODE_91017(91017, "+号规则 不同类型关联名主体不一致"),
CODE_40097(40097, "参数错误"),
CODE_41006(41006, "media_id 不能为空"),
CODE_46001(46001, "media_id 不存在"),
CODE_40009(40009, "图片尺寸太大"),
CODE_53202(53202, "本月头像修改次数已用完"),
CODE_53200(53200, "本月功能介绍修改次数已用完"),
CODE_53201(53201, "功能介绍内容命中黑名单关键字"),
CODE_85083(85083, "搜索标记位被封禁,无法修改"),
CODE_85084(85084, "非法的 status 值,只能填 0 或者 1"),
CODE_85013(85013, "无效的自定义配置"),
CODE_85014(85014, "无效的模版编号"),
CODE_85043(85043, "模版错误"),
CODE_85044(85044, "代码包超过大小限制"),
CODE_85045(85045, "ext_json 有不存在的路径"),
CODE_85046(85046, "tabBar 中缺少 path"),
CODE_85047(85047, "pages 字段为空"),
CODE_85048(85048, "ext_json 解析失败"),
CODE_80082(80082, "没有权限使用该插件"),
CODE_80067(80067, "找不到使用的插件"),
CODE_80066(80066, "非法的插件版本"),
CODE_86000(86000, "不是由第三方代小程序进行调用"),
CODE_86001(86001, "不存在第三方的已经提交的代码"),
CODE_85006(85006, "标签格式错误"),
CODE_85007(85007, "页面路径错误"),
CODE_85008(85008, "类目填写错误"),
CODE_85009(85009, "已经有正在审核的版本"),
CODE_85010(85010, "item_list 有项目为空"),
CODE_85011(85011, "标题填写错误"),
CODE_85023(85023, "审核列表填写的项目数不在 1-5 以内"),
CODE_85077(85077, "小程序类目信息失效(类目中含有官方下架的类目,请重新选择类目)"),
CODE_86002(86002, "小程序还未设置昵称、头像、简介。请先设置完后再重新提交"),
CODE_85085(85085, "近 7 天提交审核的小程序数量过多,请耐心等待审核完毕后再次提交"),
CODE_85086(85086, "提交代码审核之前需提前上传代码"),
CODE_85087(85087, "小程序已使用 api navigateToMiniProgram请声明跳转 appid 列表后再次提交"),
CODE_85012(85012, "无效的审核 id"),
CODE_87013(87013, "撤回次数达到上限每天5次每个月 10 次)"),
CODE_85019(85019, "没有审核版本"),
CODE_85020(85020, "审核状态未满足发布"),
CODE_87011(87011, "现网已经在灰度发布,不能进行版本回退"),
CODE_87012(87012, "该版本不能回退可能的原因1:无上一个线上版用于回退 2:此版本为已回退版本,不能回退 3:此版本为回退功能上线之前的版本,不能回退"),
CODE_85079(85079, "小程序没有线上版本,不能进行灰度"),
CODE_85080(85080, "小程序提交的审核未审核通过"),
CODE_85081(85081, "无效的发布比例"),
CODE_85082(85082, "当前的发布比例需要比之前设置的高"),
CODE_85021(85021, "状态不可变"),
CODE_85022(85022, "action 非法"),
CODE_89401(89401, "系统不稳定,请稍后再试,如多次失败请通过社区反馈"),
CODE_89402(89402, "该审核单不在待审核队列,请检查是否已提交审核或已审完"),
CODE_89403(89403, "本单属于平台不支持加急种类,请等待正常审核流程"),
CODE_89404(89404, "本单已加速成功,请勿重复提交"),
CODE_89405(89405, "本月加急额度不足,请提升提审质量以获取更多额度"),
CODE_85064(85064, "找不到模版/草稿"),
CODE_85065(85065, "模版库已满"),
/**
* 小程序订阅消息错误码
* https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
*/
CODE_43101(43101, "用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系"),
CODE_47003(47003, "模板参数不准确可能为空或者不满足规则errmsg会提示具体是哪个字段出错"),
/**
* 小程序绑定体验者
*/
CODE_85001(85001, "微信号不存在或微信号设置为不可搜索"),
CODE_85002(85002, "小程序绑定的体验者数量达到上限"),
CODE_85003(85003, "微信号绑定的小程序体验者达到上限"),
CODE_85004(85004, "微信号已经绑定"),
/**
* 53010
* 名称格式不合法
*/
CODE_53010(53010, "名称格式不合法"),
/**
* 53011
* 名称检测命中频率限制
*/
CODE_53011(53011, "名称检测命中频率限制"),
/**
* 53012
* 禁止使用该名称
*/
CODE_53012(53012, "禁止使用该名称"),
/**
* 53013
* 公众号名称与已有公众号名称重复;小程序该名称与已有小程序名称重复
*/
CODE_53013(53013, "公众号:名称与已有公众号名称重复;小程序:该名称与已有小程序名称重复"),
/**
* 53014
* 公众号公众号已有{名称 A+}需与该帐号相同主体才可申请{名称 A};小程序小程序已有{名称 A+}需与该帐号相同主体才可申请{名称 A}
*/
CODE_53014(53014, "公众号:公众号已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A};小程序:小程序已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A}"),
/**
* 53015
* 公众号该名称与已有小程序名称重复需与该小程序帐号相同主体才可申请;小程序该名称与已有公众号名称重复需与该公众号帐号相同主体才可申请
*/
CODE_53015(53015, "公众号:该名称与已有小程序名称重复,需与该小程序帐号相同主体才可申请;小程序:该名称与已有公众号名称重复,需与该公众号帐号相同主体才可申请"),
/**
* 53016
* 公众号该名称与已有多个小程序名称重复暂不支持申请;小程序该名称与已有多个公众号名称重复暂不支持申请
*/
CODE_53016(53016, "公众号:该名称与已有多个小程序名称重复,暂不支持申请;小程序:该名称与已有多个公众号名称重复,暂不支持申请"),
/**
* 53017
* 公众号小程序已有{名称 A+}需与该帐号相同主体才可申请{名称 A};小程序公众号已有{名称 A+}需与该帐号相同主体才可申请{名称 A}
*/
CODE_53017(53017, "公众号:小程序已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A};小程序:公众号已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A}"),
/**
* 53018
* 名称命中微信号
*/
CODE_53018(53018, "名称命中微信号"),
/**
* 53019
* 名称在保护期内
*/
CODE_53019(53019, "名称在保护期内"),
/**
* 61070
* 法人姓名与微信号不一致 name, wechat name not in accordance
*/
CODE_61070(61070, "法人姓名与微信号不一致"),
/**
* 85015
* 该账号不是小程序账号
*/
CODE_85015(85015, "该账号不是小程序账号"),
/**
* 85066
* 链接错误
*/
CODE_85066(85066, "链接错误"),
/**
* 85068
* 测试链接不是子链接
*/
CODE_85068(85068, "测试链接不是子链接"),
/**
* 85069
* 校验文件失败
*/
CODE_85069(85069, "校验文件失败"),
/**
* 85070
* 个人类型小程序无法设置二维码规则
*/
CODE_85070(85070, "个人类型小程序无法设置二维码规则"),
/**
* 85071
* 已添加该链接请勿重复添加
*/
CODE_85071(85071, "已添加该链接,请勿重复添加"),
/**
* 85072
* 该链接已被占用
*/
CODE_85072(85072, "该链接已被占用"),
/**
* 85073
* 二维码规则已满
*/
CODE_85073(85073, "二维码规则已满"),
/**
* 85074
* 小程序未发布, 小程序必须先发布代码才可以发布二维码跳转规则
*/
CODE_85074(85074, "小程序未发布, 小程序必须先发布代码才可以发布二维码跳转规则"),
/**
* 85075
* 个人类型小程序无法设置二维码规则
*/
CODE_85075(85075, "个人类型小程序无法设置二维码规则"),
/**
* 86004
* 无效微信号 invalid wechat
*/
CODE_86004(86004, "无效微信号"),
/**
* 89247
* 内部错误 inner error
*/
CODE_89247(89247, "内部错误"),
/**
* 89248
* 企业代码类型无效请选择正确类型填写 invalid code_type type
*/
CODE_89248(89248, "企业代码类型无效,请选择正确类型填写"),
/**
* 89249
* 该主体已有任务执行中距上次任务 24h 后再试 task running
*/
CODE_89249(89249, "该主体已有任务执行中,距上次任务 24h 后再试"),
/**
* 89250
* 未找到该任务 task not found
*/
CODE_89250(89250, "未找到该任务"),
/**
* 89251
* 待法人人脸核身校验 legal person checking
*/
CODE_89251(89251, "待法人人脸核身校验"),
/**
* 89252
* 法人&企业信息一致性校验中 front checking
*/
CODE_89252(89252, "法人&企业信息一致性校验中"),
/**
* 89253
* 缺少参数 lack of some params
*/
CODE_89253(89253, "缺少参数s"),
/**
* 89254
* 第三方权限集不全补全权限集全网发布后生效 lack of some component rights
*/
CODE_89254(89254, "第三方权限集不全,补全权限集全网发布后生效"),
/**
* 89255
* code参数无效请检查code长度以及内容是否正确 code参数无效请检查code长度以及内容是否正确_
* 注意code_type的值不同需要传的code长度不一样 注意code_type的值不同需要传的code长度不一样 enterprise code_invalid invalid
*/
CODE_89255(89255, "code参数无效请检查code长度以及内容是否正确_注意code_type的值不同需要传的code长度不一样 注意code_type的值不同需要传的code长度不一样"),
// CODE_504002(-504002, "云函数未找到 Function not found"),
;
private final int code;
private final String msg;
WxMaErrorMsgEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
static final Map<Integer, String> valueMap = Maps.newHashMap();
static {
for (WxMaErrorMsgEnum value : WxMaErrorMsgEnum.values()) {
valueMap.put(value.code, value.msg);
}
}
/**
* 通过错误代码查找其中文含义.
*/
public static String findMsgByCode(int code) {
return valueMap.getOrDefault(code, null);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
package com.ossez.discourse.common.exception;
/**
* WxJava专用的runtime exception.
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
* created on 2020-09-26
*/
public class WxRuntimeException extends RuntimeException {
private static final long serialVersionUID = 4881698471192264412L;
public WxRuntimeException(Throwable e) {
super(e);
}
public WxRuntimeException(String msg) {
super(msg);
}
public WxRuntimeException(String msg, Throwable e) {
super(msg, e);
}
}

View File

@ -0,0 +1,26 @@
package com.ossez.discourse.common.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* WeChatAccessToken Response Object
*
* @author YuCheng Hu
*/
public class WeChatApiDomainIp {
@JsonProperty("ip_list")
private List<String> ipList;
public List<String> getIpList() {
return ipList;
}
public void setIpList(List<String> ipList) {
this.ipList = ipList;
}
}

View File

@ -0,0 +1,16 @@
package com.ossez.discourse.common.model;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
public class WeChatHost implements Serializable {
private static final long serialVersionUID = -7648920647310280817L;
private String apiHost;
private String openHost;
private String mpHost;
}

View File

@ -0,0 +1,34 @@
package com.ossez.discourse.common.model;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* WeChatStatus Response Object
*
* @author YuCheng Hu
*/
public class WeChatStatus {
@JsonProperty("errcode")
private int errCode = 0;
@JsonProperty("errmsg")
private String errMsg;
public int getErrCode() {
return errCode;
}
public void setErrCode(int errCode) {
this.errCode = errCode;
}
public String getErrMsg() {
return errMsg;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
}

View File

@ -0,0 +1,99 @@
package com.ossez.discourse.common.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.experimental.Accessors;
import java.util.List;
public class Topics {
@JsonProperty(value = "id", required = true)
private Long topicId;
@JsonProperty(required = true)
private String postId;
@JsonProperty(required = true)
private String title;
@JsonProperty(required = true)
private String raw;
@JsonProperty(value = "category_id", required = true)
private Integer category;
@JsonProperty(value = "image_url")
private String imageUrl;
@JsonProperty(value = "word_count")
private Integer wordCount;
@JsonProperty(value = "tags")
private List<String> tags;
public Long getTopicId() {
return topicId;
}
public void setTopicId(Long topicId) {
this.topicId = topicId;
}
public String getPostId() {
return postId;
}
public void setPostId(String postId) {
this.postId = postId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getRaw() {
return raw;
}
public void setRaw(String raw) {
this.raw = raw;
}
public Integer getCategory() {
return category;
}
public void setCategory(Integer category) {
this.category = category;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public Integer getWordCount() {
return wordCount;
}
public void setWordCount(Integer wordCount) {
this.wordCount = wordCount;
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
}

View File

@ -0,0 +1,128 @@
package com.ossez.discourse.common.model.req;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
/**
* CustomMessage
*
* @author YuCheng
*/
public class CustomMessage implements Serializable {
private static final long serialVersionUID = -9196732086954365246L;
@JsonProperty("touser")
private String toUser;
@JsonProperty("msgtype")
private String msgType;
@JsonProperty("text")
private KfText text;
@JsonProperty("image")
private KfImage image;
@JsonProperty("link")
private KfLink link;
@JsonProperty("miniprogrampage")
private KfMaPage maPage;
@Data
@AllArgsConstructor
public static class KfText implements Serializable {
private static final long serialVersionUID = 151122958720941270L;
private String content;
}
@Data
@AllArgsConstructor
public static class KfImage implements Serializable {
private static final long serialVersionUID = -5409342945117300782L;
@SerializedName("media_id")
private String mediaId;
}
@Data
@Builder
public static class KfLink implements Serializable {
private static final long serialVersionUID = -6728776817556127413L;
private String title;
private String description;
private String url;
@SerializedName("thumb_url")
private String thumbUrl;
}
@Data
@Builder
public static class KfMaPage implements Serializable {
private static final long serialVersionUID = -5633492281871634466L;
private String title;
@SerializedName("pagepath")
private String pagePath;
@SerializedName("thumb_media_id")
private String thumbMediaId;
}
public String getToUser() {
return toUser;
}
public void setToUser(String toUser) {
this.toUser = toUser;
}
public String getMsgType() {
return msgType;
}
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public KfText getText() {
return text;
}
public void setText(KfText text) {
this.text = text;
}
public KfImage getImage() {
return image;
}
public void setImage(KfImage image) {
this.image = image;
}
public KfLink getLink() {
return link;
}
public void setLink(KfLink link) {
this.link = link;
}
public KfMaPage getMaPage() {
return maPage;
}
public void setMaPage(KfMaPage maPage) {
this.maPage = maPage;
}
}

View File

@ -0,0 +1,40 @@
package com.ossez.discourse.common.model.req;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
/**
* CustomMessage
*
* @author YuCheng
*/
public class DataCubeRequest implements Serializable {
private static final long serialVersionUID = -9196732086954365246L;
@JsonProperty(value = "begin_date", required = true)
private String beginDate;
@JsonProperty(value = "end_date", required = true)
private String endDate;
public String getBeginDate() {
return beginDate;
}
public void setBeginDate(String beginDate) {
this.beginDate = beginDate;
}
public String getEndDate() {
return endDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
}

View File

@ -0,0 +1,32 @@
package com.ossez.discourse.common.model.req;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
/**
* WeChatAccessToken Response Object
*
* @author YuCheng Hu
*/
public class NetworkCheck {
@JsonProperty("action")
private String action = "all";
@JsonProperty("check_operator")
private String checkOperator = StringUtils.upperCase("DEFAULT");
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getCheckOperator() {
return checkOperator;
}
public void setCheckOperator(String checkOperator) {
this.checkOperator = checkOperator;
}
}

View File

@ -0,0 +1,22 @@
package com.ossez.discourse.common.model.req;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
/**
* WeChatAccessToken Response Object
*
* @author YuCheng Hu
*/
public class QueryQuota {
@JsonProperty("cgi_path")
private String cgiPath = "/cgi-bin/message/custom/send";
public String getCgiPath() {
return cgiPath;
}
public void setCgiPath(String cgiPath) {
this.cgiPath = cgiPath;
}
}

View File

@ -0,0 +1,130 @@
package com.ossez.discourse.common.model.res;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* UserSummaryResponse Object
*
* @author YuCheng Hu
*/
public class DataCubeArticle {
@JsonProperty("list")
private List<ArticleData> articleDataList;
public static class ArticleData {
@JsonProperty("ref_date")
private String refDate;
@JsonProperty("msgid")
private String msgId;
@JsonProperty("title")
private String title;
@JsonProperty("int_page_read_user")
private Long intPageReadUser;
@JsonProperty("ori_page_read_user")
private Long oriPageReadUser;
@JsonProperty("ori_page_read_count")
private Long oriPageReadCount;
@JsonProperty("share_user")
private Long shareUser;
@JsonProperty("shareCount")
private Long shareCount;
@JsonProperty("add_to_fav_user")
private Long addToFavUser;
@JsonProperty("add_to_fav_count")
private Long addToFavCount;
public String getRefDate() {
return refDate;
}
public void setRefDate(String refDate) {
this.refDate = refDate;
}
public String getMsgId() {
return msgId;
}
public void setMsgId(String msgId) {
this.msgId = msgId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Long getIntPageReadUser() {
return intPageReadUser;
}
public void setIntPageReadUser(Long intPageReadUser) {
this.intPageReadUser = intPageReadUser;
}
public Long getOriPageReadUser() {
return oriPageReadUser;
}
public void setOriPageReadUser(Long oriPageReadUser) {
this.oriPageReadUser = oriPageReadUser;
}
public Long getOriPageReadCount() {
return oriPageReadCount;
}
public void setOriPageReadCount(Long oriPageReadCount) {
this.oriPageReadCount = oriPageReadCount;
}
public Long getShareUser() {
return shareUser;
}
public void setShareUser(Long shareUser) {
this.shareUser = shareUser;
}
public Long getShareCount() {
return shareCount;
}
public void setShareCount(Long shareCount) {
this.shareCount = shareCount;
}
public Long getAddToFavUser() {
return addToFavUser;
}
public void setAddToFavUser(Long addToFavUser) {
this.addToFavUser = addToFavUser;
}
public Long getAddToFavCount() {
return addToFavCount;
}
public void setAddToFavCount(Long addToFavCount) {
this.addToFavCount = addToFavCount;
}
}
public List<ArticleData> getArticleDataList() {
return articleDataList;
}
public void setArticleDataList(List<ArticleData> articleDataList) {
this.articleDataList = articleDataList;
}
}

View File

@ -0,0 +1,78 @@
package com.ossez.discourse.common.model.res;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* UserSummaryResponse Object
*
* @author YuCheng Hu
*/
public class DataCubeUser {
@JsonProperty("list")
private List<UserData> userDataList;
public static class UserData {
@JsonProperty("ref_date")
private String refDate;
@JsonProperty("user_source")
private Long userSource;
@JsonProperty("new_user")
private Long newUser;
@JsonProperty("cancel_user")
private Long cancelUser;
@JsonProperty("cumulate_user")
private Long cumulateUser;
public String getRefDate() {
return refDate;
}
public void setRefDate(String refDate) {
this.refDate = refDate;
}
public Long getUserSource() {
return userSource;
}
public void setUserSource(Long userSource) {
this.userSource = userSource;
}
public Long getNewUser() {
return newUser;
}
public void setNewUser(Long newUser) {
this.newUser = newUser;
}
public Long getCancelUser() {
return cancelUser;
}
public void setCancelUser(Long cancelUser) {
this.cancelUser = cancelUser;
}
public Long getCumulateUser() {
return cumulateUser;
}
public void setCumulateUser(Long cumulateUser) {
this.cumulateUser = cumulateUser;
}
}
public List<UserData> getUserDataList() {
return userDataList;
}
public void setUserDataList(List<UserData> userDataList) {
this.userDataList = userDataList;
}
}

View File

@ -0,0 +1,33 @@
package com.ossez.discourse.common.model.res;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* WeChatAccessToken Response Object
*
* @author YuCheng Hu
*/
public class NetworkCheckResponse {
@JsonProperty("dns")
private String dns;
@JsonProperty("ping")
private Ping ping;
private static class dns {
@JsonProperty("ip")
private String ip;
@JsonProperty("real_operator")
private String realOperator;
}
private static class Ping {
@JsonProperty("ip")
private String ip;
@JsonProperty("from_operator")
private String fromOperator;
@JsonProperty("package_loss")
private String packageLoss;
@JsonProperty("time")
private String time;
}
}

View File

@ -0,0 +1,66 @@
package com.ossez.discourse.common.model.res;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* WeChatAccessToken Response Object
*
* @author YuCheng Hu
*/
public class QueryQuotaResponse {
@JsonProperty("errcode")
private Integer errorCode;
@JsonProperty("errmsg")
private String errorMsg;
@JsonProperty("quota")
private Quota quota;
private static class Quota {
@JsonProperty("daily_limit")
private String dailyLimit;
@JsonProperty("used")
private String used;
@JsonProperty("remain")
private String remain;
public String getDailyLimit() {
return dailyLimit;
}
public void setDailyLimit(String dailyLimit) {
this.dailyLimit = dailyLimit;
}
public String getUsed() {
return used;
}
public void setUsed(String used) {
this.used = used;
}
public String getRemain() {
return remain;
}
public void setRemain(String remain) {
this.remain = remain;
}
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}

View File

@ -0,0 +1,15 @@
package com.ossez.discourse.common.model.res;
/**
* 包含toJson()方法的接口.
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
* created on 2020-10-05
*/
public interface ToJson {
/**
* 转换为json字符串
* @return json字符串
*/
String toJson();
}

View File

@ -0,0 +1,32 @@
package com.ossez.discourse.common.model.res;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* jspai signature.
*
* @author Daniel Qian
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WxJsapiSignature implements Serializable {
private static final long serialVersionUID = -1116808193154384804L;
private String appId;
private String nonceStr;
private long timestamp;
private String url;
private String signature;
}

View File

@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ossez.discourse.common.session;
/**
* Manifest constants for the <code>org.apache.catalina.session</code>
* package.
*
* @author Craig R. McClanahan
*/
public class Constants {
public static final String PACKAGE = "me.chanjar.weixin.common.session";
}

View File

@ -0,0 +1,72 @@
package com.ossez.discourse.common.session;
/**
*
* @author Daniel Qian
*/
public interface InternalSession {
/**
* Return the <code>HttpSession</code> for which this object
* is the facade.
*/
WxSession getSession();
/**
* Return the <code>isValid</code> flag for this session.
*/
boolean isValid();
/**
* Set the <code>isValid</code> flag for this session.
*
* @param isValid The new value for the <code>isValid</code> flag
*/
void setValid(boolean isValid);
/**
* Return the session identifier for this session.
*/
String getIdInternal();
/**
* Perform the internal processing required to invalidate this session,
* without triggering an exception if the session has already expired.
*/
void expire();
/**
* Update the accessed time information for this session. This method
* should be called by the context when a request comes in for a particular
* session, even if the application does not reference it.
*/
void access();
/**
* End the access.
*/
void endAccess();
/**
* Set the creation time for this session. This method is called by the
* Manager when an existing Session instance is reused.
*
* @param time The new creation time
*/
void setCreationTime(long time);
/**
* Set the default maximum inactive interval (in seconds)
* for Sessions created by this Manager.
*
* @param interval The new default value
*/
void setMaxInactiveInterval(int interval);
/**
* Set the session identifier for this session.
*
* @param id The new session identifier
*/
void setId(String id);
}

View File

@ -0,0 +1,119 @@
package com.ossez.discourse.common.session;
/**
* @author Daniel Qian
*/
public interface InternalSessionManager {
/**
* Return the active Session, associated with this Manager, with the
* specified session id (if any); otherwise return <code>null</code>.
*
* @param id The session id for the session to be returned
* @throws IllegalStateException if a new session cannot be
* instantiated for any reason
* @throws java.io.IOException if an input/output error occurs while
* processing this request
*/
InternalSession findSession(String id);
/**
* Construct and return a new session object, based on the default
* settings specified by this Manager's properties. The session
* id specified will be used as the session id.
* If a new session cannot be created for any reason, return
* <code>null</code>.
*
* @param sessionId The session id which should be used to create the
* new session; if <code>null</code>, a new session id will be
* generated
* @throws IllegalStateException if a new session cannot be
* instantiated for any reason
*/
InternalSession createSession(String sessionId);
/**
* Remove this Session from the active Sessions for this Manager.
*
* @param session Session to be removed
*/
void remove(InternalSession session);
/**
* Remove this Session from the active Sessions for this Manager.
*
* @param session Session to be removed
* @param update Should the expiration statistics be updated
*/
void remove(InternalSession session, boolean update);
/**
* Add this Session to the set of active Sessions for this Manager.
*
* @param session Session to be added
*/
void add(InternalSession session);
/**
* Returns the number of active sessions
*
* @return number of sessions active
*/
int getActiveSessions();
/**
* Get a session from the recycled ones or create a new empty one.
* The PersistentManager manager does not need to create session data
* because it reads it from the Store.
*/
InternalSession createEmptySession();
InternalSession[] findSessions();
/**
* Implements the Manager interface, direct call to processExpires
*/
void backgroundProcess();
/**
* Set the default maximum inactive interval (in seconds)
* for Sessions created by this Manager.
*
* @param interval The new default value
*/
void setMaxInactiveInterval(int interval);
/**
* <pre>
* Set the manager checks frequency.
* 设置每尝试多少次清理过期session才真的会执行一次清理动作
* 要和{@link #setBackgroundProcessorDelay(int)}联合起来看
* 如果把这个数字设置为6默认那么就是说manager要等待 6 * backgroundProcessorDay的时间才会清理过期session
* </pre>
*
* @param processExpiresFrequency the new manager checks frequency
*/
void setProcessExpiresFrequency(int processExpiresFrequency);
/**
* <pre>
* Set the manager background processor delay
* 设置manager sleep几秒尝试执行一次background操作清理过期session
* </pre>
*
* @param backgroundProcessorDelay
*/
void setBackgroundProcessorDelay(int backgroundProcessorDelay);
/**
* Set the maximum number of active Sessions allowed, or -1 for
* no limit.
* 设置最大活跃session数默认无限
*
* @param max The new maximum number of sessions
*/
void setMaxActiveSessions(int max);
}

View File

@ -0,0 +1,79 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
applicationSession.session.ise=invalid session state
applicationSession.value.iae=null value
fileStore.saving=Saving Session {0} to file {1}
fileStore.loading=Loading Session {0} from file {1}
fileStore.removing=Removing Session {0} at file {1}
fileStore.deleteFailed=Unable to delete file [{0}] which is preventing the creation of the session storage location
fileStore.createFailed=Unable to create directory [{0}] for the storage of session data
JDBCStore.close=Exception closing database connection {0}
JDBCStore.saving=Saving Session {0} to database {1}
JDBCStore.loading=Loading Session {0} from database {1}
JDBCStore.removing=Removing Session {0} at database {1}
JDBCStore.SQLException=SQL Error {0}
serverSession.value.iae=null value
sessionManagerImpl.createRandom=Created random number generator for session ID generation in {0}ms.
sessionManagerImpl.createSession.tmase=createSession: Too many active sessions
sessionManagerImpl.sessionTimeout=Invalid session timeout setting {0}
sessionManagerImpl.getSession.ise=getSession: Session id cannot be null
sessionManagerImpl.expireException=processsExpire: Exception during session expiration
sessionManagerImpl.loading=Loading persisted sessions from {0}
sessionManagerImpl.loading.cnfe=ClassNotFoundException while loading persisted sessions: {0}
sessionManagerImpl.loading.ioe=IOException while loading persisted sessions: {0}
sessionManagerImpl.unloading=Saving persisted sessions to {0}
sessionManagerImpl.unloading.debug=Unloading persisted sessions
sessionManagerImpl.unloading.ioe=IOException while saving persisted sessions: {0}
sessionManagerImpl.unloading.nosessions=No persisted sessions to unload
sessionManagerImpl.managerLoad=Exception loading sessions from persistent storage
sessionManagerImpl.managerUnload=Exception unloading sessions to persistent storage
sessionManagerImpl.createSession.ise=createSession: Session id cannot be null
sessionImpl.attributeEvent=Session attribute event listener threw exception
sessionImpl.bindingEvent=Session binding event listener threw exception
sessionImpl.invalidate.ise=invalidate: Session already invalidated
sessionImpl.isNew.ise=isNew: Session already invalidated
sessionImpl.getAttribute.ise=getAttribute: Session already invalidated
sessionImpl.getAttributeNames.ise=getAttributeNames: Session already invalidated
sessionImpl.getCreationTime.ise=getCreationTime: Session already invalidated
sessionImpl.getThisAccessedTime.ise=getThisAccessedTime: Session already invalidated
sessionImpl.getLastAccessedTime.ise=getLastAccessedTime: Session already invalidated
sessionImpl.getId.ise=getId: Session already invalidated
sessionImpl.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already invalidated
sessionImpl.getValueNames.ise=getValueNames: Session already invalidated
sessionImpl.logoutfail=Exception logging out user when expiring session
sessionImpl.notSerializable=Cannot serialize session attribute {0} for session {1}
sessionImpl.removeAttribute.ise=removeAttribute: Session already invalidated
sessionImpl.sessionEvent=Session event listener threw exception
sessionImpl.setAttribute.iae=setAttribute: Non-serializable attribute {0}
sessionImpl.setAttribute.ise=setAttribute: Session [{0}] has already been invalidated
sessionImpl.setAttribute.namenull=setAttribute: name parameter cannot be null
sessionImpl.sessionCreated=Created Session id = {0}
persistentManager.loading=Loading {0} persisted sessions
persistentManager.unloading=Saving {0} persisted sessions
persistentManager.expiring=Expiring {0} sessions before saving them
persistentManager.deserializeError=Error deserializing Session {0}: {1}
persistentManager.serializeError=Error serializing Session {0}: {1}
persistentManager.swapMaxIdle=Swapping session {0} to Store, idle for {1} seconds
persistentManager.backupMaxIdle=Backing up session {0} to Store, idle for {1} seconds
persistentManager.backupException=Exception occurred when backing up Session {0}: {1}
persistentManager.tooManyActive=Too many active sessions, {0}, looking for idle sessions to swap out
persistentManager.swapTooManyActive=Swapping out session {0}, idle for {1} seconds too many sessions active
persistentManager.processSwaps=Checking for sessions to swap out, {0} active sessions in memory
persistentManager.activeSession=Session {0} has been idle for {1} seconds
persistentManager.swapIn=Swapping session {0} in from Store
persistentManager.swapInException=Exception in the Store during swapIn: {0}
persistentManager.swapInInvalid=Swapped session {0} is invalid
persistentManager.storeKeysException=Unable to determine the list of session IDs for sessions in the session store, assuming that the store is empty
persistentManager.storeSizeException=Unable to determine the number of sessions in the session store, assuming that the store is empty

View File

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ossez.discourse.common.session;
/**
* An exception that indicates the maximum number of active sessions has been
* reached and the server is refusing to create any new sessions.
*
* @author Daniel Qian
*/
public class TooManyActiveSessionsException
extends IllegalStateException {
private static final long serialVersionUID = 1L;
/**
* The maximum number of active sessions the server will tolerate.
*/
private final int maxActiveSessions;
/**
* Creates a new TooManyActiveSessionsException.
*
* @param message A description for the exception.
* @param maxActive The maximum number of active sessions allowed by the
* session manager.
*/
public TooManyActiveSessionsException(String message,
int maxActive) {
super(message);
this.maxActiveSessions = maxActive;
}
/**
* Gets the maximum number of sessions allowed by the session manager.
*
* @return The maximum number of sessions allowed by the session manager.
*/
public int getMaxActiveSessions() {
return this.maxActiveSessions;
}
}

View File

@ -0,0 +1,20 @@
package com.ossez.discourse.common.session;
import java.util.Enumeration;
/**
* @author Daniel Qian
*/
public interface WxSession {
Object getAttribute(String name);
Enumeration<String> getAttributeNames();
void setAttribute(String name, Object value);
void removeAttribute(String name);
void invalidate();
}

View File

@ -0,0 +1,19 @@
package com.ossez.discourse.common.session;
/**
* @author Daniel Qian
*/
public interface WxSessionManager {
/**
* 获取某个sessionId对应的session,如果sessionId没有对应的session则新建一个并返回
*/
WxSession getSession(String sessionId);
/**
* 获取某个sessionId对应的session,如果sessionId没有对应的session若create为true则新建一个否则返回null
*/
WxSession getSession(String sessionId, boolean create);
}

View File

@ -0,0 +1,40 @@
package com.ossez.discourse.common.exception;
import com.ossez.discourse.common.enums.WxType;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import static org.assertj.core.api.Assertions.assertThat;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Slf4j
public class WxErrorTest {
@Test
public void testFromJson() {
String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\" }";
// DiscourseError wxError = DiscourseError.fromJson(json, WxType.MP);
// assertThat(40003).isEqualTo(wxError.getErrorCode());
// assertEquals(wxError.getErrorMsgEn(), "invalid openid");
}
// @Test
// public void testFromBadJson1() {
// String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\", \"media_id\": \"12323423dsfafsf232f\" }";
// WxError wxError = WxError.fromJson(json, WxType.MP);
// assertEquals(40003, wxError.getErrorCode());
// assertEquals(wxError.getErrorMsgEn(), "invalid openid");
//
// }
//
// @Test
// public void testFromBadJson2() {
// String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}";
// WxError wxError = WxError.fromJson(json, WxType.MP);
// assertEquals(0, wxError.getErrorCode());
// assertNull(wxError.getErrorMsg());
//
// }
}

View File

@ -0,0 +1,27 @@
package com.ossez.discourse.common.model;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test for Access Token
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class WeChatAccessTokenTest {
final Logger log = LoggerFactory.getLogger(WeChatAccessTokenTest.class);
private String accessTokenResponse;
@BeforeEach
protected void setUp() throws Exception {
accessTokenResponse = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}";
}
@AfterEach
protected void tearDown() throws Exception {
}
}

View File

@ -0,0 +1,12 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

553
pom.xml Normal file
View File

@ -0,0 +1,553 @@
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ossez.discourse</groupId>
<artifactId>discourse-j</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Discourse Java</name>
<description>Discourse API SDK</description>
<licenses>
<license>
<name>The MIT license</name>
<url>https://opensource.org/licenses/mit-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>YuCheng Hu</name>
<id>honeymoose</id>
<email>huyuchengus@gmail.com</email>
<timezone>-5</timezone>
<organization>Open Source</organization>
<roles>
<role>Java Developer</role>
</roles>
</developer>
</developers>
<scm>
<connection>scm:git:https://github.com/Wechat-Group/WxJava.git</connection>
<developerConnection>scm:git:git@github.com:Wechat-Group/WxJava.git</developerConnection>
<url>https://github.com/Wechat-Group/WxJava</url>
</scm>
<modules>
<module>common</module>
<module>client</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<httpclient.version>4.5.13</httpclient.version>
<jetty.version>9.4.43.v20210629</jetty.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>qrcode-utils</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<version>6.2.1</version>
<scope>provided</scope>
</dependency>
<!-- UTILITIES -->
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava2</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.19</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.0-jre</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.0</version>
</dependency>
<!-- 测试所用依赖 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>5.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.1.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-guava</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.dreamhead</groupId>
<artifactId>moco-runner</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.jedis-lock</groupId>
<artifactId>jedis-lock</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
<optional>true</optional>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.jodd</groupId>
<artifactId>jodd-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.3.3.RELEASE</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.68</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- LOGGING WITH SELF4J AND LOG4J2 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<scope>compile</scope>
</dependency>
<!-- UTILS -->
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava2</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>5.1.0</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.14.0</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.7.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
<!--/ TEST -->
</dependencies>
<distributionManagement>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<profiles>
<profile>
<id>doclint-java8-disable</id>
<activation>
<jdk>[1.8,)</jdk>
</activation>
<properties>
<javadoc.opts>-Xdoclint:none</javadoc.opts>
</properties>
</profile>
<profile>
<id>release</id>
<build>
<plugins>
<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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<additionalparam>${javadoc.opts}</additionalparam>
<charset>UTF-8</charset>
<locale>zh_CN</locale>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>native-image</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
</profile>
</profiles>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>default-deploy</id>
<phase>deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-javadoc-plugin</artifactId>-->
<!-- <version>3.4.1</version>-->
<!-- <configuration>-->
<!-- <source>11</source>-->
<!-- </configuration>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <id>attach-javadocs</id>-->
<!-- <goals>-->
<!-- <goal>jar</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<passphraseServerId>ossez</passphraseServerId>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<executions>
<execution>
<id>default-deploy</id>
<phase>deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- <plugin>-->
<!-- <groupId>org.sonatype.plugins</groupId>-->
<!-- <artifactId>nexus-staging-maven-plugin</artifactId>-->
<!-- <version>1.6.3</version>-->
<!-- <extensions>true</extensions>-->
<!-- <configuration>-->
<!-- <serverId>ossrh</serverId>-->
<!-- <nexusUrl>https://oss.sonatype.org/</nexusUrl>-->
<!-- <autoReleaseAfterClose>true</autoReleaseAfterClose>-->
<!-- </configuration>-->
<!-- </plugin>-->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-release-plugin</artifactId>-->
<!-- <version>2.5.1</version>-->
<!-- <configuration>-->
<!-- <autoVersionSubmodules>true</autoVersionSubmodules>-->
<!-- <useReleaseProfile>false</useReleaseProfile>-->
<!-- <releaseProfiles>release</releaseProfiles>-->
<!-- <goals>deploy</goals>-->
<!-- </configuration>-->
<!-- </plugin>-->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <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>-->
<!-- <failsOnError>true</failsOnError>-->
<!-- </configuration>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <phase>verify</phase>-->
<!-- <goals>-->
<!-- <goal>check</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
</plugins>
</build>
</project>