diff --git a/pom.xml b/pom.xml index e7f81ec..fd6fc3b 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,10 @@ org.apache.httpcomponents httpasyncclient + + org.apache.httpcomponents + httpclient + org.apache.poi diff --git a/src/main/java/com/saye/hgddmz/controller/GetDateController.java b/src/main/java/com/saye/hgddmz/controller/GetDateController.java index 8514b01..44dd5a0 100644 --- a/src/main/java/com/saye/hgddmz/controller/GetDateController.java +++ b/src/main/java/com/saye/hgddmz/controller/GetDateController.java @@ -6,18 +6,28 @@ import cn.hutool.http.HttpUtil; import com.saye.hgddmz.commons.date.DateDUtil; import com.saye.hgddmz.commons.string.StringDUtil; import com.saye.hgddmz.entity.BankbillHistory; +import com.saye.hgddmz.entity.UserOrder; import com.saye.hgddmz.util.DownloadFtpUtil; +import com.saye.hgddmz.util.HttpClientUtil; import lombok.extern.slf4j.Slf4j; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.Cell; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.*; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import java.io.File; import java.io.FileInputStream; +import java.io.InputStream; +import java.text.DecimalFormat; import java.util.*; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; /** @@ -36,6 +46,10 @@ public class GetDateController { private static String appid = "wx45acd2b4907cb8f4"; private static String secret = "895b90585c4698485c07e113711eac85"; private static String key = "Nxwj20250903Jojubanking12091209x"; + + // 微信消息推送相关常量 + private static String WX_APP_ID = "wx45acd2b4907cb8f4"; + private static String WX_SECRET = "895b90585c4698485c07e113711eac85"; /** * 安全获取单元格字符串值 @@ -1111,4 +1125,429 @@ public class GetDateController { // } // System.out.println(); // } + + /** + * 发送对账结果微信消息推送 + * @param tradeDate 对账时间 + * @param operationResult 操作结果 + * @param reconcileResult 对账结果 + * @param operatorName 操作人 + * @param openid 用户openid + * @return 推送结果 + */ + public int SendNotifyYJJ(String tradeDate, String operationResult, String reconcileResult, String operatorName, String openid) { + try { + //第一步 获取token + String accessUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&" + + "&appid=" + WX_APP_ID + "&secret=" + WX_SECRET; + String accessTokenStr = HttpClientUtil.doGet(accessUrl, null); + log.info("step 1 get accesstoken:" + accessTokenStr); + + if (accessTokenStr == null || accessTokenStr.isEmpty()) { + log.error("获取微信access_token失败,响应为空"); + return -1; + } + + JSONObject jsonObject = JSON.parseObject(accessTokenStr); + if (jsonObject == null) { + log.error("解析微信access_token响应失败,无法解析JSON"); + return -1; + } + + Object accessTokenObj = jsonObject.get("access_token"); + if (accessTokenObj == null) { + log.error("微信access_token响应中没有access_token字段,响应内容: {}", accessTokenStr); + return -1; + } + + String accessToken = accessTokenObj.toString(); + + String sendUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken; + JSONObject sendPara = new JSONObject(); + JSONObject dataPara = new JSONObject(); + + // 对账时间 + JSONObject time2 = new JSONObject(); + time2.put("value", tradeDate); + time2.put("color", "#173177"); + + // 操作结果 + JSONObject const3 = new JSONObject(); + const3.put("value", operationResult); + const3.put("color", "#173177"); + + // 对账结果 + JSONObject const4 = new JSONObject(); + const4.put("value", reconcileResult); + const4.put("color", "#173177"); + + // 操作人和时间 + JSONObject character_string5 = new JSONObject(); + character_string5.put("value", operatorName + " " + DateDUtil.DateToStr(DateDUtil.yyyy_MM_dd_HH_mm_ss, new Date())); + character_string5.put("color", "#173177"); + + dataPara.put("time2", time2); + dataPara.put("const3", const3); + dataPara.put("const4", const4); + dataPara.put("character_string5", character_string5); + + sendPara.put("touser", openid); + sendPara.put("template_id", "sbzMHunU1zmB_Hg4LRAs7BSmYPw6CtxFuBHMXllxZB0"); + sendPara.put("url", "https://nxwj.btlsoln.com/nxwj-gzh/#/reconciliation"); + sendPara.put("data", dataPara); + String sendRes = HttpClientUtil.doPost(sendUrl, sendPara); + log.info("step 2 send notify:" + sendRes); + + if (sendRes == null || sendRes.isEmpty()) { + log.error("发送微信消息失败,响应为空"); + return -1; + } + + JSONObject jsonSend = JSON.parseObject(sendRes); + if (jsonSend != null && jsonSend.containsKey("errcode")) { + Integer errcode = jsonSend.getInteger("errcode"); + String errmsg = jsonSend.getString("errmsg"); + + if (errcode != null && errcode == 0) { + log.info("微信消息发送成功"); + return 0; + } else { + // 根据不同错误码提供具体的错误信息 + switch (errcode) { + case 43004: + log.error("微信消息发送失败:用户未关注公众号,无法接收模板消息。errcode: {}, errmsg: {}", errcode, errmsg); + return -2; // 特殊返回码表示用户未关注 + case 40001: + log.error("微信消息发送失败:access_token无效。errcode: {}, errmsg: {}", errcode, errmsg); + return -3; + case 40003: + log.error("微信消息发送失败:openid无效。errcode: {}, errmsg: {}", errcode, errmsg); + return -4; + case 41030: + log.error("微信消息发送失败:页面不存在或者小程序没有关联公众号。errcode: {}, errmsg: {}", errcode, errmsg); + return -5; + default: + log.error("微信消息发送失败,errcode: {}, errmsg: {}", errcode, errmsg); + return -1; + } + } + } + + return 0; + } catch (Exception e) { + log.error("发送微信消息异常", e); + return -1; + } + } + + /** + * 测试对账结果微信消息推送接口 + * @param map 包含对账信息和openid的参数 + * @return 推送结果 + */ + @PostMapping("/sendNotifyYJJ") + @ResponseBody + public HashMap sendNotifyYJJ(@RequestBody HashMap map) { + HashMap responseMap = new HashMap<>(); + String errCode = "0"; + String errMsg = ""; + + try { + // 从请求参数中获取数据 + String tradeDate = StringDUtil.changeNullToEmpty(map.get("tradeDate")); + String operationResult = StringDUtil.changeNullToEmpty(map.get("operationResult")); + String reconcileResult = StringDUtil.changeNullToEmpty(map.get("reconcileResult")); + String operatorName = StringDUtil.changeNullToEmpty(map.get("operatorName")); + String openid = StringDUtil.changeNullToEmpty(map.get("openid")); + + // 参数校验 + if (tradeDate.isEmpty()) { + errCode = "1"; + errMsg = "对账时间不能为空"; + responseMap.put("errCode", errCode); + responseMap.put("errMsg", errMsg); + return responseMap; + } + + if (openid.isEmpty()) { + errCode = "1"; + errMsg = "openid不能为空"; + responseMap.put("errCode", errCode); + responseMap.put("errMsg", errMsg); + return responseMap; + } + + // 设置默认值 + if (operationResult.isEmpty()) { + operationResult = "操作完成!"; + } + if (reconcileResult.isEmpty()) { + reconcileResult = "对账完成!"; + } + if (operatorName.isEmpty()) { + operatorName = "系统"; + } + + // 调用推送方法 + int result = SendNotifyYJJ(tradeDate, operationResult, reconcileResult, operatorName, openid); + + if (result == 0) { + errMsg = "对账结果消息推送成功"; + } else { + errCode = "1"; + // 根据返回码提供具体的错误信息 + switch (result) { + case -2: + errMsg = "消息推送失败:用户未关注公众号,无法接收模板消息"; + break; + case -3: + errMsg = "消息推送失败:微信access_token无效"; + break; + case -4: + errMsg = "消息推送失败:用户openid无效"; + break; + case -5: + errMsg = "消息推送失败:页面不存在或小程序未关联公众号"; + break; + default: + errMsg = "对账结果消息推送失败"; + break; + } + } + + } catch (Exception e) { + log.error("微信消息推送异常", e); + errCode = "999"; + errMsg = "微信消息推送异常: " + e.getMessage(); + } + + responseMap.put("errCode", errCode); + responseMap.put("errMsg", errMsg); + return responseMap; + } + + /** + * 检查用户是否关注公众号 + * @param openid 用户openid + * @return true-已关注,false-未关注 + */ + public boolean checkUserSubscribe(String openid) { + try { + // 获取access_token + String accessUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&" + + "&appid=" + WX_APP_ID + "&secret=" + WX_SECRET; + String accessTokenStr = HttpClientUtil.doGet(accessUrl, null); + + if (accessTokenStr == null || accessTokenStr.isEmpty()) { + log.error("获取access_token失败"); + return false; + } + + JSONObject tokenJson = JSON.parseObject(accessTokenStr); + Object accessTokenObj = tokenJson.get("access_token"); + if (accessTokenObj == null) { + log.error("access_token响应中没有access_token字段"); + return false; + } + + String accessToken = accessTokenObj.toString(); + + // 获取用户信息 + String userInfoUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openid; + String userInfoResponse = HttpClientUtil.doGet(userInfoUrl, null); + log.info("用户信息响应: {}", userInfoResponse); + + if (userInfoResponse != null && !userInfoResponse.isEmpty()) { + JSONObject userInfo = JSON.parseObject(userInfoResponse); + if (userInfo.containsKey("subscribe")) { + Integer subscribe = userInfo.getInteger("subscribe"); + return subscribe != null && subscribe == 1; + } + } + + } catch (Exception e) { + log.error("检查用户关注状态异常", e); + } + return false; + } + + /** + * 根据验证码和手机号获取openid + * @param yz 验证码 + * @param phone 手机号 + * @return openid,如果获取失败返回null + */ + public String getUserOpenIdByPhone(String yz, String phone) { + try { + String url = "http://172.16.21.21:8443/nxgzh/userInfo/getUserOpenIdByPhone?yz=" + yz + "&phone=" + phone; + String response = HttpClientUtil.doPost(url, null); + log.info("获取openid请求URL: {}", url); + log.info("获取openid响应: {}", response); + + if (response != null && !response.isEmpty()) { + JSONObject jsonResponse = JSON.parseObject(response); + log.info("解析后的JSON对象: {}", jsonResponse); + + // 返回格式为 {"code": 200, "message": "执行成功", "refreshToken": null, "data": "o4MF86KDH-v1Rh5ryVj4TAeLs2JY"} + // 检查code是否为200 + Integer code = jsonResponse.getInteger("code"); + log.info("响应code: {}", code); + + if (code != null && code == 200) { + // data字段直接是openid字符串 + if (jsonResponse.containsKey("data")) { + String openid = jsonResponse.getString("data"); + log.info("获取到的openid: {}", openid); + + if (openid != null && !openid.isEmpty() && !"null".equals(openid)) { + log.info("成功获取openid,手机号: {}, openid: {}", phone, openid); + return openid; + } else { + log.warn("openid为空或null,手机号: {}, data值: {}", phone, openid); + } + } else { + log.warn("响应中不包含data字段,手机号: {}", phone); + } + } else { + String message = jsonResponse.getString("message"); + log.warn("获取openid失败,手机号: {}, code: {}, 返回信息: {}", phone, code, message); + } + } else { + log.warn("响应为空,手机号: {}", phone); + } + } catch (Exception e) { + log.error("获取openid失败,验证码: {}, 手机号: {}, 错误: {}", yz, phone, e.getMessage(), e); + } + return null; + } + + /** + * 获取用户openid的REST接口 + * @param map 包含yz和phone参数的请求体 + * @return 包含openid的响应结果 + */ + @PostMapping("/getUserOpenId") + @ResponseBody + public HashMap getUserOpenId(@RequestBody HashMap map) { + HashMap responseMap = new HashMap<>(); + String errCode = "0"; + String errMsg = ""; + + try { + // 从请求参数中获取验证码和手机号 + String yz = StringDUtil.changeNullToEmpty(map.get("yz")); + String phone = StringDUtil.changeNullToEmpty(map.get("phone")); + log.info("请求获取openid,yz: {}, phone: {}", yz, phone); + + if (yz.isEmpty()) { + errCode = "1"; + errMsg = "验证码不能为空"; + responseMap.put("errCode", errCode); + responseMap.put("errMsg", errMsg); + return responseMap; + } + + if (phone.isEmpty()) { + errCode = "1"; + errMsg = "手机号不能为空"; + responseMap.put("errCode", errCode); + responseMap.put("errMsg", errMsg); + return responseMap; + } + + // 调用获取openid的方法 + String openid = getUserOpenIdByPhone(yz, phone); + + if (openid != null && !openid.isEmpty()) { + responseMap.put("openid", openid); + errMsg = "获取openid成功"; + } else { + errCode = "1"; + errMsg = "获取openid失败,验证码或手机号错误或网络异常"; + } + + } catch (Exception e) { + log.error("获取openid异常", e); + errCode = "999"; + errMsg = "获取openid异常: " + e.getMessage(); + } + + responseMap.put("errCode", errCode); + responseMap.put("errMsg", errMsg); + return responseMap; + } + + /** + * 下载ZIP文件接口 + * 调用远程服务下载文件并将字节流返回给客户端 + * @param fileName 文件名(通过路径参数传递) + * @return 文件字节流 + */ + @GetMapping("/download/{fileName}") + public ResponseEntity downloadFile(@PathVariable String fileName) { + CloseableHttpClient httpClient = null; + CloseableHttpResponse response = null; + + try { + log.info("开始调用远程下载接口,文件名: {}", fileName); + + // 构建远程下载URL + String remoteUrl = "http://172.16.21.21:8443/nxgzh/find/download/" + fileName; + log.info("远程下载URL: {}", remoteUrl); + + // 创建HTTP客户端 + httpClient = HttpClients.createDefault(); + HttpGet httpGet = new HttpGet(remoteUrl); + + // 执行请求 + response = httpClient.execute(httpGet); + int statusCode = response.getStatusLine().getStatusCode(); + + log.info("远程接口响应状态码: {}", statusCode); + + if (statusCode == 200) { + // 获取响应的输入流 + InputStream inputStream = response.getEntity().getContent(); + InputStreamResource resource = new InputStreamResource(inputStream); + + // 获取文件大小 + long contentLength = response.getEntity().getContentLength(); + + // 构造响应头 + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + + if (contentLength > 0) { + headers.setContentLength(contentLength); + } + + log.info("文件下载成功,文件名: {}, 大小: {} bytes", fileName, contentLength); + + return ResponseEntity.ok() + .headers(headers) + .body(resource); + + } else if (statusCode == 404) { + log.error("远程文件不存在: {}", fileName); + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .header("X-Error", "File not found: " + fileName) + .body(null); + } else { + log.error("远程接口返回错误状态码: {}", statusCode); + return ResponseEntity.status(statusCode) + .header("X-Error", "Remote server error: " + statusCode) + .body(null); + } + + } catch (Exception e) { + log.error("下载文件异常,文件名: {}, 错误: {}", fileName, e.getMessage(), e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .header("X-Error", "Download failed: " + e.getMessage()) + .body(null); + } finally { + // 注意:不要关闭response和httpClient,因为流还在被读取 + // Spring会在响应完成后自动处理资源清理 + } + } } diff --git a/src/main/java/com/saye/hgddmz/entity/UserOrder.java b/src/main/java/com/saye/hgddmz/entity/UserOrder.java new file mode 100644 index 0000000..95ddd2a --- /dev/null +++ b/src/main/java/com/saye/hgddmz/entity/UserOrder.java @@ -0,0 +1,39 @@ +package com.saye.hgddmz.entity; + +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/** + * 用户订单实体类 + * @author thuang + * @version 1.0 + * @date 2025/01/15 + */ +@Getter +@Setter +public class UserOrder implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 医院编号 + */ + private String hosnum; + + /** + * 订单金额 + */ + private Double money; + + /** + * 成功时间 + */ + private String successtime; + + /** + * 订单编号 + */ + private String code; +} + diff --git a/src/main/java/com/saye/hgddmz/util/HttpClientUtil.java b/src/main/java/com/saye/hgddmz/util/HttpClientUtil.java new file mode 100644 index 0000000..8f725dc --- /dev/null +++ b/src/main/java/com/saye/hgddmz/util/HttpClientUtil.java @@ -0,0 +1,83 @@ +package com.saye.hgddmz.util; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * HTTP客户端工具类 + * @author thuang + * @version 1.0 + * @date 2025/01/15 + */ +@Slf4j +public class HttpClientUtil { + + /** + * 发送GET请求 + * @param url 请求URL + * @param headers 请求头 + * @return 响应结果 + */ + public static String doGet(String url, java.util.Map headers) { + HttpClient httpClient = HttpClients.createDefault(); + HttpGet httpGet = new HttpGet(url); + + // 设置请求头 + if (headers != null) { + headers.forEach(httpGet::setHeader); + } + + try { + HttpResponse response = httpClient.execute(httpGet); + HttpEntity entity = response.getEntity(); + String result = EntityUtils.toString(entity, StandardCharsets.UTF_8); + log.debug("GET请求响应: {}", result); + return result; + } catch (IOException e) { + log.error("GET请求失败: {}", e.getMessage(), e); + return null; + } + } + + /** + * 发送POST请求 + * @param url 请求URL + * @param params 请求参数(JSON格式) + * @return 响应结果 + */ + public static String doPost(String url, Object params) { + HttpClient httpClient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(url); + + try { + // 设置请求头 + httpPost.setHeader("Content-Type", "application/json;charset=UTF-8"); + + // 设置请求体 + String jsonParams = JSON.toJSONString(params); + StringEntity entity = new StringEntity(jsonParams, StandardCharsets.UTF_8); + httpPost.setEntity(entity); + + HttpResponse response = httpClient.execute(httpPost); + HttpEntity responseEntity = response.getEntity(); + String result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8); + log.debug("POST请求响应: {}", result); + return result; + } catch (IOException e) { + log.error("POST请求失败: {}", e.getMessage(), e); + return null; + } + } +} +