diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index fc1edce..6bdd852 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -70,6 +70,11 @@ com.ruoyi ruoyi-generator + + com.github.binarywang + weixin-java-common + 4.6.0 + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/WxApi.java b/ruoyi-admin/src/main/java/com/ruoyi/api/WxApi.java index 4e476f4..8e47a00 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/api/WxApi.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/WxApi.java @@ -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 getCodeUrl(String userType,String url) { + public Map getCodeUrl(String userType, String url) { Map 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; - } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/WxMsgApi.java b/ruoyi-admin/src/main/java/com/ruoyi/api/WxMsgApi.java new file mode 100644 index 0000000..01ae65c --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/WxMsgApi.java @@ -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(); + } + } + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/WeChatMessage.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/WeChatMessage.java new file mode 100644 index 0000000..5d03c97 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/WeChatMessage.java @@ -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; + + +} + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/service/IWeChatMessageService.java b/ruoyi-admin/src/main/java/com/ruoyi/api/service/IWeChatMessageService.java new file mode 100644 index 0000000..3b2b3c8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/service/IWeChatMessageService.java @@ -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); + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/WeChatMessageServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/WeChatMessageServiceImpl.java new file mode 100644 index 0000000..0b26f95 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/WeChatMessageServiceImpl.java @@ -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 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 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 = "1、进群"; + String xsStr = "2、新手教程"; + String tgzStr = "3、通告主卡"; + String bzStr = "4、博主VIP"; + 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 map_content = new HashMap<>(); + map_content.put("content", text); + HashMap 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 "\n" + + " \n" + + " \n" + + " " + System.currentTimeMillis() / 1000 + "\n" + + " \n" + + ""; + } + + /** + * 获取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"); + } + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/util/HttpUtils.java b/ruoyi-admin/src/main/java/com/ruoyi/api/util/HttpUtils.java new file mode 100644 index 0000000..32334dd --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/util/HttpUtils.java @@ -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> httpEntity) { + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity response = restTemplate.exchange(url, method, httpEntity, String.class); + return response.getBody(); + } +} + + + + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/util/MessageUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/api/util/MessageUtil.java new file mode 100644 index 0000000..ec53ff6 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/util/MessageUtil.java @@ -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 + * @throws Exception + */ + @SuppressWarnings("unchecked") + public static Map parseXml(HttpServletRequest request) throws Exception { + // 将解析结果存储在HashMap中 + Map map = new HashMap(); + + // 从request中取得输入流 + InputStream inputStream = request.getInputStream(); + // 读取输入流 + SAXReader reader = new SAXReader(); + Document document = reader.read(inputStream); + // 得到xml根元素 + Element root = document.getRootElement(); + // 得到根元素的所有子节点 + List elementList = root.elements(); + + // 遍历所有子节点 + for (Element e : elementList){ + map.put(e.getName(), e.getText()); + } + + // 释放资源 + inputStream.close(); + inputStream = null; + return map; + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/base/service/IBaseConfigService.java b/ruoyi-admin/src/main/java/com/ruoyi/base/service/IBaseConfigService.java index acd7fb3..b056589 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/base/service/IBaseConfigService.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/base/service/IBaseConfigService.java @@ -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 -{ +public interface IBaseConfigService extends IService { IPage queryListPage(BaseConfig pageReqVO, Page page); + + /** + * 通过code获取配置 + * + * @param code code + * @return java.util.List + * @author PQZ + * @date 16:17 2025/4/25 + **/ + List queryConfigByCode(String code); } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/base/service/impl/BaseConfigServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/base/service/impl/BaseConfigServiceImpl.java index a0e98a8..03a8eed 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/base/service/impl/BaseConfigServiceImpl.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/base/service/impl/BaseConfigServiceImpl.java @@ -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 queryListPage(BaseConfig pageReqVO, Page page) { return baseConfigMapper.queryListPage(pageReqVO, page); } + + /** + * 通过code获取配置 + * + * @param code code + * @return java.util.List + * @author PQZ + * @date 16:17 2025/4/25 + **/ + @Override + public List queryConfigByCode(String code) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(DlBaseEntity::getDelFlag,0) + .eq(BaseConfig::getCode,code); + return list(lambdaQueryWrapper); + } } diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 6d45895..f33377a 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -147,6 +147,9 @@ xss: wx-app: appId: wxd96fda6510adb6d3 appSecret: 2d3bf7172d09966bd98e1611117c2cb0 + token: tgxcxtoken + aesKey: afBZchk5wSFaldDkZB9DZ9Ib6JUV4NwMARaRThsRFqQ + msgDataFormat: XML wxpay: #微信公众号appid appId: wx7d10b0fa4886a583