提交 009a9176 编写于 作者: 112Lst's avatar 112Lst

【add】微信接口开发

上级 68362a7a
......@@ -12,6 +12,7 @@
<module name="club-auth-domain" />
<module name="club-starter" />
<module name="club-auth-application-controller" />
<module name="club-wx" />
<module name="club-application-job" />
<module name="club-auth-application-job" />
<module name="club-gateway" />
......
......@@ -21,5 +21,7 @@
<file url="file://$PROJECT_DIR$/club-subject/club-starter/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/club-subject/club-subject-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/club-subject/club-subject-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/club-wx/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/club-wx/src/main/resources" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
......@@ -9,6 +9,7 @@
<option value="$PROJECT_DIR$/club-oss/pom.xml" />
<option value="$PROJECT_DIR$/club-auth/pom.xml" />
<option value="$PROJECT_DIR$/club-gateway/pom.xml" />
<option value="$PROJECT_DIR$/club-wx/pom.xml" />
</list>
</option>
<option name="ignoredFiles">
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lssst</groupId>
<artifactId>club-wx</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.4.2</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.18</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>central</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>
\ No newline at end of file
package com.lssst.wx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
/**
* 微信服务启动器
*/
@SpringBootApplication
@ComponentScan("com.lssst")
public class WxApplication {
public static void main(String[] args) {
SpringApplication.run(WxApplication.class);
}
}
package com.lssst.wx.controller;
import com.lssst.wx.handler.WxChatMsgFactory;
import com.lssst.wx.handler.WxChatMsgHandler;
import com.lssst.wx.utils.MessageUtil;
import com.lssst.wx.utils.SHA1;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
@RestController
@Slf4j
public class CallBackController {
private static final String token = "adwidhaidwoaid";
@Resource
private WxChatMsgFactory wxChatMsgFactory;
@RequestMapping("/test")
public String test() {
return "hello world";
}
/**
* 回调消息校验
*/
@GetMapping("callback")
public String callback(@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr) {
log.info("get验签请求参数:signature:{},timestamp:{},nonce:{},echostr:{}",
signature, timestamp, nonce, echostr);
String shaStr = SHA1.getSHA1(token, timestamp, nonce, "");
if (signature.equals(shaStr)) {
return echostr;
}
return "unknown";
}
@PostMapping(value = "callback", produces = "application/xml;charset=UTF-8")
public String callback(
@RequestBody String requestBody,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam(value = "msg_signature", required = false) String msgSignature) {
log.info("接收到微信消息:requestBody:{}", requestBody);
Map<String, String> messageMap = MessageUtil.parseXml(requestBody);
String msgType = messageMap.get("MsgType");
String event = messageMap.get("Event") == null ? "" : messageMap.get("Event");
log.info("msgType:{},event:{}", msgType, event);
StringBuilder sb = new StringBuilder();
sb.append(msgType);
if (!StringUtils.isEmpty(event)) {
sb.append(".");
sb.append(event);
}
String msgTypeKey = sb.toString();
WxChatMsgHandler wxChatMsgHandler = wxChatMsgFactory.getHandlerByMsgType(msgTypeKey);
if (Objects.isNull(wxChatMsgHandler)) {
return "unknown";
}
String replyContent = wxChatMsgHandler.dealMsg(messageMap);
log.info("replyContent:{}", replyContent);
return replyContent;
}
}
package com.lssst.wx.handler;
import com.lssst.wx.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class ReceiveTextMsgHandler implements WxChatMsgHandler {
private static final String KEY_WORD = "验证码";
@Resource
private RedisUtil redisUtil;
@Override
public WxChatMsgTypeEnum getMsgType() {
return WxChatMsgTypeEnum.TEXT_MSG;
}
@Override
public String dealMsg(Map<String, String> messageMap) {
log.info("接收到文本消息事件");
String content = messageMap.get("Content");
if (!KEY_WORD.equals(content)) {
return "";
}
String fromUserName = messageMap.get("FromUserName");
String toUserName = messageMap.get("ToUserName");
Random random = new Random();
int num = random.nextInt(1000);
String numKey = redisUtil.buildKey(fromUserName, String.valueOf(num));
redisUtil.setNx(numKey, "1", 5L, TimeUnit.MINUTES);
String numContent = "您当前的验证码是:" + num + "5分钟内有效";
String replyContent = "<xml>\n" +
" <ToUserName><![CDATA[" + fromUserName + "]]></ToUserName>\n" +
" <FromUserName><![CDATA[" + toUserName + "]]></FromUserName>\n" +
" <CreateTime>12345678</CreateTime>\n" +
" <MsgType><![CDATA[text]]></MsgType>\n" +
" <Content><![CDATA[" + numContent + "]]></Content>\n" +
"</xml>";
return replyContent;
}
}
package com.lssst.wx.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@Slf4j
public class SubscribeMsgHandler implements WxChatMsgHandler {
@Override
public WxChatMsgTypeEnum getMsgType() {
return WxChatMsgTypeEnum.SUBSCRIBE;
}
@Override
public String dealMsg(Map<String, String> messageMap) {
log.info("触发用户关注事件!");
String fromUserName = messageMap.get("FromUserName");
String toUserName = messageMap.get("ToUserName");
String subscribeContent = "感谢您的关注,我是xxx";
String content = "<xml>\n" +
" <ToUserName><![CDATA[" + fromUserName + "]]></ToUserName>\n" +
" <FromUserName><![CDATA[" + toUserName + "]]></FromUserName>\n" +
" <CreateTime>12345678</CreateTime>\n" +
" <MsgType><![CDATA[text]]></MsgType>\n" +
" <Content><![CDATA[" + subscribeContent + "]]></Content>\n" +
"</xml>";
return content;
}
}
package com.lssst.wx.handler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class WxChatMsgFactory implements InitializingBean {
@Resource
private List<WxChatMsgHandler> wxChatMsgHandlerList;
private Map<WxChatMsgTypeEnum, WxChatMsgHandler> handlerMap = new HashMap<>();
public WxChatMsgHandler getHandlerByMsgType(String msgType) {
WxChatMsgTypeEnum msgTypeEnum = WxChatMsgTypeEnum.getByMsgType(msgType);
return handlerMap.get(msgTypeEnum);
}
@Override
public void afterPropertiesSet() throws Exception {
for (WxChatMsgHandler wxChatMsgHandler : wxChatMsgHandlerList) {
handlerMap.put(wxChatMsgHandler.getMsgType(), wxChatMsgHandler);
}
}
}
package com.lssst.wx.handler;
import java.util.Map;
public interface WxChatMsgHandler {
WxChatMsgTypeEnum getMsgType();
String dealMsg(Map<String, String> messageMap);
}
package com.lssst.wx.handler;
public enum WxChatMsgTypeEnum {
SUBSCRIBE("event.subscribe", "用户关注事件"),
TEXT_MSG("text", "接收用户文本消息");
private String msgType;
private String desc;
WxChatMsgTypeEnum(String msgType, String desc) {
this.msgType = msgType;
this.desc = desc;
}
public static WxChatMsgTypeEnum getByMsgType(String msgType) {
for (WxChatMsgTypeEnum wxChatMsgTypeEnum : WxChatMsgTypeEnum.values()) {
if (wxChatMsgTypeEnum.msgType.equals(msgType)) {
return wxChatMsgTypeEnum;
}
}
return null;
}
}
package com.lssst.wx.redis;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis的config处理
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
return redisTemplate;
}
private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jsonRedisSerializer.setObjectMapper(objectMapper);
return jsonRedisSerializer;
}
}
package com.lssst.wx.redis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* RedisUtil工具类
*/
@Component
@Slf4j
public class RedisUtil {
@Resource
private RedisTemplate redisTemplate;
private static final String CACHE_KEY_SEPARATOR = ".";
/**
* 构建缓存key
*/
public String buildKey(String... strObjs) {
return Stream.of(strObjs).collect(Collectors.joining(CACHE_KEY_SEPARATOR));
}
/**
* 是否存在key
*/
public boolean exist(String key) {
return redisTemplate.hasKey(key);
}
/**
* 删除key
*/
public boolean del(String key) {
return redisTemplate.delete(key);
}
/**
* set(不带过期)
*/
public void set(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* set(带过期)
*/
public boolean setNx(String key, String value, Long time, TimeUnit timeUnit) {
return redisTemplate.opsForValue().setIfAbsent(key, value, time, timeUnit);
}
/**
* 获取string类型缓存
*/
public String get(String key) {
return (String) redisTemplate.opsForValue().get(key);
}
public Boolean zAdd(String key, String value, Long score) {
return redisTemplate.opsForZSet().add(key, value, Double.valueOf(String.valueOf(score)));
}
public Long countZset(String key) {
return redisTemplate.opsForZSet().size(key);
}
public Set<String> rangeZset(String key, long start, long end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
public Long removeZset(String key, Object value) {
return redisTemplate.opsForZSet().remove(key, value);
}
public void removeZsetList(String key, Set<String> value) {
value.stream().forEach((val) -> redisTemplate.opsForZSet().remove(key, val));
}
public Double score(String key, Object value) {
return redisTemplate.opsForZSet().score(key, value);
}
public Set<String> rangeByScore(String key, long start, long end) {
return redisTemplate.opsForZSet().rangeByScore(key, Double.valueOf(String.valueOf(start)), Double.valueOf(String.valueOf(end)));
}
public Object addScore(String key, Object obj, double score) {
return redisTemplate.opsForZSet().incrementScore(key, obj, score);
}
public Object rank(String key, Object obj) {
return redisTemplate.opsForZSet().rank(key, obj);
}
}
package com.lssst.wx.utils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MessageUtil {
/**
* 解析微信发来的请求(XML).
*
* @param msg 消息
* @return map
*/
public static Map<String, String> parseXml(final String msg) {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
// 从request中取得输入流
try (InputStream inputStream = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8.name()))) {
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
}
package com.lssst.wx.utils;
import lombok.extern.slf4j.Slf4j;
import java.security.MessageDigest;
import java.util.Arrays;
/**
* sha1生成签名工具
*/
@Slf4j
public class SHA1 {
/**
* 用SHA1算法生成安全签名
*
* @param token 票据
* @param timestamp 时间戳
* @param nonce 随机字符串
* @param encrypt 密文
* @return 安全签名
*/
public static String getSHA1(String token, String timestamp, String nonce, String encrypt) {
try {
String[] array = new String[]{token, timestamp, nonce, encrypt};
StringBuffer sb = new StringBuffer();
// 字符串排序
Arrays.sort(array);
for (int i = 0; i < 4; i++) {
sb.append(array[i]);
}
String str = sb.toString();
// SHA1签名生成
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(str.getBytes());
byte[] digest = md.digest();
StringBuffer hexStr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++) {
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2) {
hexStr.append(0);
}
hexStr.append(shaHex);
}
return hexStr.toString();
} catch (Exception e) {
log.error("sha加密生成签名失败:", e);
return null;
}
}
}
server:
port: 3012
spring:
redis:
# Redis数据库索引(默认为0)
database: 1
# Redis服务器地址
host: 117.72.14.166
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: jichi1234
# 连接超时时间
timeout: 2s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册