mirror of
https://github.com/201206030/novel-plus.git
synced 2025-07-30 15:08:46 +00:00
初步实现微信自动回复消息
This commit is contained in:
parent
d9ad02e3f5
commit
39faf4a6e8
@ -66,6 +66,12 @@
|
||||
<version>${sharding.jdbc.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.16</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--分页助手启动器-->
|
||||
<dependency>
|
||||
|
@ -0,0 +1,40 @@
|
||||
package com.java2nb.novel.controller;
|
||||
|
||||
import com.java2nb.novel.core.except.AesException;
|
||||
import com.java2nb.novel.core.utils.WXPublicUtils;
|
||||
import com.java2nb.novel.dto.WxMessageDto;
|
||||
import com.java2nb.novel.service.WeChatService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author zhu
|
||||
* @date 2023/5/9
|
||||
* @description
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/wechat")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class WeChatController {
|
||||
|
||||
@Autowired
|
||||
private WeChatService weChatService;
|
||||
|
||||
@GetMapping("/")
|
||||
@ResponseBody
|
||||
public String weCheck(HttpServletRequest request) throws AesException {
|
||||
return weChatService.weCheck(request);
|
||||
}
|
||||
|
||||
@PostMapping("/")
|
||||
@ResponseBody
|
||||
public String getMessage(HttpServletRequest request,@RequestBody WxMessageDto dto) {
|
||||
return weChatService.getMessage(request, dto);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.java2nb.novel.core.except;
|
||||
|
||||
/**
|
||||
* @author zhu
|
||||
* @date 2023/5/9
|
||||
* @description
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AesException extends Exception {
|
||||
|
||||
public final static int OK = 0;
|
||||
public final static int ValidateSignatureError = -40001;
|
||||
public final static int ParseXmlError = -40002;
|
||||
public final static int ComputeSignatureError = -40003;
|
||||
public final static int IllegalAesKey = -40004;
|
||||
public final static int ValidateAppidError = -40005;
|
||||
public final static int EncryptAESError = -40006;
|
||||
public final static int DecryptAESError = -40007;
|
||||
public final static int IllegalBuffer = -40008;
|
||||
public final static int EncodeBase64Error = -40009;
|
||||
public final static int DecodeBase64Error = -40010;
|
||||
public final static int GenReturnXmlError = -40011;
|
||||
|
||||
private int code;
|
||||
|
||||
private static String getMessage(int code) {
|
||||
switch (code) {
|
||||
case ValidateSignatureError:
|
||||
return "签名验证错误";
|
||||
case ParseXmlError:
|
||||
return "xml解析失败";
|
||||
case ComputeSignatureError:
|
||||
return "sha加密生成签名失败";
|
||||
case IllegalAesKey:
|
||||
return "SymmetricKey非法";
|
||||
case ValidateAppidError:
|
||||
return "appid校验失败";
|
||||
case EncryptAESError:
|
||||
return "aes加密失败";
|
||||
case DecryptAESError:
|
||||
return "aes解密失败";
|
||||
case IllegalBuffer:
|
||||
return "解密后得到的buffer非法";
|
||||
case EncodeBase64Error:
|
||||
return "base64加密错误";
|
||||
case DecodeBase64Error:
|
||||
return "base64解密错误";
|
||||
case GenReturnXmlError:
|
||||
return "xml生成失败";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public AesException(int code) {
|
||||
super(getMessage(code));
|
||||
this.code = code;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.java2nb.novel.core.utils;
|
||||
|
||||
import com.java2nb.novel.core.except.AesException;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author zhu
|
||||
* @date 2023/5/9
|
||||
* @description
|
||||
*/
|
||||
public class SHA1 {
|
||||
/**
|
||||
* 用SHA1算法验证Token
|
||||
*
|
||||
* @param token 票据
|
||||
* @param timestamp 时间戳
|
||||
* @param nonce 随机字符串
|
||||
* @return 安全签名
|
||||
* @throws AesException
|
||||
*/
|
||||
public static String getSHA1(String token, String timestamp, String nonce) throws AesException {
|
||||
try {
|
||||
String[] array = new String[]{token, timestamp, nonce};
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// 字符串排序
|
||||
Arrays.sort(array);
|
||||
for (int i = 0; i < 3; 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) {
|
||||
e.printStackTrace();
|
||||
throw new AesException(AesException.ComputeSignatureError);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.java2nb.novel.core.utils;
|
||||
|
||||
import com.java2nb.novel.core.except.AesException;
|
||||
|
||||
/**
|
||||
* @author zhu
|
||||
* @date 2023/5/9
|
||||
* @description
|
||||
*/
|
||||
public class WXPublicUtils {
|
||||
|
||||
/**
|
||||
* 验证Token
|
||||
* @param msgSignature 签名串,对应URL参数的signature
|
||||
* @param timeStamp 时间戳,对应URL参数的timestamp
|
||||
* @param nonce 随机串,对应URL参数的nonce
|
||||
*
|
||||
* @return 是否为安全签名
|
||||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
|
||||
*/
|
||||
public static boolean verifyUrl(String msgSignature, String timeStamp, String nonce)
|
||||
throws AesException {
|
||||
// 这里的 WXPublicConstants.TOKEN 填写你自己设置的Token就可以了
|
||||
String signature = SHA1.getSHA1("zyb707099", timeStamp, nonce);
|
||||
if (!signature.equals(msgSignature)) {
|
||||
throw new AesException(AesException.ValidateSignatureError);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package com.java2nb.novel.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* @author zhu
|
||||
* @date 2023/5/9
|
||||
* @description
|
||||
*/
|
||||
@Data
|
||||
@XmlRootElement(name = "xml")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class WxMessageDto {
|
||||
/**
|
||||
* 开发者微信号
|
||||
*/
|
||||
@XmlElement(name = "ToUserName")
|
||||
private String toUserName;
|
||||
|
||||
/**
|
||||
* 发送方帐号(一个OpenID)
|
||||
*/
|
||||
@XmlElement(name = "FromUserName")
|
||||
private String fromUserName;
|
||||
|
||||
/**
|
||||
* 消息创建时间 (整型)
|
||||
*/
|
||||
@XmlElement(name = "CreateTime")
|
||||
private String createTime;
|
||||
|
||||
/**
|
||||
* 消息类型,文本为text
|
||||
*/
|
||||
@XmlElement(name = "MsgType")
|
||||
private String msgType;
|
||||
|
||||
/**
|
||||
* 文本消息内容
|
||||
*/
|
||||
@XmlElement(name = "Content")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息id,64位整型
|
||||
*/
|
||||
@XmlElement(name = "MsgId")
|
||||
private String msgId;
|
||||
|
||||
/**
|
||||
* 消息的数据id
|
||||
*/
|
||||
@XmlElement(name = "MsgDataId")
|
||||
private String msgDataId;
|
||||
|
||||
/**
|
||||
* 多图文时第几篇文章,从1开始(消息如果来自文章时才有)
|
||||
*/
|
||||
@XmlElement(name = "Idx")
|
||||
private String idx;
|
||||
|
||||
/**
|
||||
* 图片链接(由系统生成)
|
||||
*/
|
||||
@XmlElement(name = "PicUrl")
|
||||
private String picUrl;
|
||||
|
||||
/**
|
||||
* 图片消息媒体id,可以调用获取临时素材接口拉取数据。
|
||||
*/
|
||||
@XmlElement(name = "MediaId")
|
||||
private String mediaId;
|
||||
|
||||
/**
|
||||
* 语音格式:amr
|
||||
*/
|
||||
@XmlElement(name = "Format")
|
||||
private String format;
|
||||
/**
|
||||
* 语音识别结果,UTF8编码
|
||||
*/
|
||||
@XmlElement(name = "Recognition")
|
||||
private String recognition;
|
||||
|
||||
/**
|
||||
* 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
|
||||
*/
|
||||
@XmlElement(name = "ThumbMediaId")
|
||||
private String thumbMediaId;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.java2nb.novel.service;
|
||||
|
||||
import com.java2nb.novel.core.except.AesException;
|
||||
import com.java2nb.novel.dto.WxMessageDto;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author zhu
|
||||
* @date 2023/5/9
|
||||
* @description
|
||||
*/
|
||||
public interface WeChatService {
|
||||
/**
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
* @throws AesException
|
||||
*/
|
||||
String weCheck(HttpServletRequest request) throws AesException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param request
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
String getMessage(HttpServletRequest request, WxMessageDto dto);
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package com.java2nb.novel.service.impl;
|
||||
|
||||
import cn.hutool.core.util.XmlUtil;
|
||||
import com.java2nb.novel.core.except.AesException;
|
||||
import com.java2nb.novel.core.utils.WXPublicUtils;
|
||||
import com.java2nb.novel.dto.WxMessageDto;
|
||||
import com.java2nb.novel.service.WeChatService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author zhu
|
||||
* @date 2023/5/9
|
||||
* @description
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class WeChatServiceImpl implements WeChatService {
|
||||
@Override
|
||||
public String weCheck(HttpServletRequest request) throws AesException {
|
||||
String msgSignature = request.getParameter("signature");
|
||||
String msgTimestamp = request.getParameter("timestamp");
|
||||
String msgNonce = request.getParameter("nonce");
|
||||
String echostr = request.getParameter("echostr");
|
||||
if (WXPublicUtils.verifyUrl(msgSignature, msgTimestamp, msgNonce)) {
|
||||
return echostr;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage(HttpServletRequest request, WxMessageDto dto) {
|
||||
String msgSignature = request.getParameter("signature");
|
||||
String msgTimestamp = request.getParameter("timestamp");
|
||||
String msgNonce = request.getParameter("nonce");
|
||||
String echostr = request.getParameter("echostr");
|
||||
StringBuilder sb = new StringBuilder("<xml>\n" +
|
||||
" <ToUserName><![CDATA[" + dto.getFromUserName() + "]]></ToUserName>\n" +
|
||||
" <FromUserName><![CDATA[" + dto.getToUserName() + "]]></FromUserName>\n" +
|
||||
" <CreateTime>"+dto.getCreateTime()+"</CreateTime>\n");
|
||||
switch (dto.getMsgType()) {
|
||||
case "text":
|
||||
//处理文本消息
|
||||
disposeText(sb, dto.getContent());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sb.append("</xml>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void disposeText(StringBuilder sb, String content) {
|
||||
//查询是否存在该返回信息逻辑
|
||||
//无逻辑,返回菜单
|
||||
String stringBuilder = "你发的信息小仙无法识别嗷!\n" +
|
||||
"请回复以下数字:\n" +
|
||||
"1. 北京\n" +
|
||||
"2. 上海\n" +
|
||||
"3. 武汉\n" +
|
||||
"4. 小说";
|
||||
sb.append("<MsgType><![CDATA[text]]></MsgType>\n");
|
||||
sb.append("<Content><![CDATA[" + stringBuilder + "]]></Content>\n");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user