Kaynağa Gözat

#193 增加小程序模块weixin-java-miniapp,支持小程序后台开发

master
Binary Wang 7 yıl önce
ebeveyn
işleme
1bd9b15c98
50 değiştirilmiş dosya ile 3641 ekleme ve 0 silme
  1. +1
    -0
      pom.xml
  2. +84
    -0
      weixin-java-miniapp/pom.xml
  3. +65
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMediaService.java
  4. +36
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java
  5. +30
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java
  6. +135
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
  7. +39
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
  8. +61
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java
  9. +40
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java
  10. +28
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java
  11. +265
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java
  12. +51
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
  13. +49
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaJscode2SessionResult.java
  14. +99
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java
  15. +236
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
  16. +44
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcode.java
  17. +214
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java
  18. +131
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUserInfo.java
  19. +24
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java
  20. +27
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/ImageBuilder.java
  21. +27
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/TextBuilder.java
  22. +73
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java
  23. +185
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaInMemoryConfig.java
  24. +26
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java
  25. +20
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageHandler.java
  26. +28
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageInterceptor.java
  27. +17
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageMatcher.java
  28. +176
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouter.java
  29. +316
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouterRule.java
  30. +43
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java
  31. +61
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/http/QrCodeRequestExecutor.java
  32. +24
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java
  33. +47
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaKefuMessageGsonAdapter.java
  34. +60
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java
  35. +88
    -0
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java
  36. +52
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImplTest.java
  37. +76
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java
  38. +26
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java
  39. +35
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
  40. +46
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java
  41. +40
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java
  42. +130
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java
  43. +31
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessageTest.java
  44. +144
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaDemoServer.java
  45. +92
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaPortalServlet.java
  46. +33
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/ApiTestModule.java
  47. +61
    -0
      weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConfig.java
  48. +14
    -0
      weixin-java-miniapp/src/test/resources/logback-test.xml
  49. +11
    -0
      weixin-java-miniapp/src/test/resources/test-config.sample.xml
  50. BIN
      weixin-java-miniapp/src/test/resources/tmp.png

+ 1
- 0
pom.xml Dosyayı Görüntüle

@@ -93,6 +93,7 @@
<module>weixin-java-cp</module>
<module>weixin-java-mp</module>
<module>weixin-java-pay</module>
<module>weixin-java-miniapp</module>
<!--module>weixin-java-osgi</module-->
</modules>



+ 84
- 0
weixin-java-miniapp/pom.xml Dosyayı Görüntüle

@@ -0,0 +1,84 @@
<?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>
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>2.6.3.BETA</version>
</parent>
<artifactId>weixin-java-miniapp</artifactId>
<name>WeiXin Java Tools - MiniApp</name>
<description>微信小程序Java SDK</description>

<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-common</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</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>joda-time</groupId>
<artifactId>joda-time</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</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>

</project>

+ 65
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMediaService.java Dosyayı Görüntüle

@@ -0,0 +1,65 @@
package cn.binarywang.wx.miniapp.api;

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

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

/**
* <pre>
* 临时素材接口
* Created by Binary Wang on 2016/7/21.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaMediaService {
String MEDIA_UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/media/upload?type=%s";
String MEDIA_GET_URL = "https://api.weixin.qq.com/cgi-bin/media/get";

/**
* <pre>
* 新增临时素材
* 小程序可以使用本接口把媒体文件(目前仅支持图片)上传到微信服务器,用户发送客服消息或被动回复用户消息。
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/material.html#新增临时素材">新增临时素材</a>
* 接口url格式:https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
* </pre>
*
* @param mediaType 媒体类型,
* @param file 文件对象
* @see #uploadMedia(String, String, InputStream)
*/
WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException;

/**
* <pre>
* 新增临时素材
* 小程序可以使用本接口把媒体文件(目前仅支持图片)上传到微信服务器,用户发送客服消息或被动回复用户消息。
*
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/material.html#新增临时素材">新增临时素材</a>
* 接口url格式:https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
* </pre>
*
* @param mediaType 媒体类型
* @param fileType 文件类型
* @param inputStream 输入流
* @see #uploadMedia(java.lang.String, java.io.File)
*/
WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException;

/**
* <pre>
* 获取临时素材
* 小程序可以使用本接口获取客服消息内的临时素材(即下载临时的多媒体文件)。目前小程序仅支持下载图片文件。
*
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/material.html#获取临时素材">获取临时素材</a>
* 接口url格式:https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
* </pre>
*
* @param mediaId 媒体Id
* @return 保存到本地的临时文件
*/
File getMedia(String mediaId) throws WxErrorException;

}

+ 36
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java Dosyayı Görüntüle

@@ -0,0 +1,36 @@
package cn.binarywang.wx.miniapp.api;

import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import me.chanjar.weixin.common.exception.WxErrorException;

/**
* <pre>
* 消息发送接口
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaMsgService {
String KEFU_MESSAGE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
String TEMPLATE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send";

/**
* <pre>
* 发送客服消息
* 详情请见: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547&token=&lang=zh_CN">发送客服消息</a>
* 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
* </pre>
*/
boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException;

/**
* <pre>
* 发送模板消息
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN
* </pre>
*
* @return 消息Id
*/
String sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException;
}

+ 30
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java Dosyayı Görüntüle

@@ -0,0 +1,30 @@
package cn.binarywang.wx.miniapp.api;

import me.chanjar.weixin.common.exception.WxErrorException;

import java.io.File;

/**
* <pre>
* 二维码相关操作接口
* 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/qrcode.html
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaQrcodeService {

/**
* <pre>
* 获取小程序页面二维码
* 适用于需要的码数量较少的业务场景
* 通过该接口,仅能生成已发布的小程序的二维码。
* 可以在开发者工具预览时生成开发版的带参二维码。
* 带参二维码只有 100000 个,请谨慎调用。
* </pre>
*
* @param path 不能为空,最大长度 128 字节
* @param width 默认430 二维码的宽度
*/
File createQrcode(String path, int width) throws WxErrorException;
}

+ 135
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java Dosyayı Görüntüle

@@ -0,0 +1,135 @@
package cn.binarywang.wx.miniapp.api;

import cn.binarywang.wx.miniapp.config.WxMaConfig;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaService {
/**
* 获取access_token
*/
String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

/**
* <pre>
* 验证消息的确来自微信服务器
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
* </pre>
*/
boolean checkSignature(String timestamp, String nonce, String signature);

/**
* 获取access_token, 不强制刷新access_token
*
* @see #getAccessToken(boolean)
*/
String getAccessToken() throws WxErrorException;

/**
* <pre>
* 获取access_token,本方法线程安全
* 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
*
* 另:本service的所有方法都会在access_token过期是调用此方法
*
* 程序员在非必要情况下尽量不要主动调用此方法
*
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN
* </pre>
*
* @param forceRefresh 强制刷新
*/
String getAccessToken(boolean forceRefresh) throws WxErrorException;

/**
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求
*/
String get(String url, String queryParam) throws WxErrorException;

/**
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求
*/
String post(String url, String postData) throws WxErrorException;

/**
* <pre>
* Service没有实现某个API的时候,可以用这个,
* 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
* 可以参考,{@link MediaUploadRequestExecutor}的实现方法
* </pre>
*/
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;

/**
* <pre>
* 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
* 默认:1000ms
* </pre>
*/
void setRetrySleepMillis(int retrySleepMillis);

/**
* <pre>
* 设置当微信系统响应系统繁忙时,最大重试次数
* 默认:5次
* </pre>
*/
void setMaxRetryTimes(int maxRetryTimes);

/**
* 获取WxMaConfig 对象
*
* @return WxMaConfig
*/
WxMaConfig getWxMaConfig();

/**
* 注入 {@link WxMaConfig} 的实现
*/
void setWxMaConfig(WxMaConfig wxConfigProvider);

/**
* 返回消息(客服消息和模版消息)发送接口方法实现类,以方便调用其各个接口
*
* @return WxMaMsgService
*/
WxMaMsgService getMsgService();

/**
* 返回素材相关接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMaMediaService
*/
WxMaMediaService getMediaService();

/**
* 返回用户相关接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMaUserService
*/
WxMaUserService getUserService();

/**
* 返回二维码相关接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMaQrcodeService
*/
WxMaQrcodeService getQrcodeService();

/**
* 初始化http请求对象
*/
void initHttp();

/**
* 请求http请求相关信息
*/
RequestHttp getRequestHttp();


}

+ 39
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java Dosyayı Görüntüle

@@ -0,0 +1,39 @@
package cn.binarywang.wx.miniapp.api;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import me.chanjar.weixin.common.exception.WxErrorException;

/**
* 用户信息相关操作接口
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaUserService {
String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session";

/**
* 获取登录后的session信息
*
* @param jsCode 登录时获取的 code
*/
WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException;

/**
* 解密用户敏感数据
*
* @param sessionKey 会话密钥
* @param encryptedData 消息密文
* @param ivStr 加密算法的初始向量
*/
WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr);

/**
* 验证用户信息完整性
*
* @param sessionKey 会话密钥
* @param rawData 微信用户基本信息
* @param signature 数据签名
*/
boolean checkUserInfo(String sessionKey, String rawData, String signature);
}

+ 61
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java Dosyayı Görüntüle

@@ -0,0 +1,61 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaMediaService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.UUID;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaMediaServiceImpl implements WxMaMediaService {
private final Logger log = LoggerFactory.getLogger(this.getClass());

private WxMaService wxMaService;

public WxMaMediaServiceImpl(WxMaService wxMaService) {
this.wxMaService = wxMaService;
}

@Override
public WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException {
try {
return this.uploadMedia(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType));
} catch (IOException e) {
e.printStackTrace();
throw new WxErrorException(WxError.newBuilder().setErrorMsg(e.getMessage()).build());
}
}

@Override
public WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException {
String url = String.format(MEDIA_UPLOAD_URL, mediaType);
return this.wxMaService.execute(MediaUploadRequestExecutor.create(this.wxMaService.getRequestHttp()), url, file);
}

@Override
public File getMedia(String mediaId) throws WxErrorException {
try {
RequestExecutor<File, String> executor = MediaDownloadRequestExecutor
.create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("wxma").toFile());
return this.wxMaService.execute(executor, MEDIA_GET_URL, "media_id=" + mediaId);
} catch (IOException e) {
this.log.error(e.getMessage(), e);
throw new WxErrorException(WxError.newBuilder().setErrorMsg(e.getMessage()).build());
}
}

}

+ 40
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java Dosyayı Görüntüle

@@ -0,0 +1,40 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaMsgService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaMsgServiceImpl implements WxMaMsgService {
private static final JsonParser JSON_PARSER = new JsonParser();
private WxMaService wxMaService;

public WxMaMsgServiceImpl(WxMaService wxMaService) {
this.wxMaService = wxMaService;
}

@Override
public boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException {
String responseContent = this.wxMaService.post(KEFU_MESSAGE_SEND_URL, message.toJson());
return responseContent != null;
}

@Override
public String sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException {
String responseContent = this.wxMaService.post(TEMPLATE_MSG_SEND_URL, templateMessage.toJson());
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
if (jsonObject.get("errcode").getAsInt() == 0) {
return jsonObject.get("msgid").getAsString();
}

throw new WxErrorException(WxError.fromJson(responseContent));
}

}

+ 28
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java Dosyayı Görüntüle

@@ -0,0 +1,28 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaQrcodeService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaQrcode;
import cn.binarywang.wx.miniapp.util.http.QrCodeRequestExecutor;
import me.chanjar.weixin.common.exception.WxErrorException;

import java.io.File;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaQrcodeServiceImpl implements WxMaQrcodeService {
private WxMaService wxMaService;

public WxMaQrcodeServiceImpl(WxMaService wxMaService) {
this.wxMaService = wxMaService;
}

@Override
public File createQrcode(String path, int width) throws WxErrorException {
String url = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode";
return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()),
url, new WxMaQrcode(path, width));
}

}

+ 265
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java Dosyayı Görüntüle

@@ -0,0 +1,265 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.*;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.common.util.http.*;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.concurrent.locks.Lock;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpClient, HttpHost> {
private static final JsonParser JSON_PARSER = new JsonParser();
private final Logger log = LoggerFactory.getLogger(this.getClass());

private CloseableHttpClient httpClient;
private HttpHost httpProxy;
private WxMaConfig wxMaConfig;

private WxMaMsgService kefuService = new WxMaMsgServiceImpl(this);
private WxMaMediaService materialService = new WxMaMediaServiceImpl(this);
private WxMaUserService userService = new WxMaUserServiceImpl(this);
private WxMaQrcodeService qrCodeService = new WxMaQrcodeServiceImpl(this);

private int retrySleepMillis = 1000;
private int maxRetryTimes = 5;

@Override
public CloseableHttpClient getRequestHttpClient() {
return httpClient;
}

@Override
public HttpHost getRequestHttpProxy() {
return httpProxy;
}

@Override
public HttpType getRequestType() {
return HttpType.APACHE_HTTP;
}

@Override
public void initHttp() {
WxMaConfig configStorage = this.getWxMaConfig();
ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder();
if (null == apacheHttpClientBuilder) {
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get();
}

apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost())
.httpProxyPort(configStorage.getHttpProxyPort())
.httpProxyUsername(configStorage.getHttpProxyUsername())
.httpProxyPassword(configStorage.getHttpProxyPassword());

if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort());
}

this.httpClient = apacheHttpClientBuilder.build();
}

@Override
public RequestHttp getRequestHttp() {
return this;
}

@Override
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
Lock lock = this.getWxMaConfig().getAccessTokenLock();
try {
lock.lock();

if (forceRefresh) {
this.getWxMaConfig().expireAccessToken();
}

if (this.getWxMaConfig().isAccessTokenExpired()) {
String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(),
this.getWxMaConfig().getSecret());
try {
HttpGet httpGet = new HttpGet(url);
if (this.getRequestHttpProxy() != null) {
RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
httpGet.setConfig(config);
}
try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) {
String resultContent = new BasicResponseHandler().handleResponse(response);
WxError error = WxError.fromJson(resultContent);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(),
accessToken.getExpiresIn());
} finally {
httpGet.releaseConnection();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} finally {
lock.unlock();
}

return this.getWxMaConfig().getAccessToken();
}

@Override
public boolean checkSignature(String timestamp, String nonce, String signature) {
try {
return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature);
} catch (Exception e) {
this.log.error("Checking signature failed, and the reason is :" + e.getMessage());
return false;
}
}

@Override
public String getAccessToken() throws WxErrorException {
return getAccessToken(false);
}

@Override
public String get(String url, String queryParam) throws WxErrorException {
return execute(SimpleGetRequestExecutor.create(this), url, queryParam);
}

@Override
public String post(String url, String postData) throws WxErrorException {
return execute(SimplePostRequestExecutor.create(this), url, postData);
}

/**
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求
*/
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
int retryTimes = 0;
do {
try {
return this.executeInternal(executor, uri, data);
} catch (WxErrorException e) {
if (retryTimes + 1 > this.maxRetryTimes) {
this.log.warn("重试达到最大次数【{}】", maxRetryTimes);
//最后一次重试失败后,直接抛出异常,不再等待
throw new RuntimeException("微信服务端异常,超出重试次数");
}

WxError error = e.getError();
// -1 系统繁忙, 1000ms后重试
if (error.getErrorCode() == -1) {
int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
try {
this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
Thread.sleep(sleepMillis);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
} else {
throw e;
}
}
} while (retryTimes++ < this.maxRetryTimes);

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

public synchronized <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
if (uri.contains("access_token=")) {
throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
}
String accessToken = getAccessToken(false);

String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken;

try {
T result = executor.execute(uriWithAccessToken, data);
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uriWithAccessToken, data, result);
return result;
} catch (WxErrorException e) {
WxError error = e.getError();
/*
* 发生以下情况时尝试刷新access_token
* 40001 获取access_token时AppSecret错误,或者access_token无效
* 42001 access_token超时
* 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期)
*/
if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) {
// 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
this.getWxMaConfig().expireAccessToken();
if (this.getWxMaConfig().autoRefreshToken()) {
return this.execute(executor, uri, data);
}
}

if (error.getErrorCode() != 0) {
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uriWithAccessToken, data, error);
throw new WxErrorException(error);
}
return null;
} catch (IOException e) {
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uriWithAccessToken, data, e.getMessage());
throw new RuntimeException(e);
}
}

@Override
public WxMaConfig getWxMaConfig() {
return this.wxMaConfig;
}

@Override
public void setWxMaConfig(WxMaConfig wxConfigProvider) {
this.wxMaConfig = wxConfigProvider;
this.initHttp();
}

@Override
public void setRetrySleepMillis(int retrySleepMillis) {
this.retrySleepMillis = retrySleepMillis;
}

@Override
public void setMaxRetryTimes(int maxRetryTimes) {
this.maxRetryTimes = maxRetryTimes;
}

@Override
public WxMaMsgService getMsgService() {
return this.kefuService;
}

@Override
public WxMaMediaService getMediaService() {
return this.materialService;
}

@Override
public WxMaUserService getUserService() {
return this.userService;
}

@Override
public WxMaQrcodeService getQrcodeService() {
return this.qrCodeService;
}
}

+ 51
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java Dosyayı Görüntüle

@@ -0,0 +1,51 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
import com.google.common.base.Joiner;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.apache.commons.codec.digest.DigestUtils;

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

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaUserServiceImpl implements WxMaUserService {
private WxMaService service;

WxMaUserServiceImpl(WxMaService service) {
this.service = service;
}

@Override
public WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException {
final WxMaConfig config = service.getWxMaConfig();
Map<String, String> params = new HashMap<>();
params.put("appid", config.getAppid());
params.put("secret", config.getSecret());
params.put("js_code", jsCode);
params.put("grant_type", "authorization_code");

String result = this.service.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
return WxMaJscode2SessionResult.fromJson(result);
}

@Override
public WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr) {
return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr));
}

@Override
public boolean checkUserInfo(String sessionKey, String rawData, String signature) {
final String generatedSignature = DigestUtils.sha1Hex(rawData + sessionKey);
System.out.println(generatedSignature);
return generatedSignature.equals(signature);
}

}

+ 49
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaJscode2SessionResult.java Dosyayı Görüntüle

@@ -0,0 +1,49 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
import com.google.gson.annotations.SerializedName;

/**
* {"session_key":"nzoqhc3OnwHzeTxJs+inbQ==","expires_in":2592000,"openid":"oVBkZ0aYgDMDIywRdgPW8-joxXc4"}
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaJscode2SessionResult {
@SerializedName("session_key")
private String sessionKey;

@SerializedName("expires_in")
private Integer expiresin;

@SerializedName("openid")
private String openid;

public String getSessionKey() {
return sessionKey;
}

public void setSessionKey(String sessionKey) {
this.sessionKey = sessionKey;
}

public Integer getExpiresin() {
return expiresin;
}

public void setExpiresin(Integer expiresin) {
this.expiresin = expiresin;
}

public String getOpenid() {
return openid;
}

public void setOpenid(String openid) {
this.openid = openid;
}

public static WxMaJscode2SessionResult fromJson(String json) {
return WxMaGsonBuilder.create().fromJson(json, WxMaJscode2SessionResult.class);
}

}

+ 99
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java Dosyayı Görüntüle

@@ -0,0 +1,99 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.builder.ImageBuilder;
import cn.binarywang.wx.miniapp.builder.TextBuilder;
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;

import java.io.Serializable;

/**
* 客服消息
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaKefuMessage implements Serializable {
private static final long serialVersionUID = -9196732086954365246L;

private String toUser;
private String msgType;
private String content;
private String mediaId;
private String thumbMediaId;
private String title;
private String description;

/**
* 获得文本消息builder
*/
public static TextBuilder TEXT() {
return new TextBuilder();
}

/**
* 获得图片消息builder
*/
public static ImageBuilder IMAGE() {
return new ImageBuilder();
}

public String getToUser() {
return this.toUser;
}

public void setToUser(String toUser) {
this.toUser = toUser;
}

public String getMsgType() {
return this.msgType;
}

public void setMsgType(String msgType) {
this.msgType = msgType;
}

public String getContent() {
return this.content;
}

public void setContent(String content) {
this.content = content;
}

public String getMediaId() {
return this.mediaId;
}

public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}

public String getThumbMediaId() {
return this.thumbMediaId;
}

public void setThumbMediaId(String thumbMediaId) {
this.thumbMediaId = thumbMediaId;
}

public String getTitle() {
return this.title;
}

public void setTitle(String title) {
this.title = title;
}

public String getDescription() {
return this.description;
}

public void setDescription(String description) {
this.description = description;
}

public String toJson() {
return WxMaGsonBuilder.create().toJson(this);
}

}

+ 236
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java Dosyayı Görüntüle

@@ -0,0 +1,236 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
import cn.binarywang.wx.miniapp.util.xml.XStreamTransformer;
import com.google.gson.annotations.SerializedName;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.util.ToStringUtils;
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@XStreamAlias("xml")
public class WxMaMessage implements Serializable {
private static final long serialVersionUID = -3586245291677274914L;

@SerializedName("Encrypt")
@XStreamAlias("Encrypt")
@XStreamConverter(value = XStreamCDataConverter.class)
private String encrypt;

@SerializedName("ToUserName")
@XStreamAlias("ToUserName")
@XStreamConverter(value = XStreamCDataConverter.class)
private String toUser;

@SerializedName("FromUserName")
@XStreamAlias("FromUserName")
@XStreamConverter(value = XStreamCDataConverter.class)
private String fromUser;

@SerializedName("CreateTime")
@XStreamAlias("CreateTime")
@XStreamConverter(value = XStreamCDataConverter.class)
private Integer createTime;

@SerializedName("MsgDataFormat")
@XStreamAlias("MsgDataFormat")
@XStreamConverter(value = XStreamCDataConverter.class)
private String msgType;

// 文本消息
@SerializedName("Content")
@XStreamAlias("Content")
@XStreamConverter(value = XStreamCDataConverter.class)
private String content;

@SerializedName("MsgId")
@XStreamAlias("MsgId")
@XStreamConverter(value = XStreamCDataConverter.class)
private Long msgId;

// 图片消息
@SerializedName("PicUrl")
@XStreamAlias("PicUrl")
@XStreamConverter(value = XStreamCDataConverter.class)
private String picUrl;

@SerializedName("MediaId")
@XStreamAlias("MediaId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String mediaId;

// 事件消息
@SerializedName("Event")
@XStreamAlias("Event")
@XStreamConverter(value = XStreamCDataConverter.class)
private String event;

@SerializedName("SessionFrom")
@XStreamAlias("SessionFrom")
@XStreamConverter(value = XStreamCDataConverter.class)
private String sessionFrom;

public static WxMaMessage fromXml(String xml) {
return XStreamTransformer.fromXml(WxMaMessage.class, xml);
}

public static WxMaMessage fromXml(InputStream is) {
return XStreamTransformer.fromXml(WxMaMessage.class, is);
}

/**
* 从加密字符串转换
*
* @param encryptedXml 密文
* @param wxMaConfig 配置存储器对象
* @param timestamp 时间戳
* @param nonce 随机串
* @param msgSignature 签名串
*/
public static WxMaMessage fromEncryptedXml(String encryptedXml,
WxMaConfig wxMaConfig, String timestamp, String nonce,
String msgSignature) {
String plainText = new WxMaCryptUtils(wxMaConfig).decrypt(msgSignature, timestamp, nonce, encryptedXml);
return fromXml(plainText);
}

public static WxMaMessage fromEncryptedXml(InputStream is, WxMaConfig wxMaConfig, String timestamp,
String nonce, String msgSignature) {
try {
return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxMaConfig,
timestamp, nonce, msgSignature);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static WxMaMessage fromJson(String json) {
return WxMaGsonBuilder.create().fromJson(json, WxMaMessage.class);
}

public static WxMaMessage fromEncryptedJson(String encryptedJson, WxMaConfig config) {
try {
WxMaMessage encryptedMessage = fromJson(encryptedJson);
String plainText = new WxMaCryptUtils(config).decrypt(encryptedMessage.getEncrypt());
return fromJson(plainText);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public static WxMaMessage fromEncryptedJson(InputStream inputStream, WxMaConfig config) {
try {
return fromEncryptedJson(IOUtils.toString(inputStream, StandardCharsets.UTF_8), config);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public String toString() {
return ToStringUtils.toSimpleString(this);
}

public String toJson() {
return WxMaGsonBuilder.create().toJson(this);
}

public String getToUser() {
return toUser;
}

public void setToUser(String toUser) {
this.toUser = toUser;
}

public String getFromUser() {
return fromUser;
}

public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}

public Integer getCreateTime() {
return createTime;
}

public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}

public String getMsgType() {
return msgType;
}

public void setMsgType(String msgType) {
this.msgType = msgType;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public Long getMsgId() {
return msgId;
}

public void setMsgId(Long msgId) {
this.msgId = msgId;
}

public String getPicUrl() {
return picUrl;
}

public void setPicUrl(String picUrl) {
this.picUrl = picUrl;
}

public String getMediaId() {
return mediaId;
}

public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}

public String getEvent() {
return event;
}

public void setEvent(String event) {
this.event = event;
}

public String getSessionFrom() {
return sessionFrom;
}

public void setSessionFrom(String sessionFrom) {
this.sessionFrom = sessionFrom;
}

public String getEncrypt() {
return encrypt;
}

public void setEncrypt(String encrypt) {
this.encrypt = encrypt;
}
}

+ 44
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcode.java Dosyayı Görüntüle

@@ -0,0 +1,44 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;

import java.io.Serializable;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaQrcode implements Serializable {
private static final long serialVersionUID = 5777119669111011584L;
private String path;
private int width = 430;

public WxMaQrcode(String path, int width) {
this.path = path;
this.width = width;
}

public static WxMaQrcode fromJson(String json) {
return WxMaGsonBuilder.create().fromJson(json, WxMaQrcode.class);
}

public String getPath() {
return path;
}

public void setPath(String path) {
this.path = path;
}

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}

@Override
public String toString() {
return WxMaGsonBuilder.create().toJson(this);
}
}

+ 214
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java Dosyayı Görüntüle

@@ -0,0 +1,214 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;

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

/**
* 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaTemplateMessage implements Serializable {
private static final long serialVersionUID = 5063374783759519418L;
/**
* touser 是 接收者(用户)的 openid
*/
private String toUser;
/**
* template_id 是 所需下发的模板消息的id
*/
private String templateId;
/**
* <pre>
* page 否 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
* </pre>
*/
private String page;
/**
* form_id 是 表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id
*/
private String formId;
/**
* data 是 模板内容,不填则下发空模板
*/
private List<Data> data = new ArrayList<>();
/**
* color 否 模板内容字体的颜色,不填默认黑色
*/
private String color;
/**
* emphasis_keyword 否 模板需要放大的关键词,不填则默认无放大
*/
private String emphasisKeyword;

private WxMaTemplateMessage(Builder builder) {
setToUser(builder.toUser);
setTemplateId(builder.templateId);
setPage(builder.page);
setFormId(builder.formId);
setData(builder.data);
setColor(builder.color);
setEmphasisKeyword(builder.emphasisKeyword);
}

public static Builder newBuilder() {
return new Builder();
}

public String toJson() {
return WxMaGsonBuilder.create().toJson(this);
}

public String getToUser() {
return toUser;
}

public void setToUser(String toUser) {
this.toUser = toUser;
}

public String getTemplateId() {
return templateId;
}

public void setTemplateId(String templateId) {
this.templateId = templateId;
}

public String getPage() {
return page;
}

public void setPage(String page) {
this.page = page;
}

public String getFormId() {
return formId;
}

public void setFormId(String formId) {
this.formId = formId;
}

public List<Data> getData() {
return data;
}

public void setData(List<Data> data) {
this.data = data;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

public String getEmphasisKeyword() {
return emphasisKeyword;
}

public void setEmphasisKeyword(String emphasisKeyword) {
this.emphasisKeyword = emphasisKeyword;
}

public static class Data {
private String name;
private String value;
private String color;

public Data(String name, String value) {
this.name = name;
this.value = value;
}

public Data(String name, String value, String color) {
this.name = name;
this.value = value;
this.color = color;
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public String getValue() {
return this.value;
}

public void setValue(String value) {
this.value = value;
}

public String getColor() {
return this.color;
}

public void setColor(String color) {
this.color = color;
}

}

public static final class Builder {
private String toUser;
private String templateId;
private String page;
private String formId;
private List<Data> data;
private String color;
private String emphasisKeyword;

private Builder() {
}

public Builder toUser(String toUser) {
this.toUser = toUser;
return this;
}

public Builder templateId(String templateId) {
this.templateId = templateId;
return this;
}

public Builder page(String page) {
this.page = page;
return this;
}

public Builder formId(String formId) {
this.formId = formId;
return this;
}

public Builder data(List<Data> data) {
this.data = data;
return this;
}

public Builder color(String color) {
this.color = color;
return this;
}

public Builder emphasisKeyword(String emphasisKeyword) {
this.emphasisKeyword = emphasisKeyword;
return this;
}

public WxMaTemplateMessage build() {
return new WxMaTemplateMessage(this);
}
}
}

+ 131
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUserInfo.java Dosyayı Görüntüle

@@ -0,0 +1,131 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaUserInfo {
private String openId;
private String nickName;
private String gender;
private String language;
private String city;
private String province;
private String country;
private String avatarUrl;
private String unionId;
private Watermark watermark;

public static WxMaUserInfo fromJson(String json) {
return WxMaGsonBuilder.create().fromJson(json, WxMaUserInfo.class);
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}

public String getOpenId() {
return openId;
}

public void setOpenId(String openId) {
this.openId = openId;
}

public String getNickName() {
return nickName;
}

public void setNickName(String nickName) {
this.nickName = nickName;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public String getLanguage() {
return language;
}

public void setLanguage(String language) {
this.language = language;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getProvince() {
return province;
}

public void setProvince(String province) {
this.province = province;
}

public String getCountry() {
return country;
}

public void setCountry(String country) {
this.country = country;
}

public String getAvatarUrl() {
return avatarUrl;
}

public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}

public String getUnionId() {
return unionId;
}

public void setUnionId(String unionId) {
this.unionId = unionId;
}

public Watermark getWatermark() {
return watermark;
}

public void setWatermark(Watermark watermark) {
this.watermark = watermark;
}

public static class Watermark {
private String timestamp;
private String appid;

public String getTimestamp() {
return timestamp;
}

public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}

public String getAppid() {
return appid;
}

public void setAppid(String appid) {
this.appid = appid;
}
}
}

+ 24
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java Dosyayı Görüntüle

@@ -0,0 +1,24 @@
package cn.binarywang.wx.miniapp.builder;

import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class BaseBuilder<T> {
protected String msgType;
protected String toUser;

@SuppressWarnings("unchecked")
public T toUser(String toUser) {
this.toUser = toUser;
return (T) this;
}

public WxMaKefuMessage build() {
WxMaKefuMessage m = new WxMaKefuMessage();
m.setMsgType(this.msgType);
m.setToUser(this.toUser);
return m;
}
}

+ 27
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/ImageBuilder.java Dosyayı Görüntüle

@@ -0,0 +1,27 @@
package cn.binarywang.wx.miniapp.builder;

import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public final class ImageBuilder extends BaseBuilder<ImageBuilder> {
private String mediaId;

public ImageBuilder() {
this.msgType = WxMaConstants.KefuMsgType.IMAGE;
}

public ImageBuilder mediaId(String media_id) {
this.mediaId = media_id;
return this;
}

@Override
public WxMaKefuMessage build() {
WxMaKefuMessage m = super.build();
m.setMediaId(this.mediaId);
return m;
}
}

+ 27
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/TextBuilder.java Dosyayı Görüntüle

@@ -0,0 +1,27 @@
package cn.binarywang.wx.miniapp.builder;

import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public final class TextBuilder extends BaseBuilder<TextBuilder> {
private String content;

public TextBuilder() {
this.msgType = WxMaConstants.KefuMsgType.TEXT;
}

public TextBuilder content(String content) {
this.content = content;
return this;
}

@Override
public WxMaKefuMessage build() {
WxMaKefuMessage m = super.build();
m.setContent(this.content);
return m;
}
}

+ 73
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java Dosyayı Görüntüle

@@ -0,0 +1,73 @@
package cn.binarywang.wx.miniapp.config;

import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;

import java.util.concurrent.locks.Lock;

/**
* 小程序配置
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaConfig {

String getAccessToken();

Lock getAccessTokenLock();

boolean isAccessTokenExpired();

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

/**
* 应该是线程安全的
*
* @param accessToken 要更新的WxAccessToken对象
*/
void updateAccessToken(WxAccessToken accessToken);

/**
* 应该是线程安全的
*
* @param accessToken 新的accessToken值
* @param expiresInSeconds 过期时间,以秒为单位
*/
void updateAccessToken(String accessToken, int expiresInSeconds);

String getAppid();

String getSecret();

String getToken();

String getAesKey();

String getMsgDataFormat();

long getExpiresTime();

String getHttpProxyHost();

int getHttpProxyPort();

String getHttpProxyUsername();

String getHttpProxyPassword();

/**
* http client builder
*
* @return ApacheHttpClientBuilder
*/
ApacheHttpClientBuilder getApacheHttpClientBuilder();

/**
* 是否自动刷新token
*/
boolean autoRefreshToken();

}

+ 185
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaInMemoryConfig.java Dosyayı Görüntüle

@@ -0,0 +1,185 @@
package cn.binarywang.wx.miniapp.config;

import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.ToStringUtils;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;

import java.io.File;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaInMemoryConfig implements WxMaConfig {
protected volatile String msgDataFormat;
protected volatile String appid;
protected volatile String secret;
protected volatile String token;
protected volatile String accessToken;
protected volatile String aesKey;
protected volatile long expiresTime;

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

protected Lock accessTokenLock = new ReentrantLock();

/**
* 临时文件目录
*/
protected volatile File tmpDirFile;

protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder;

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

public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}

@Override
public Lock getAccessTokenLock() {
return this.accessTokenLock;
}

public void setAccessTokenLock(Lock accessTokenLock) {
this.accessTokenLock = accessTokenLock;
}

@Override
public boolean isAccessTokenExpired() {
return System.currentTimeMillis() > this.expiresTime;
}

@Override
public synchronized void updateAccessToken(WxAccessToken accessToken) {
updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
}

@Override
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
this.accessToken = accessToken;
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
}

@Override
public void expireAccessToken() {
this.expiresTime = 0;
}

@Override
public String getSecret() {
return this.secret;
}

public void setSecret(String secret) {
this.secret = secret;
}

@Override
public String getToken() {
return this.token;
}

public void setToken(String token) {
this.token = token;
}

@Override
public long getExpiresTime() {
return this.expiresTime;
}

public void setExpiresTime(long expiresTime) {
this.expiresTime = expiresTime;
}

@Override
public String getAesKey() {
return this.aesKey;
}

public void setAesKey(String aesKey) {
this.aesKey = aesKey;
}

@Override
public String getMsgDataFormat() {
return this.msgDataFormat;
}

public void setMsgDataFormat(String msgDataFormat) {
this.msgDataFormat = msgDataFormat;
}

@Override
public String getHttpProxyHost() {
return this.httpProxyHost;
}

public void setHttpProxyHost(String httpProxyHost) {
this.httpProxyHost = httpProxyHost;
}

@Override
public int getHttpProxyPort() {
return this.httpProxyPort;
}

public void setHttpProxyPort(int httpProxyPort) {
this.httpProxyPort = httpProxyPort;
}

@Override
public String getHttpProxyUsername() {
return this.httpProxyUsername;
}

public void setHttpProxyUsername(String httpProxyUsername) {
this.httpProxyUsername = httpProxyUsername;
}

@Override
public String getHttpProxyPassword() {
return this.httpProxyPassword;
}

public void setHttpProxyPassword(String httpProxyPassword) {
this.httpProxyPassword = httpProxyPassword;
}

@Override
public String toString() {
return ToStringUtils.toSimpleString(this);
}

@Override
public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
return this.apacheHttpClientBuilder;
}

public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
this.apacheHttpClientBuilder = apacheHttpClientBuilder;
}

@Override
public boolean autoRefreshToken() {
return true;
}

public String getAppid() {
return appid;
}

public void setAppid(String appid) {
this.appid = appid;
}
}

+ 26
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java Dosyayı Görüntüle

@@ -0,0 +1,26 @@
package cn.binarywang.wx.miniapp.constant;

/**
* <pre>
* 小程序常量
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaConstants {
/**
* 消息格式
*/
public static class MsgDataFormat {
public static final String XML = "XML";
public static final String JSON = "JSON";
}

/**
* 客服消息的消息类型
*/
public static class KefuMsgType {
public static final String TEXT = "text";//文本消息
public static final String IMAGE = "image";//图片消息
}
}

+ 20
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageHandler.java Dosyayı Görüntüle

@@ -0,0 +1,20 @@
package cn.binarywang.wx.miniapp.message;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;

import java.util.Map;

/**
* 处理小程序推送消息的处理器接口
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaMessageHandler {

void handle(WxMaMessage message, Map<String, Object> context,
WxMaService service, WxSessionManager sessionManager) throws WxErrorException;

}

+ 28
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageInterceptor.java Dosyayı Görüntüle

@@ -0,0 +1,28 @@
package cn.binarywang.wx.miniapp.message;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;

import java.util.Map;

/**
* 微信消息拦截器,可以用来做验证
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaMessageInterceptor {

/**
* 拦截微信消息
*
* @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @return true代表OK,false代表不OK
*/
boolean intercept(WxMaMessage wxMessage,
Map<String, Object> context,
WxMaService wxMaService,
WxSessionManager sessionManager) throws WxErrorException;

}

+ 17
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageMatcher.java Dosyayı Görüntüle

@@ -0,0 +1,17 @@
package cn.binarywang.wx.miniapp.message;

import cn.binarywang.wx.miniapp.bean.WxMaMessage;

/**
* 消息匹配器,用在消息路由的时候
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaMessageMatcher {

/**
* 消息是否匹配某种模式
*/
boolean match(WxMaMessage message);

}

+ 176
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouter.java Dosyayı Görüntüle

@@ -0,0 +1,176 @@
package cn.binarywang.wx.miniapp.message;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import me.chanjar.weixin.common.api.WxErrorExceptionHandler;
import me.chanjar.weixin.common.api.WxMessageDuplicateChecker;
import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker;
import me.chanjar.weixin.common.session.InternalSession;
import me.chanjar.weixin.common.session.InternalSessionManager;
import me.chanjar.weixin.common.session.StandardSessionManager;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.LogExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaMessageRouter {
private static final int DEFAULT_THREAD_POOL_SIZE = 100;
private final Logger log = LoggerFactory.getLogger(WxMaMessageRouter.class);
private final List<WxMaMessageRouterRule> rules = new ArrayList<>();

private final WxMaService wxMaService;

private ExecutorService executorService;

private WxMessageDuplicateChecker messageDuplicateChecker;

private WxSessionManager sessionManager;

private WxErrorExceptionHandler exceptionHandler;

public WxMaMessageRouter(WxMaService wxMaService) {
this.wxMaService = wxMaService;
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker();
this.sessionManager = new StandardSessionManager();
this.exceptionHandler = new LogExceptionHandler();
}

/**
* <pre>
* 设置自定义的 {@link ExecutorService}
* 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100)
* </pre>
*/
public void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}

/**
* <pre>
* 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker}
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker}
* </pre>
*/
public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) {
this.messageDuplicateChecker = messageDuplicateChecker;
}

/**
* <pre>
* 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager}
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager}
* </pre>
*/
public void setSessionManager(WxSessionManager sessionManager) {
this.sessionManager = sessionManager;
}

/**
* <pre>
* 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler}
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler}
* </pre>
*/
public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}

List<WxMaMessageRouterRule> getRules() {
return this.rules;
}

/**
* 开始一个新的Route规则
*/
public WxMaMessageRouterRule rule() {
return new WxMaMessageRouterRule(this);
}

/**
* 处理微信消息
*/
public void route(final WxMaMessage wxMessage, final Map<String, Object> context) {
final List<WxMaMessageRouterRule> matchRules = new ArrayList<>();
// 收集匹配的规则
for (final WxMaMessageRouterRule rule : this.rules) {
if (rule.test(wxMessage)) {
matchRules.add(rule);
if (!rule.isReEnter()) {
break;
}
}
}

if (matchRules.size() == 0) {
return;
}

final List<Future<?>> futures = new ArrayList<>();
for (final WxMaMessageRouterRule rule : matchRules) {
// 返回最后一个非异步的rule的执行结果
if (rule.isAsync()) {
futures.add(
this.executorService.submit(new Runnable() {
@Override
public void run() {
rule.service(wxMessage, context, WxMaMessageRouter.this.wxMaService, WxMaMessageRouter.this.sessionManager, WxMaMessageRouter.this.exceptionHandler);
}
})
);
} else {
rule.service(wxMessage, context, this.wxMaService, this.sessionManager, this.exceptionHandler);
// 在同步操作结束,session访问结束
this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser());
sessionEndAccess(wxMessage);
}
}

if (futures.size() > 0) {
this.executorService.submit(new Runnable() {
@Override
public void run() {
for (Future<?> future : futures) {
try {
future.get();
WxMaMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser());
// 异步操作结束,session访问结束
sessionEndAccess(wxMessage);
} catch (InterruptedException | ExecutionException e) {
WxMaMessageRouter.this.log.error("Error happened when wait task finish", e);
}
}
}
});
}

}

public void route(final WxMaMessage wxMessage) {
this.route(wxMessage, new HashMap<String, Object>());
}

/**
* 对session的访问结束
*/
protected void sessionEndAccess(WxMaMessage wxMessage) {

InternalSession session = ((InternalSessionManager) this.sessionManager).findSession(wxMessage.getFromUser());
if (session != null) {
session.endAccess();
}

}
}

+ 316
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouterRule.java Dosyayı Görüntüle

@@ -0,0 +1,316 @@
package cn.binarywang.wx.miniapp.message;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import me.chanjar.weixin.common.api.WxErrorExceptionHandler;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaMessageRouterRule {

private final WxMaMessageRouter routerBuilder;

private boolean async = true;

private String fromUser;

private String msgType;

private String event;

private String eventKey;

private String content;

private String rContent;

private WxMaMessageMatcher matcher;

private boolean reEnter = false;

private List<WxMaMessageHandler> handlers = new ArrayList<>();

private List<WxMaMessageInterceptor> interceptors = new ArrayList<>();

public WxMaMessageRouterRule(WxMaMessageRouter routerBuilder) {
this.routerBuilder = routerBuilder;
}

/**
* 设置是否异步执行,默认是true
*/
public WxMaMessageRouterRule async(boolean async) {
this.async = async;
return this;
}

/**
* 如果msgType等于某值
*/
public WxMaMessageRouterRule msgType(String msgType) {
this.msgType = msgType;
return this;
}

/**
* 如果event等于某值
*/
public WxMaMessageRouterRule event(String event) {
this.event = event;
return this;
}

/**
* 如果eventKey等于某值
*/
public WxMaMessageRouterRule eventKey(String eventKey) {
this.eventKey = eventKey;
return this;
}

/**
* 如果content等于某值
*/
public WxMaMessageRouterRule content(String content) {
this.content = content;
return this;
}

/**
* 如果content匹配该正则表达式
*/
public WxMaMessageRouterRule rContent(String regex) {
this.rContent = regex;
return this;
}

/**
* 如果fromUser等于某值
*/
public WxMaMessageRouterRule fromUser(String fromUser) {
this.fromUser = fromUser;
return this;
}

/**
* 如果消息匹配某个matcher,用在用户需要自定义更复杂的匹配规则的时候
*/
public WxMaMessageRouterRule matcher(WxMaMessageMatcher matcher) {
this.matcher = matcher;
return this;
}

/**
* 设置微信消息拦截器
*/
public WxMaMessageRouterRule interceptor(WxMaMessageInterceptor interceptor) {
return interceptor(interceptor, (WxMaMessageInterceptor[]) null);
}

/**
* 设置微信消息拦截器
*/
public WxMaMessageRouterRule interceptor(WxMaMessageInterceptor interceptor, WxMaMessageInterceptor... otherInterceptors) {
this.interceptors.add(interceptor);
if (otherInterceptors != null && otherInterceptors.length > 0) {
for (WxMaMessageInterceptor i : otherInterceptors) {
this.interceptors.add(i);
}
}
return this;
}

/**
* 设置微信消息处理器
*/
public WxMaMessageRouterRule handler(WxMaMessageHandler handler) {
return handler(handler, (WxMaMessageHandler[]) null);
}

/**
* 设置微信消息处理器
*/
public WxMaMessageRouterRule handler(WxMaMessageHandler handler, WxMaMessageHandler... otherHandlers) {
this.handlers.add(handler);
if (otherHandlers != null && otherHandlers.length > 0) {
for (WxMaMessageHandler i : otherHandlers) {
this.handlers.add(i);
}
}
return this;
}

/**
* 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则
*/
public WxMaMessageRouter end() {
this.routerBuilder.getRules().add(this);
return this.routerBuilder;
}

/**
* 规则结束,但是消息还会进入其他规则
*/
public WxMaMessageRouter next() {
this.reEnter = true;
return end();
}

/**
* 将微信自定义的事件修正为不区分大小写,
* 比如框架定义的事件常量为click,但微信传递过来的却是CLICK
*/
protected boolean test(WxMaMessage wxMessage) {
return
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUser()))
&&
(this.msgType == null || this.msgType.toLowerCase().equals((wxMessage.getMsgType() == null ? null : wxMessage.getMsgType().toLowerCase())))
&&
(this.event == null || this.event.toLowerCase().equals((wxMessage.getEvent() == null ? null : wxMessage.getEvent().toLowerCase())))
&&
(this.content == null || this.content
.equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim()))
&&
(this.rContent == null || Pattern
.matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim()))
&&
(this.matcher == null || this.matcher.match(wxMessage))
;
}

/**
* 处理微信推送过来的消息
*
* @return true 代表继续执行别的router,false 代表停止执行别的router
*/
protected void service(WxMaMessage wxMessage,
Map<String, Object> context,
WxMaService wxMaService,
WxSessionManager sessionManager,
WxErrorExceptionHandler exceptionHandler) {
if (context == null) {
context = new HashMap<>();
}

try {
// 如果拦截器不通过
for (WxMaMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context, wxMaService, sessionManager)) {
return;
}
}

// 交给handler处理
for (WxMaMessageHandler handler : this.handlers) {
// 返回最后handler的结果
if (handler == null) {
continue;
}
handler.handle(wxMessage, context, wxMaService, sessionManager);
}
} catch (WxErrorException e) {
exceptionHandler.handle(e);
}
}

public WxMaMessageRouter getRouterBuilder() {
return this.routerBuilder;
}

public boolean isAsync() {
return this.async;
}

public void setAsync(boolean async) {
this.async = async;
}

public String getFromUser() {
return this.fromUser;
}

public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}

public String getMsgType() {
return this.msgType;
}

public void setMsgType(String msgType) {
this.msgType = msgType;
}

public String getEvent() {
return this.event;
}

public void setEvent(String event) {
this.event = event;
}

public String getEventKey() {
return this.eventKey;
}

public void setEventKey(String eventKey) {
this.eventKey = eventKey;
}

public String getContent() {
return this.content;
}

public void setContent(String content) {
this.content = content;
}

public String getrContent() {
return this.rContent;
}

public void setrContent(String rContent) {
this.rContent = rContent;
}

public WxMaMessageMatcher getMatcher() {
return this.matcher;
}

public void setMatcher(WxMaMessageMatcher matcher) {
this.matcher = matcher;
}

public boolean isReEnter() {
return this.reEnter;
}

public void setReEnter(boolean reEnter) {
this.reEnter = reEnter;
}

public List<WxMaMessageHandler> getHandlers() {
return this.handlers;
}

public void setHandlers(List<WxMaMessageHandler> handlers) {
this.handlers = handlers;
}

public List<WxMaMessageInterceptor> getInterceptors() {
return this.interceptors;
}

public void setInterceptors(List<WxMaMessageInterceptor> interceptors) {
this.interceptors = interceptors;
}
}

+ 43
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java Dosyayı Görüntüle

@@ -0,0 +1,43 @@
package cn.binarywang.wx.miniapp.util.crypt;

import cn.binarywang.wx.miniapp.config.WxMaConfig;
import me.chanjar.weixin.common.util.crypto.PKCS7Encoder;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCryptUtil {
public WxMaCryptUtils(WxMaConfig config) {
this.appidOrCorpid = config.getAppid();
this.token = config.getToken();
this.aesKey = Base64.decodeBase64(config.getAesKey() + "=");
}

/**
* AES解密
*
* @param encryptedData 消息密文
* @param ivStr iv字符串
*/
public static String decrypt(String sessionKey, String encryptedData, String ivStr) {
try {
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(Base64.decodeBase64(ivStr)));

Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(sessionKey), "AES"), params);

return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("AES解密失败", e);
}
}

}

+ 61
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/http/QrCodeRequestExecutor.java Dosyayı Görüntüle

@@ -0,0 +1,61 @@
package cn.binarywang.wx.miniapp.util.http;

import cn.binarywang.wx.miniapp.bean.WxMaQrcode;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler;
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;

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

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class QrCodeRequestExecutor implements RequestExecutor<File, WxMaQrcode> {
protected RequestHttp<CloseableHttpClient, HttpHost> requestHttp;

public QrCodeRequestExecutor(RequestHttp requestHttp) {
this.requestHttp = requestHttp;
}

@Override
public File execute(String uri, WxMaQrcode ticket) throws WxErrorException, IOException {
HttpPost httpPost = new HttpPost(uri);
if (requestHttp.getRequestHttpProxy() != null) {
httpPost
.setConfig(RequestConfig.custom()
.setProxy(requestHttp.getRequestHttpProxy())
.build()
);
}
httpPost.setEntity(new StringEntity(ticket.toString()));

try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost);
InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) {
Header[] contentTypeHeader = response.getHeaders("Content-Type");
if (contentTypeHeader != null && contentTypeHeader.length > 0
&& ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) {
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
throw new WxErrorException(WxError.fromJson(responseContent));
}

return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg");
} finally {
httpPost.releaseConnection();
}
}
}

+ 24
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java Dosyayı Görüntüle

@@ -0,0 +1,24 @@
package cn.binarywang.wx.miniapp.util.json;

import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaGsonBuilder {
private static final GsonBuilder INSTANCE = new GsonBuilder();

static {
INSTANCE.disableHtmlEscaping();
INSTANCE.registerTypeAdapter(WxMaKefuMessage.class, new WxMaKefuMessageGsonAdapter());
INSTANCE.registerTypeAdapter(WxMaTemplateMessage.class, new WxMaTemplateMessageGsonAdapter());
}

public static Gson create() {
return INSTANCE.create();
}

}

+ 47
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaKefuMessageGsonAdapter.java Dosyayı Görüntüle

@@ -0,0 +1,47 @@
/*
* KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
*
* This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
* only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
* arose from modification of the original source, or other redistribution of this source
* is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
*/
package cn.binarywang.wx.miniapp.util.json;

import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Type;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaKefuMessageGsonAdapter implements JsonSerializer<WxMaKefuMessage> {

@Override
public JsonElement serialize(WxMaKefuMessage message, Type typeOfSrc, JsonSerializationContext context) {
JsonObject messageJson = new JsonObject();
messageJson.addProperty("touser", message.getToUser());
messageJson.addProperty("msgtype", message.getMsgType());

if (WxMaConstants.KefuMsgType.TEXT.equals(message.getMsgType())) {
JsonObject text = new JsonObject();
text.addProperty("content", message.getContent());
messageJson.add("text", text);
}

if (WxMaConstants.KefuMsgType.IMAGE.equals(message.getMsgType())) {
JsonObject image = new JsonObject();
image.addProperty("media_id", message.getMediaId());
messageJson.add("image", image);
}

return messageJson;
}

}

+ 60
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java Dosyayı Görüntüle

@@ -0,0 +1,60 @@
package cn.binarywang.wx.miniapp.util.json;

import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaTemplateMessageGsonAdapter implements JsonSerializer<WxMaTemplateMessage> {

@Override
public JsonElement serialize(WxMaTemplateMessage message, Type typeOfSrc, JsonSerializationContext context) {
JsonObject messageJson = new JsonObject();
messageJson.addProperty("touser", message.getToUser());
messageJson.addProperty("template_id", message.getTemplateId());
if (message.getPage() != null) {
messageJson.addProperty("page", message.getPage());
}

if (message.getFormId() != null) {
messageJson.addProperty("form_id", message.getFormId());
}

if (message.getPage() != null) {
messageJson.addProperty("page", message.getPage());
}

if (message.getColor() != null) {
messageJson.addProperty("color", message.getColor());
}

if (message.getEmphasisKeyword() != null) {
messageJson.addProperty("emphasis_keyword", message.getEmphasisKeyword());
}

JsonObject data = new JsonObject();
messageJson.add("data", data);

if (message.getData() == null) {
return messageJson;
}

for (WxMaTemplateMessage.Data datum : message.getData()) {
JsonObject dataJson = new JsonObject();
dataJson.addProperty("value", datum.getValue());
if (datum.getColor() != null) {
dataJson.addProperty("color", datum.getColor());
}
data.add(datum.getName(), dataJson);
}

return messageJson;
}

}

+ 88
- 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java Dosyayı Görüntüle

@@ -0,0 +1,88 @@
package cn.binarywang.wx.miniapp.util.xml;

import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import com.thoughtworks.xstream.XStream;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;

import java.io.InputStream;
import java.util.*;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class XStreamTransformer {
private static final Map<Class<?>, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>();

static {
registerClass(WxMaMessage.class);
}

/**
* xml -> pojo
*/
@SuppressWarnings("unchecked")
public static <T> T fromXml(Class<T> clazz, String xml) {
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml);
return object;
}

@SuppressWarnings("unchecked")
public static <T> T fromXml(Class<T> clazz, InputStream is) {
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is);
return object;
}

/**
* pojo -> xml
*/
public static <T> String toXml(Class<T> clazz, T object) {
return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object);
}

/**
* 注册扩展消息的解析器
*
* @param clz 类型
* @param xStream xml解析器
*/
private static void register(Class<?> clz, XStream xStream) {
CLASS_2_XSTREAM_INSTANCE.put(clz, xStream);
}

/**
* 会自动注册该类及其子类
*
* @param clz 要注册的类
*/
private static void registerClass(Class<?> clz) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(clz);
xstream.processAnnotations(getInnerClasses(clz));
if (clz.equals(WxMaMessage.class)) {
// 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId
xstream.aliasField("MsgID", WxMaMessage.class, "msgId");
}

register(clz, xstream);
}

private static Class<?>[] getInnerClasses(Class<?> clz) {
Class<?>[] innerClasses = clz.getClasses();
if (innerClasses == null) {
return null;
}

List<Class<?>> result = new ArrayList<>();
result.addAll(Arrays.asList(innerClasses));
for (Class<?> inner : innerClasses) {
Class<?>[] innerClz = getInnerClasses(inner);
if (innerClz == null) {
continue;
}

result.addAll(Arrays.asList(innerClz));
}

return result.toArray(new Class<?>[0]);
}
}

+ 52
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImplTest.java Dosyayı Görüntüle

@@ -0,0 +1,52 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import com.google.inject.Inject;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

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

import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

/**
* 临时素材接口的测试
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaMediaServiceImplTest {
@Inject
protected WxMaService wxService;

private String mediaId;

@Test
public void testUploadMedia() throws WxErrorException, IOException {
String mediaType = "image";
String fileType = "png";
String fileName = "tmp.png";
try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)) {
WxMediaUploadResult res = this.wxService.getMediaService().uploadMedia(mediaType, fileType, inputStream);
assertNotNull(res.getType());
assertNotNull(res.getCreatedAt());
assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null);
this.mediaId = res.getMediaId();
System.out.println(res);
}
}

@Test(dependsOnMethods = {"testUploadMedia"})
public void testGetMedia() throws WxErrorException {
File file = this.wxService.getMediaService().getMedia(this.mediaId);
assertNotNull(file);
System.out.println(file.getAbsolutePath());
}
}

+ 76
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java Dosyayı Görüntüle

@@ -0,0 +1,76 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import cn.binarywang.wx.miniapp.test.TestConfig;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* 测试客服相关接口
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaMsgServiceImplTest {

@Inject
protected WxMaService wxService;

public void testSendKefuMpNewsMessage() throws WxErrorException {
TestConfig configStorage = (TestConfig) this.wxService
.getWxMaConfig();
WxMaKefuMessage message = new WxMaKefuMessage();
message.setMsgType(WxConsts.CUSTOM_MSG_MPNEWS);
message.setToUser(configStorage.getOpenid());

this.wxService.getMsgService().sendKefuMsg(message);
}

public void testSendKefuMessage() throws WxErrorException {
TestConfig config = (TestConfig) this.wxService
.getWxMaConfig();
WxMaKefuMessage message = new WxMaKefuMessage();
message.setMsgType(WxConsts.CUSTOM_MSG_TEXT);
message.setToUser(config.getOpenid());
message.setContent(
"欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>");

this.wxService.getMsgService().sendKefuMsg(message);
}

@Test(invocationCount = 5, threadPoolSize = 3)
public void testSendTemplateMsg() throws WxErrorException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
TestConfig config = (TestConfig) this.wxService.getWxMaConfig();

WxMaTemplateMessage templateMessage = WxMaTemplateMessage.newBuilder()
.toUser(config.getOpenid())
.formId("FORMID")
.page("index")
.data(Lists.newArrayList(
new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177"),
new WxMaTemplateMessage.Data("keyword2", dateFormat.format(new Date()), "#173177"),
new WxMaTemplateMessage.Data("keyword3", "粤海喜来登酒店", "#173177"),
new WxMaTemplateMessage.Data("keyword4", "广州市天河区天河路208号", "#173177")))
.templateId(config.getTemplateId())
.emphasisKeyword("keyword1.DATA")
.build();

String msgId = this.wxService.getMsgService().sendTemplateMsg(templateMessage);
Assert.assertNotNull(msgId);
System.out.println(msgId);
}

}

+ 26
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java Dosyayı Görüntüle

@@ -0,0 +1,26 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import com.google.inject.Inject;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

import java.io.File;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaQrcodeServiceImplTest {
@Inject
protected WxMaService wxService;

@Test
public void testCreateQrCode() throws Exception {
final File qrCode = this.wxService.getQrcodeService().createQrcode("111", 122);
System.out.println(qrCode);
}

}

+ 35
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java Dosyayı Görüntüle

@@ -0,0 +1,35 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import com.google.inject.Inject;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertTrue;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaServiceImplTest {

@Inject
private WxMaService wxService;

public void testRefreshAccessToken() throws WxErrorException {
WxMaConfig configStorage = this.wxService.getWxMaConfig();
String before = configStorage.getAccessToken();
this.wxService.getAccessToken(false);

String after = configStorage.getAccessToken();
assertNotEquals(before, after);
assertTrue(StringUtils.isNotBlank(after));
}

}

+ 46
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java Dosyayı Görüntüle

@@ -0,0 +1,46 @@
package cn.binarywang.wx.miniapp.api.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import com.google.inject.Inject;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

/**
* 测试用户相关的接口
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaUserServiceImplTest {

@Inject
private WxMaService wxService;

@Test
public void testGetSessionKey() throws Exception {
assertNotNull(this.wxService.getUserService().getSessionInfo("aaa"));
}

@Test
public void testGetUserInfo() throws Exception {
WxMaUserInfo userInfo = this.wxService.getUserService().getUserInfo("tiihtNczf5v6AKRyjwEUhQ==",
"CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==",
"r7BXXKkLb8qrSNn05n0qiA==");
assertNotNull(userInfo);
System.out.println(userInfo.toString());
}

@Test
public void testCheckUserInfo() throws Exception {
assertTrue(this.wxService.getUserService().checkUserInfo("HyVFkGl5F5OQWJZZaNzBBg==",
"{\"nickName\":\"Band\",\"gender\":1,\"language\":\"zh_CN\",\"city\":\"Guangzhou\",\"province\":\"Guangdong\",\"country\":\"CN\",\"avatarUrl\":\"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0\"}",
"75e81ceda165f4ffa64f4068af58c64b8f54b88c"));
}

}

+ 40
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java Dosyayı Görüntüle

@@ -0,0 +1,40 @@
package cn.binarywang.wx.miniapp.bean;

import me.chanjar.weixin.common.api.WxConsts;
import org.testng.Assert;
import org.testng.annotations.Test;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Test
public class WxMaKefuMessageTest {

public void testTextReply() {
WxMaKefuMessage reply = new WxMaKefuMessage();
reply.setToUser("OPENID");
reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT);
reply.setContent("sfsfdsdf");
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}");
}

public void testTextBuild() {
WxMaKefuMessage reply = WxMaKefuMessage.TEXT().toUser("OPENID").content("sfsfdsdf").build();
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}");
}

public void testImageReply() {
WxMaKefuMessage reply = new WxMaKefuMessage();
reply.setToUser("OPENID");
reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE);
reply.setMediaId("MEDIA_ID");
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}");
}

public void testImageBuild() {
WxMaKefuMessage reply = WxMaKefuMessage.IMAGE().toUser("OPENID").mediaId("MEDIA_ID").build();
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}");
}


}

+ 130
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java Dosyayı Görüntüle

@@ -0,0 +1,130 @@
package cn.binarywang.wx.miniapp.bean;

import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import me.chanjar.weixin.common.api.WxConsts;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Test
public class WxMaMessageTest {

public void testFromXml() {
String xml = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName> "
+ "<CreateTime>1348831860</CreateTime>"
+ "<MsgDataFormat><![CDATA[text]]></MsgDataFormat>"
+ "<Content><![CDATA[this is a test]]></Content>"
+ "<MsgId>1234567890123456</MsgId>"
+ "<PicUrl><![CDATA[this is a url]]></PicUrl>"
+ "<MediaId><![CDATA[media_id]]></MediaId>"
+ "<Format><![CDATA[Format]]></Format>"
+ "<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>"
+ "<Location_X>23.134521</Location_X>"
+ "<Location_Y>113.358803</Location_Y>"
+ "<Scale>20</Scale>"
+ "<Label><![CDATA[位置信息]]></Label>"
+ "<Description><![CDATA[公众平台官网链接]]></Description>"
+ "<Url><![CDATA[url]]></Url>"
+ "<Title><![CDATA[公众平台官网链接]]></Title>"
+ "<Event><![CDATA[subscribe]]></Event>"
+ "<EventKey><![CDATA[qrscene_123123]]></EventKey>"
+ "<Ticket><![CDATA[TICKET]]></Ticket>"
+ "<Latitude>23.137466</Latitude>"
+ "<Longitude>113.352425</Longitude>"
+ "<Precision>119.385040</Precision>"
+ "<ScanCodeInfo>"
+ " <ScanType><![CDATA[qrcode]]></ScanType>"
+ " <ScanResult><![CDATA[1]]></ScanResult>"
+ "</ScanCodeInfo>"
+ "<SendPicsInfo>"
+ " <Count>1</Count>\n"
+ " <PicList>"
+ " <item>"
+ " <PicMd5Sum><![CDATA[1b5f7c23b5bf75682a53e7b6d163e185]]></PicMd5Sum>"
+ " </item>"
+ " </PicList>"
+ "</SendPicsInfo>"
+ "<SendLocationInfo>"
+ " <Location_X><![CDATA[23]]></Location_X>\n"
+ " <Location_Y><![CDATA[113]]></Location_Y>\n"
+ " <Scale><![CDATA[15]]></Scale>\n"
+ " <Label><![CDATA[ 广州市海珠区客村艺苑路 106号]]></Label>\n"
+ " <Poiname><![CDATA[wo de poi]]></Poiname>\n"
+ "</SendLocationInfo>"
+ "</xml>";
WxMaMessage wxMessage = WxMaMessage.fromXml(xml);
assertEquals(wxMessage.getToUser(), "toUser");
assertEquals(wxMessage.getFromUser(), "fromUser");
assertEquals(wxMessage.getCreateTime(), new Long(1348831860L));
assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT);
assertEquals(wxMessage.getContent(), "this is a test");
assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L));
assertEquals(wxMessage.getPicUrl(), "this is a url");
assertEquals(wxMessage.getMediaId(), "media_id");
assertEquals(wxMessage.getEvent(), "subscribe");
}

public void testFromXml2() {

String xml = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName> "
+ "<CreateTime>1348831860</CreateTime>"
+ "<MsgDataFormat><![CDATA[text]]></MsgDataFormat>"
+ "<Content><![CDATA[this is a test]]></Content>"
+ "<MsgID>1234567890123456</MsgID>"
+ "<PicUrl><![CDATA[this is a url]]></PicUrl>"
+ "<MediaId><![CDATA[media_id]]></MediaId>"
+ "<Format><![CDATA[Format]]></Format>"
+ "<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>"
+ "<Location_X>23.134521</Location_X>"
+ "<Location_Y>113.358803</Location_Y>"
+ "<Scale>20</Scale>"
+ "<Label><![CDATA[位置信息]]></Label>"
+ "<Description><![CDATA[公众平台官网链接]]></Description>"
+ "<Url><![CDATA[url]]></Url>"
+ "<Title><![CDATA[公众平台官网链接]]></Title>"
+ "<Event><![CDATA[subscribe]]></Event>"
+ "<EventKey><![CDATA[qrscene_123123]]></EventKey>"
+ "<Ticket><![CDATA[TICKET]]></Ticket>"
+ "<Latitude>23.137466</Latitude>"
+ "<Longitude>113.352425</Longitude>"
+ "<Precision>119.385040</Precision>"
+ "<ScanCodeInfo>"
+ " <ScanType><![CDATA[qrcode]]></ScanType>"
+ " <ScanResult><![CDATA[1]]></ScanResult>"
+ "</ScanCodeInfo>"
+ "<SendPicsInfo>"
+ " <Count>1</Count>\n"
+ " <PicList>"
+ " <item>"
+ " <PicMd5Sum><![CDATA[1b5f7c23b5bf75682a53e7b6d163e185]]></PicMd5Sum>"
+ " </item>"
+ " </PicList>"
+ "</SendPicsInfo>"
+ "<SendLocationInfo>"
+ " <Location_X><![CDATA[23]]></Location_X>\n"
+ " <Location_Y><![CDATA[113]]></Location_Y>\n"
+ " <Scale><![CDATA[15]]></Scale>\n"
+ " <Label><![CDATA[ 广州市海珠区客村艺苑路 106号]]></Label>\n"
+ " <Poiname><![CDATA[wo de poi]]></Poiname>\n"
+ "</SendLocationInfo>"
+ "</xml>";
WxMaMessage wxMessage = WxMaMessage.fromXml(xml);
assertEquals(wxMessage.getToUser(), "toUser");
assertEquals(wxMessage.getFromUser(), "fromUser");
assertEquals(wxMessage.getCreateTime(), new Integer(1348831860));
assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT);
assertEquals(wxMessage.getContent(), "this is a test");
assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L));
assertEquals(wxMessage.getPicUrl(), "this is a url");
assertEquals(wxMessage.getMediaId(), "media_id");
assertEquals(wxMessage.getEvent(), "subscribe");
}

}

+ 31
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessageTest.java Dosyayı Görüntüle

@@ -0,0 +1,31 @@
package cn.binarywang.wx.miniapp.bean;

import com.google.common.collect.Lists;
import org.testng.annotations.Test;

import static org.testng.AssertJUnit.assertEquals;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaTemplateMessageTest {
@Test
public void testToJson() throws Exception {
WxMaTemplateMessage tm = WxMaTemplateMessage.newBuilder()
.toUser("OPENID")
//.color("aaaaa")
.formId("FORMID")
.page("index")
.data(Lists.newArrayList(
new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177"),
new WxMaTemplateMessage.Data("keyword2", "2015年01月05日12:30", "#173177"),
new WxMaTemplateMessage.Data("keyword3", "粤海喜来登酒店", "#173177"),
new WxMaTemplateMessage.Data("keyword4", "广州市天河区天河路208号", "#173177")))
.templateId("TEMPLATE_ID")
.emphasisKeyword("keyword1.DATA")
.build();

assertEquals(tm.toJson(), "{\"touser\":\"OPENID\",\"template_id\":\"TEMPLATE_ID\",\"page\":\"index\",\"form_id\":\"FORMID\",\"emphasis_keyword\":\"keyword1.DATA\",\"data\":{\"keyword1\":{\"value\":\"339208499\",\"color\":\"#173177\"},\"keyword2\":{\"value\":\"2015年01月05日12:30\",\"color\":\"#173177\"},\"keyword3\":{\"value\":\"粤海喜来登酒店\",\"color\":\"#173177\"},\"keyword4\":{\"value\":\"广州市天河区天河路208号\",\"color\":\"#173177\"}}}");
}

}

+ 144
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaDemoServer.java Dosyayı Görüntüle

@@ -0,0 +1,144 @@
package cn.binarywang.wx.miniapp.demo;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import cn.binarywang.wx.miniapp.test.TestConfig;
import com.google.common.collect.Lists;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaDemoServer {

private static final WxMaMessageHandler logHandler = new WxMaMessageHandler() {
@Override
public void handle(WxMaMessage wxMessage, Map<String, Object> context,
WxMaService service, WxSessionManager sessionManager) throws WxErrorException {
System.out.println("收到消息:" + wxMessage.toString());
service.getMsgService().sendKefuMsg(WxMaKefuMessage.TEXT().content("收到信息为:" + wxMessage.toJson())
.toUser(wxMessage.getFromUser()).build());
}
};
private static final WxMaMessageHandler textHandler = new WxMaMessageHandler() {
@Override
public void handle(WxMaMessage wxMessage, Map<String, Object> context,
WxMaService service, WxSessionManager sessionManager)
throws WxErrorException {
service.getMsgService().sendKefuMsg(WxMaKefuMessage.TEXT().content("回复文本消息")
.toUser(wxMessage.getFromUser()).build());
}

};
private static final WxMaMessageHandler picHandler = new WxMaMessageHandler() {
@Override
public void handle(WxMaMessage wxMessage, Map<String, Object> context,
WxMaService service, WxSessionManager sessionManager) throws WxErrorException {
try {
WxMediaUploadResult uploadResult = service.getMediaService()
.uploadMedia("image", "png",
ClassLoader.getSystemResourceAsStream("tmp.png"));
service.getMsgService().sendKefuMsg(
WxMaKefuMessage
.IMAGE()
.mediaId(uploadResult.getMediaId())
.toUser(wxMessage.getFromUser())
.build());
} catch (WxErrorException e) {
e.printStackTrace();
}
}
};
private static final WxMaMessageHandler qrcodeHandler = new WxMaMessageHandler() {
@Override
public void handle(WxMaMessage wxMessage, Map<String, Object> context,
WxMaService service, WxSessionManager sessionManager) throws WxErrorException {
try {
final File file = service.getQrcodeService().createQrcode("123", 430);
WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
service.getMsgService().sendKefuMsg(
WxMaKefuMessage
.IMAGE()
.mediaId(uploadResult.getMediaId())
.toUser(wxMessage.getFromUser())
.build());
} catch (WxErrorException e) {
e.printStackTrace();
}
}
};

private static final WxMaMessageHandler templateMsgHandler = new WxMaMessageHandler() {
@Override
public void handle(WxMaMessage wxMessage, Map<String, Object> context,
WxMaService service, WxSessionManager sessionManager)
throws WxErrorException {
service.getMsgService().sendTemplateMsg(WxMaTemplateMessage.newBuilder()
.templateId(templateId).data(Lists.newArrayList(
new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177")))
.toUser(wxMessage.getFromUser())
.formId("自己替换可用的formid")
.build());
}

};

private static WxMaConfig config;
private static WxMaService service;
private static WxMaMessageRouter router;
private static String templateId;

public static void main(String[] args) throws Exception {
init();

Server server = new Server(8080);

ServletHandler servletHandler = new ServletHandler();
server.setHandler(servletHandler);

ServletHolder endpointServletHolder = new ServletHolder(new WxMaPortalServlet(config, service, router));
servletHandler.addServletWithMapping(endpointServletHolder, "/*");

server.start();
server.join();
}

private static void init() {
try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) {
TestConfig config = TestConfig.fromXml(is1);
config.setAccessTokenLock(new ReentrantLock());
templateId = config.getTemplateId();

WxMaDemoServer.config = config;
service = new WxMaServiceImpl();
service.setWxMaConfig(config);

router = new WxMaMessageRouter(service);

router.rule().handler(logHandler).next()
.rule().async(false).content("模板").handler(templateMsgHandler).end()
.rule().async(false).content("文本").handler(textHandler).end()
.rule().async(false).content("图片").handler(picHandler).end()
.rule().async(false).content("二维码").handler(qrcodeHandler).end();
} catch (IOException e) {
e.printStackTrace();
}
}
}

+ 92
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaPortalServlet.java Dosyayı Görüntüle

@@ -0,0 +1,92 @@
package cn.binarywang.wx.miniapp.demo;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaPortalServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

private WxMaConfig wxMaConfig;
private WxMaService wxMaService;
private WxMaMessageRouter wxMaMessageRouter;

WxMaPortalServlet(WxMaConfig wxMaConfig, WxMaService wxMaService,
WxMaMessageRouter wxMaMessageRouter) {
this.wxMaConfig = wxMaConfig;
this.wxMaService = wxMaService;
this.wxMaMessageRouter = wxMaMessageRouter;
}

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);

String signature = request.getParameter("signature");
String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp");

if (!this.wxMaService.checkSignature(timestamp, nonce, signature)) {
// 消息签名不正确,说明不是公众平台发过来的消息
response.getWriter().println("非法请求");
return;
}

String echoStr = request.getParameter("echostr");
if (StringUtils.isNotBlank(echoStr)) {
// 说明是一个仅仅用来验证的请求,回显echostr
response.getWriter().println(echoStr);
return;
}

String encryptType = request.getParameter("encrypt_type");
final boolean isJson = Objects.equals(this.wxMaConfig.getMsgDataFormat(), WxMaConstants.MsgDataFormat.JSON);
if (StringUtils.isBlank(encryptType)) {
// 明文传输的消息
WxMaMessage inMessage;
if (isJson) {
inMessage = WxMaMessage.fromJson(IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8));
} else {//xml
inMessage = WxMaMessage.fromXml(request.getInputStream());
}

this.wxMaMessageRouter.route(inMessage);
response.getWriter().write("success");
return;
}

if ("aes".equals(encryptType)) {
// 是aes加密的消息
String msgSignature = request.getParameter("msg_signature");
WxMaMessage inMessage;
if (isJson) {
inMessage = WxMaMessage.fromEncryptedJson(request.getInputStream(), this.wxMaConfig);
} else {//xml
inMessage = WxMaMessage.fromEncryptedXml(request.getInputStream(), this.wxMaConfig, timestamp, nonce, msgSignature);
}

this.wxMaMessageRouter.route(inMessage);
response.getWriter().write("success");
return;
}

response.getWriter().println("不可识别的加密类型");
}

}

+ 33
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/ApiTestModule.java Dosyayı Görüntüle

@@ -0,0 +1,33 @@
package cn.binarywang.wx.miniapp.test;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import com.google.inject.Binder;
import com.google.inject.Module;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.ReentrantLock;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class ApiTestModule implements Module {

@Override
public void configure(Binder binder) {
try (InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml")) {
TestConfig config = TestConfig.fromXml(inputStream);
config.setAccessTokenLock(new ReentrantLock());

WxMaService wxService = new cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl();
wxService.setWxMaConfig(config);

binder.bind(WxMaService.class).toInstance(wxService);
binder.bind(WxMaConfig.class).toInstance(config);
} catch (IOException e) {
e.printStackTrace();
}
}

}

+ 61
- 0
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConfig.java Dosyayı Görüntüle

@@ -0,0 +1,61 @@
package cn.binarywang.wx.miniapp.test;

import cn.binarywang.wx.miniapp.config.WxMaInMemoryConfig;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import org.apache.commons.lang3.builder.ToStringBuilder;

import java.io.InputStream;
import java.util.concurrent.locks.Lock;

/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@XStreamAlias("xml")
public class TestConfig extends WxMaInMemoryConfig {

private String openid;
private String kfAccount;
private String templateId;

public static TestConfig fromXml(InputStream is) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(TestConfig.class);
return (TestConfig) xstream.fromXML(is);
}

public String getOpenid() {
return this.openid;
}

public void setOpenid(String openid) {
this.openid = openid;
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}

public String getKfAccount() {
return this.kfAccount;
}

public void setKfAccount(String kfAccount) {
this.kfAccount = kfAccount;
}

public String getTemplateId() {
return this.templateId;
}

public void setTemplateId(String templateId) {
this.templateId = templateId;
}

public void setAccessTokenLock(Lock lock) {
super.accessTokenLock = lock;
}

}

+ 14
- 0
weixin-java-miniapp/src/test/resources/logback-test.xml Dosyayı Görüntüle

@@ -0,0 +1,14 @@
<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="info">
<appender-ref ref="STDOUT"/>
</root>

<logger name="cn.binarywang.wx.miniapp" level="debug"/>
</configuration>

+ 11
- 0
weixin-java-miniapp/src/test/resources/test-config.sample.xml Dosyayı Görüntüle

@@ -0,0 +1,11 @@
<xml>
<msgDataFormat>JSON或者XML</msgDataFormat>
<appid>appid</appid>
<secret>secret</secret>
<token>Token</token>
<aesKey>EncodingAESKey</aesKey>
<accessToken>可以不填写</accessToken>
<expiresTime>可以不填写</expiresTime>
<openid>某个用户的openId</openid>
<templateId>模版消息的模版ID</templateId>
</xml>

BIN
weixin-java-miniapp/src/test/resources/tmp.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 253  |  Yükseklik: 253  |  Boyut: 5.1 KiB

Yükleniyor…
İptal
Kaydet