From f036e108931d20588013c2092df4031ad3887eee Mon Sep 17 00:00:00 2001 From: gjh <1421wake> Date: Fri, 20 Jun 2025 14:19:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E8=AE=A2=E9=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/platform/config/AppConfig.java | 13 + .../platform/controller/WeChatController.java | 270 ++++++++++++++++++ .../domain/app/WeComSubscribeDataItem.java | 18 ++ .../app/WeComSubscribeMessageRequest.java | 43 +++ 4 files changed, 344 insertions(+) create mode 100644 ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/config/AppConfig.java create mode 100644 ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/controller/WeChatController.java create mode 100644 ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/domain/app/WeComSubscribeDataItem.java create mode 100644 ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/domain/app/WeComSubscribeMessageRequest.java diff --git a/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/config/AppConfig.java b/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/config/AppConfig.java new file mode 100644 index 0000000..0ee6864 --- /dev/null +++ b/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/config/AppConfig.java @@ -0,0 +1,13 @@ +package org.dromara.platform.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AppConfig { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/controller/WeChatController.java b/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/controller/WeChatController.java new file mode 100644 index 0000000..eece0db --- /dev/null +++ b/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/controller/WeChatController.java @@ -0,0 +1,270 @@ +package org.dromara.platform.controller; + +import com.alibaba.excel.EasyExcel; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.platform.domain.PerformanceManagement; +import org.dromara.platform.domain.ProjectManager; +import org.dromara.platform.domain.app.WeComSubscribeDataItem; +import org.dromara.platform.domain.app.WeComSubscribeMessageRequest; +import org.dromara.platform.domain.bo.PartyInfoBo; +import org.dromara.platform.domain.vo.PartyInfoVo; +import org.dromara.platform.domain.vo.WorkOrderInfoVo; +import org.dromara.platform.listener.PerformanceManagementListener; +import org.dromara.platform.listener.ProjectManagerListener; +import org.dromara.platform.service.IPerformanceManagementService; +import org.dromara.platform.service.IWorkOrderInfoService; +import org.dromara.platform.service.ProjectManagerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; + +/** + * @Author gejunhao + * @Date 2025/1/10 14:11 + * @Description: 测试接口 + */ +@RequiredArgsConstructor +@RestController +@Slf4j +@RequestMapping("/wechat") +public class WeChatController { + // 替换为你的 AppID 和 AppSecret + private static final String APP_ID = "wxe2eba8943d47678e"; + private static final String APP_SECRET = "7af0a56aaa7780ad041ee4cda2359353"; + private static final String APP_OPENID = "ohCQF7nRbdBVnk4GUD_TgTRv_IYs"; + private static final String ACCESS_TOKEN = "93_Rdp-c-VBBN-2qNAxBQ1pmx3gvuxjoMk2A_BmczJinRcJ9G3IwJ-okucNWz30OY7bb_8tk44XjQ5pdaRf8-lvrb8blIUjuFDklcyLtjX-1g4Ik9Q1cUuzhP-O9DwLYPhAEASFW"; + private static final String TEMPLATE_ID = "z9G-4dk8JHget0Ftm8ysEa6pT2mztklfDTYF9sYBDzM"; + private static final String SUBSCRIBE_SEND_PREFIX = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="; + + + @GetMapping("/hello") + public void test(HttpServletResponse response) throws IOException { + log.warn("hello word!"); + response.setContentType("text/html;charset=utf8"); + response.getWriter().println("调用接口成功!"); + } + + @Autowired + private ObjectMapper objectMapper; + @Resource + private RestTemplate restTemplate; + + @GetMapping("/getOpenId") + public R< Map> getOpenId(String jsCode) throws JsonProcessingException { + Map result = getOpenIdForApp(jsCode); + return R.ok("根据jsCode获取对应的openId", result); + } + + + @GetMapping("/getAccessToken") + public R> getAccessToken() throws JsonProcessingException { + Map result = getAccessTokenForApp(); + // 5. 返回给前端 + return R.ok("获取对应的access_token",result); + } + + + @GetMapping("/sendSubscribeMsg") + public ResponseEntity sendSubscribeMessage(String openid, String accessToken) { + // 使用默认值或传入参数 + openid = openid == null ? APP_OPENID : openid; + accessToken = accessToken == null ? ACCESS_TOKEN : accessToken; + + String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken; + + // 构建请求体 + WeComSubscribeMessageRequest request = new WeComSubscribeMessageRequest(); + request.setTouser(openid); + request.setTemplate_id(TEMPLATE_ID); + request.setPage("pages/index/index"); + + Map dataMap = new HashMap<>(); + dataMap.put("character_string5", new WeComSubscribeDataItem("工单编号:123456")); + dataMap.put("thing2", new WeComSubscribeDataItem("空调内部故障")); + dataMap.put("time13", new WeComSubscribeDataItem("2025年6月20日")); + dataMap.put("thing10", new WeComSubscribeDataItem("宁波国研机房")); + dataMap.put("thing8", new WeComSubscribeDataItem("2025年6月21日")); + + request.setData(dataMap); + + // 打印格式化的 JSON 请求体 + try { + String formattedJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(request); + String compactJson = objectMapper.writeValueAsString(request); + System.out.println("🎯 请求地址: " + url); + System.out.println("📥 请求体 JSON(紧凑格式):\n" + compactJson); + System.out.println("📘 请求体 JSON(格式化):\n" + formattedJson); + } catch (JsonProcessingException e) { + System.err.println("⚠ JSON 序列化失败:" + e.getMessage()); + } + + // 设置请求头并清除可能导致 412 的字段 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + // 显式移除 If-Match / If-None-Match(保险起见) + headers.remove("If-Match"); + headers.remove("If-None-Match"); + + HttpEntity entity = new HttpEntity<>(request, headers); + + // 发送请求并捕获异常 + try { + ResponseEntity response = restTemplate.postForEntity(url, entity, String.class); + + // 打印响应结果(调试用) + System.out.println("📤 响应状态码: " + response.getStatusCode()); + System.out.println("📤 响应内容: " + response.getBody()); + + return response; + } catch (HttpClientErrorException e) { + // 或者返回错误信息给前端 + System.err.println("❌ HTTP 错误状态码: " + e.getStatusCode()); + System.err.println("❌ 响应头: " + e.getResponseHeaders()); + System.err.println("❌ 响应体: " + e.getResponseBodyAsString()); + throw e; + } + + } + @GetMapping("/sendSubscribeMsgTest") + public ResponseEntity sendSubscribeMsgTest( + @RequestParam(required = false) String openid, + @RequestParam(required = false) String accessToken) { + + // 使用默认值或传入参数 + openid = openid == null ? APP_OPENID : openid; + accessToken = accessToken == null ? ACCESS_TOKEN : accessToken; + + String url = SUBSCRIBE_SEND_PREFIX + accessToken; + + // 构建请求体对象 + WeComSubscribeMessageRequest request = new WeComSubscribeMessageRequest(); + request.setTouser(openid); + request.setTemplate_id(TEMPLATE_ID); + request.setPage("pages/index/index"); + + Map dataMap = new HashMap<>(); + dataMap.put("character_string5", new WeComSubscribeDataItem("工单编号:123456")); + dataMap.put("thing2", new WeComSubscribeDataItem("空调内部故障")); + dataMap.put("time13", new WeComSubscribeDataItem("2025年6月20日")); + dataMap.put("thing10", new WeComSubscribeDataItem("宁波国研机房")); + dataMap.put("thing8", new WeComSubscribeDataItem("2025年6月21日")); + + request.setData(dataMap); + // 序列化 JSON 并记录日志 + String jsonBody; + try { + jsonBody = objectMapper.writeValueAsString(request); + } catch (JsonProcessingException e) { + String errorMsg = "❌ JSON 构建失败:" + e.getMessage(); + log.error(errorMsg, e); // 统一日志记录 + return ResponseEntity.status(500).body(errorMsg); + } + + // 打印调试信息(格式化 JSON) + try { + String formattedJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(request); + log.info("🎯 请求地址: {}", url); + log.info("📘 请求体 JSON(格式化):\n{}", formattedJson); + } catch (JsonProcessingException e) { + log.warn("⚠️ 格式化 JSON 失败,跳过打印"); + } + + // 创建 HttpClient 实例并发送 POST 请求 + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest httpRequest = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) + .build(); + + try { + HttpResponse response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); + + // 记录响应结果 + log.info("📤 响应状态码: {}", response.statusCode()); + log.info("📤 响应内容: {}", response.body()); + + // 返回给前端 + return ResponseEntity.status(response.statusCode()).body(response.body()); + + } catch (IOException | InterruptedException e) { + String errorMsg = "❌ 请求发送失败:" + e.getMessage(); + log.error(errorMsg, e); + return ResponseEntity.status(500).body(errorMsg); + } + } + + private Map getAccessTokenForApp() throws JsonProcessingException { + // 1. 构造请求 URL + String url = "https://api.weixin.qq.com/cgi-bin/token?" + + "grant_type=client_credential&" + + "appid={appid}&" + + "secret={secret}"; + + // 2. 构造参数 Map + Map uriVariables = new HashMap<>(); + uriVariables.put("appid", APP_ID); + uriVariables.put("secret", APP_SECRET); + + // 3. 发起请求 + String response = restTemplate.getForObject(url, String.class, uriVariables); + + // 4. 解析 JSON 获取 access_token + Map result = objectMapper.readValue(response, new TypeReference>() {}); + String accessToken = result.get("access_token"); + if (accessToken == null || accessToken.isEmpty()) { + throw new RuntimeException("获取 access_token 失败: " + response); + } + log.info("获取access_token:{}",accessToken); + return result; + } + + + private Map getOpenIdForApp(String jsCode) throws JsonProcessingException { + String url = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={jscode}&grant_type=authorization_code"; + + Map uriVariables = new HashMap<>(); + uriVariables.put("appid", APP_ID); + uriVariables.put("secret", APP_SECRET); + uriVariables.put("jscode", jsCode); + + // 发送 GET 请求并接收返回结果(微信返回的是 JSON) + String response = restTemplate.getForObject(url, String.class, uriVariables); + + // 这里你可以解析 response 获取 openid 等信息 + log.info("微信返回结果:{}", response); + Map result = objectMapper.readValue(response, new TypeReference>() {}); + return result; + } +} diff --git a/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/domain/app/WeComSubscribeDataItem.java b/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/domain/app/WeComSubscribeDataItem.java new file mode 100644 index 0000000..699c6ca --- /dev/null +++ b/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/domain/app/WeComSubscribeDataItem.java @@ -0,0 +1,18 @@ +package org.dromara.platform.domain.app; + +public class WeComSubscribeDataItem { + private String value; + + public WeComSubscribeDataItem(String value) { + this.value = value; + } + + // Getter and Setter + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/domain/app/WeComSubscribeMessageRequest.java b/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/domain/app/WeComSubscribeMessageRequest.java new file mode 100644 index 0000000..d23b323 --- /dev/null +++ b/ruoyi-modules/guoyan-platform/src/main/java/org/dromara/platform/domain/app/WeComSubscribeMessageRequest.java @@ -0,0 +1,43 @@ +package org.dromara.platform.domain.app; + +import java.util.Map; + +public class WeComSubscribeMessageRequest { + private String touser; + private String template_id; + private String page; + private Map data; + + // Getter and Setter + public String getTouser() { + return touser; + } + + public void setTouser(String touser) { + this.touser = touser; + } + + public String getTemplate_id() { + return template_id; + } + + public void setTemplate_id(String template_id) { + this.template_id = template_id; + } + + public String getPage() { + return page; + } + + public void setPage(String page) { + this.page = page; + } + + public Map getData() { + return data; + } + + public void setData(Map data) { + this.data = data; + } +}