This commit is contained in:
PQZ 2025-04-25 17:39:31 +08:00
parent 6147418efe
commit 0b93959d61
11 changed files with 456 additions and 18 deletions

View File

@ -70,6 +70,11 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-common</artifactId>
<version>4.6.0</version>
</dependency>
</dependencies>

View File

@ -2,22 +2,15 @@ package com.ruoyi.api;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.busi.utils.WeChatUtils;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.GzhLoginBody;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.member.service.IMemberUserService;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
@ -30,13 +23,12 @@ public class WxApi {
private IMemberUserService memberUserService;
@GetMapping("/getCodeUrl")
@Anonymous
public Map<String, String> getCodeUrl(String userType,String url) {
public Map<String, String> getCodeUrl(String userType, String url) {
Map<String, String> res = new HashMap<>();
res.put("codeUrl", weChatUtils.getCodeUrl("https://www.ddtg.site/#/"+url, userType));
return res;
res.put("codeUrl", weChatUtils.getCodeUrl("https://www.ddtg.site/#/" + url, userType));
return res;
}
@GetMapping("/getWebAccessTokenAndOpenid")
@ -47,15 +39,13 @@ public class WxApi {
@PostMapping("/gzhLogin")
@Anonymous
public AjaxResult gzhLogin(@RequestBody GzhLoginBody gzhLoginBody)
{
public AjaxResult gzhLogin(@RequestBody GzhLoginBody gzhLoginBody) {
JSONObject userInfo = weChatUtils.getUserInfo(gzhLoginBody.getAccess_token(), gzhLoginBody.getOpenid());
//如果解析成功,获取token
String token = memberUserService.gzhLogin(gzhLoginBody.getOpenid(),userInfo);
String token = memberUserService.gzhLogin(gzhLoginBody.getOpenid(), userInfo);
AjaxResult ajax = AjaxResult.success();
ajax.put(Constants.TOKEN, token);
return ajax;
}
}

View File

@ -0,0 +1,58 @@
package com.ruoyi.api;
import com.ruoyi.api.domain.WeChatMessage;
import com.ruoyi.api.service.IWeChatMessageService;
import com.ruoyi.busi.utils.WeChatUtils;
import com.ruoyi.common.annotation.Anonymous;
import com.wechat.pay.java.core.http.HttpMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@RestController
@RequestMapping("/wxMsgApi")
public class WxMsgApi {
@Autowired
private WeChatUtils weChatUtils;
@Resource
private IWeChatMessageService weChatMessageService;
/**
* 校验签名
*
* @param message {@link WeChatMessage}
* @param request {@link HttpServletRequest}
* @return java.lang.Object
* @author PQZ
* @date 12:17 2025/4/25
**/
@RequestMapping("/message")
@Anonymous
public void register(WeChatMessage message, HttpServletRequest request, HttpServletResponse response) throws IOException {
String method = request.getMethod();
if (HttpMethod.GET.name().equalsIgnoreCase(method)) {
String echostr = weChatMessageService.checkSignature(message);
response.getOutputStream().write(echostr.getBytes());
} else if (HttpMethod.POST.name().equalsIgnoreCase(method)) {
// 进入POST聊天处理
// 将请求响应的编码均设置为UTF-8防止中文乱码
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 接收消息并返回消息
String result = weChatMessageService.acceptMessage(request, response);
// 响应消息
PrintWriter out = response.getWriter();
out.print(result);
out.close();
}
}
}

View File

@ -0,0 +1,37 @@
package com.ruoyi.api.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class WeChatMessage {
/**
* 微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数nonce参数
*/
private String signature;
/**
* 时间戳
*/
private String timestamp;
/**
* 随机数
*/
private String nonce;
/**
* 随机字符串
*/
private String echostr;
}

View File

@ -0,0 +1,37 @@
package com.ruoyi.api.service;
import com.ruoyi.api.domain.WeChatMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 微信消息自动回复Service
*
* @author vinjor-m
* @date 2025-03-17
*/
public interface IWeChatMessageService {
/**
* 校验签名
*
* @param message {@link WeChatMessage}
* @return java.lang.String
* @author PQZ
* @date 12:08 2025/4/25
**/
String checkSignature(WeChatMessage message);
/**
* 发送消息返回
*
* @param request {@link HttpServletRequest}
* @param response {@link HttpServletResponse}
* @return java.lang.String
* @author PQZ
* @date 12:52 2025/4/25
**/
String acceptMessage(HttpServletRequest request, HttpServletResponse response);
}

View File

@ -0,0 +1,194 @@
package com.ruoyi.api.service.impl;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONArray;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.google.gson.JsonArray;
import com.ruoyi.api.domain.WeChatMessage;
import com.ruoyi.api.service.IWeChatMessageService;
import com.ruoyi.api.util.HttpUtils;
import com.ruoyi.api.util.MessageUtil;
import com.ruoyi.base.domain.BaseConfig;
import com.ruoyi.base.service.IBaseConfigService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 会员卡Service业务层处理
*
* @author vinjor-m
* @date 2025-03-17
*/
@Service
public class WeChatMessageServiceImpl implements IWeChatMessageService {
@Value("${wx-app.appId}")
private String appId;
@Value("${wx-app.appSecret}")
private String appSecret;
@Value("${wx-app.token}")
private String token;
@Resource
private IBaseConfigService configService;
/**发送消息url*/
private static String SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
/**获取accessTokenUrl*/
private String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* 校验签名
*
* @param message {@link WeChatMessage}
* @return java.lang.String
* @author PQZ
* @date 12:08 2025/4/25
**/
@Override
public String checkSignature(WeChatMessage message) {
String signature = message.getSignature();
String timestamp = message.getTimestamp();
String nonce = message.getNonce();
//必须与请求参数中的token一致
List<String> list = new ArrayList<>();
list.add(token);
list.add(timestamp);
list.add(nonce);
// 排序跟使用hutool工具类 进行字典排序跟加密
String str = list.stream().sorted().collect(Collectors.joining());
String tmpStr = SecureUtil.sha1(str);
if (signature.equals(tmpStr)) {
return message.getEchostr();
} else {
return "shibai";
}
}
/**
* 发送消息返回
*
* @param request {@link HttpServletRequest}
* @param response {@link HttpServletResponse}
* @return java.lang.String
* @author PQZ
* @date 12:52 2025/4/25
**/
@Override
public String acceptMessage(HttpServletRequest request, HttpServletResponse response) {
//返回值
String result = "success";
try {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
Map<String, String> requestMap = MessageUtil.parseXml(request);
// 发送者的openid
String fromUserName = requestMap.get("FromUserName");
// 小程序的原始ID
String toUserName = requestMap.get("ToUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
// 文本消息内容
String content = requestMap.get("Content");
// 事件类型
String event = requestMap.get("Event");
StringBuilder contentMessage = new StringBuilder();
String contentStr = "嗨,欢迎加入通告快捷,您想咨询那个问题,请点击选择";
String jqHtml = "1111";
String xsHtml = "1111";
String tgzHtml = "https://www.ddtg.site/#/pages/mine/member/member-card?userType=01";
String bzHtml = "https://www.ddtg.site/#/pages/mine/member/member-card?userType=02";
String jqStr = "<a href=\""+jqHtml+"\">1、进群</a>";
String xsStr = "<a href=\""+xsHtml+"\">2、新手教程</a>";
String tgzStr = "<a href=\""+tgzHtml+"\">3、通告主卡</a>";
String bzStr = "<a href=\""+bzHtml+"\">4、博主VIP</a>";
contentMessage.append(contentStr).append("\n")
.append(jqStr).append("\n")
.append(xsStr).append("\n")
.append(tgzStr).append("\n")
.append(bzStr).append("\n");
if (msgType.equals("event")) {
sendCustomerTextMessage(fromUserName, contentMessage.toString(), getAccessToken());
} else if (msgType.equals("text")) {
if (content.equals("0")) {
return switchCustomerService(fromUserName, toUserName);
} else {
sendCustomerTextMessage(fromUserName, contentMessage.toString(), getAccessToken());
}
} else {
sendCustomerTextMessage(fromUserName, contentMessage.toString(), getAccessToken());
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private void sendCustomerTextMessage(String openid, String text, String accessToken) throws IOException {
HashMap<String, Object> map_content = new HashMap<>();
map_content.put("content", text);
HashMap<String, Object> map = new HashMap<>();
map.put("touser", openid);
map.put("msgtype", "text");
map.put("text", map_content);
String content = JSON.toJSONString(map);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity httpEntity = new HttpEntity(map, headers);
HttpUtils.sendPostRequest(SEND_URL + "?access_token=" + accessToken, HttpMethod.POST, httpEntity);
}
/**
* 人工服务
*
* @param fromUserName
* @param toUserName
* @return
*/
public String switchCustomerService(String fromUserName, String toUserName) {
return "<xml>\n" +
" <ToUserName><![CDATA[" + fromUserName + "]]></ToUserName>\n" +
" <FromUserName><![CDATA[" + toUserName + "]]></FromUserName>\n" +
" <CreateTime>" + System.currentTimeMillis() / 1000 + "</CreateTime>\n" +
" <MsgType><![CDATA[transfer_customer_service]]></MsgType>\n" +
"</xml>";
}
/**
* 获取accessToken
* @author PQZ
* @date 16:12 2025/4/25
* @return java.lang.String
**/
public String getAccessToken() {
//拼接url传入appId和secret
url = url.replace("APPID", appId).replace("APPSECRET", appSecret);
String str = HttpUtils.sendGetRequest(url);
JSONObject jsonObject = JSONObject.parseObject(str);
return jsonObject.getString("access_token");
}
}

View File

@ -0,0 +1,36 @@
package com.ruoyi.api.util;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpMethod;
import java.util.Map;
/**
* 客服功能 - 消息发送请求工具类
* Created by Lance on 2020/10/10 17:52
*/
public class HttpUtils {
/**
* Get 发送的请求
* @param url
* @return
*/
public static String sendGetRequest(String url) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(url, String.class);
}
public static String sendPostRequest(String url, HttpMethod method, HttpEntity<Map<String, Object>> httpEntity) {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(url, method, httpEntity, String.class);
return response.getBody();
}
}

View File

@ -0,0 +1,49 @@
package com.ruoyi.api.util;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MessageUtil {
// 请求消息类型文本
public static final String REQ_MESSAGE_TYPE_TEXT = "text";
/**
* 解析微信发来的请求XML
*
* @param request
* @return Map<String, String>
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
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());
}
// 释放资源
inputStream.close();
inputStream = null;
return map;
}
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.base.service;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -8,11 +9,20 @@ import com.ruoyi.base.domain.BaseConfig;
/**
* 基础配置-账户配置小程序配置的内容Service接口
*
*
* @author vinjor-m
* @date 2025-03-17
*/
public interface IBaseConfigService extends IService<BaseConfig>
{
public interface IBaseConfigService extends IService<BaseConfig> {
IPage<BaseConfig> queryListPage(BaseConfig pageReqVO, Page<BaseConfig> page);
/**
* 通过code获取配置
*
* @param code code
* @return java.util.List<com.ruoyi.base.domain.BaseConfig>
* @author PQZ
* @date 16:17 2025/4/25
**/
List<BaseConfig> queryConfigByCode(String code);
}

View File

@ -1,6 +1,9 @@
package com.ruoyi.base.service.impl;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.DlBaseEntity;
import com.ruoyi.common.utils.DateUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -27,4 +30,20 @@ public class BaseConfigServiceImpl extends ServiceImpl<BaseConfigMapper,BaseConf
public IPage<BaseConfig> queryListPage(BaseConfig pageReqVO, Page<BaseConfig> page) {
return baseConfigMapper.queryListPage(pageReqVO, page);
}
/**
* 通过code获取配置
*
* @param code code
* @return java.util.List<com.ruoyi.base.domain.BaseConfig>
* @author PQZ
* @date 16:17 2025/4/25
**/
@Override
public List<BaseConfig> queryConfigByCode(String code) {
LambdaQueryWrapper<BaseConfig> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(DlBaseEntity::getDelFlag,0)
.eq(BaseConfig::getCode,code);
return list(lambdaQueryWrapper);
}
}

View File

@ -147,6 +147,9 @@ xss:
wx-app:
appId: wxd96fda6510adb6d3
appSecret: 2d3bf7172d09966bd98e1611117c2cb0
token: tgxcxtoken
aesKey: afBZchk5wSFaldDkZB9DZ9Ib6JUV4NwMARaRThsRFqQ
msgDataFormat: XML
wxpay:
#微信公众号appid
appId: wx7d10b0fa4886a583