From 3b84f7df62ee5c88a517a86615cdbbac5c43fce0 Mon Sep 17 00:00:00 2001
From: xin <1099200748@qq.com>
Date: Wed, 28 May 2025 21:56:23 +0800
Subject: [PATCH] 微信和配置

---
 oying-system/src/main/java/com/oying/modules/security/config/WeiXinProperties.java |   31 ++++
 oying-system/src/main/resources/config/application-dev.yml                         |   54 +++++++
 oying-common/src/main/java/com/oying/utils/HttpRequest.java                        |  101 ++++++++++++++
 oying-system/src/main/resources/config/application-prod.yml                        |   53 +++++++
 pom.xml                                                                            |   14 ++
 oying-common/src/main/java/com/oying/utils/RedisUtils.java                         |   10 +
 oying-system/src/main/java/com/oying/modules/security/service/WeiXinService.java   |  109 +++++++++++++++
 7 files changed, 372 insertions(+), 0 deletions(-)

diff --git a/oying-common/src/main/java/com/oying/utils/HttpRequest.java b/oying-common/src/main/java/com/oying/utils/HttpRequest.java
new file mode 100644
index 0000000..d3293c4
--- /dev/null
+++ b/oying-common/src/main/java/com/oying/utils/HttpRequest.java
@@ -0,0 +1,101 @@
+package com.oying.utils;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.oying.exception.BadRequestException;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContexts;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+import javax.net.ssl.SSLContext;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Map;
+
+/**
+ * 功能描述:
+ *
+ * @author LIX
+ * @date 创建时间 :2022/6/15 下午4:19
+ */
+public class HttpRequest {
+
+
+    /**
+     * 读取请求数据流
+     *
+     * @param request 请求数据
+     * @return String
+     */
+    public static String getRequestBody(HttpServletRequest request) {
+        StringBuilder sb = new StringBuilder();
+        try (ServletInputStream inputStream = request.getInputStream();
+             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line);
+            }
+        } catch (IOException e) {
+            throw new BadRequestException("读取数据流异常");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 忽略ssl证书验证
+     *
+     * @return HttpComponentsClientHttpRequestFactory
+     */
+    public static HttpComponentsClientHttpRequestFactory getFactory() {
+        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+        try {
+            TrustStrategy acceptingTrustStrategy = (chain, authType) -> true;
+            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
+            SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
+            HttpClientBuilder clientBuilder = HttpClients.custom();
+            CloseableHttpClient httpClient = clientBuilder.setSSLSocketFactory(socketFactory).build();
+            requestFactory.setHttpClient(httpClient);
+        } catch (Exception e) {
+            throw new BadRequestException("忽略ssl证书验证失败!!!");
+        }
+        return requestFactory;
+    }
+
+    private static HttpHeaders getHeaders() {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return headers;
+    }
+
+    /**
+     * http返回String
+     */
+    public static String exchangeString(HttpMethod httpMethod, String url, Map<String, Object> map) {
+        HttpHeaders headers = getHeaders();
+        HttpEntity<Object> httpEntity = new HttpEntity<>(map, headers);
+        RestTemplate restTemplate = new RestTemplate();
+        return restTemplate.exchange(url, httpMethod, httpEntity, String.class).getBody();
+    }
+
+    /**
+     * http返回JSONObject
+     */
+    public static JSONObject exchangeJsonObject(HttpMethod httpMethod, String url, Map<String, Object> map) {
+        HttpHeaders headers = getHeaders();
+        HttpEntity<Object> httpEntity = new HttpEntity<>(map, headers);
+        RestTemplate restTemplate = new RestTemplate();
+        return restTemplate.exchange(url, httpMethod, httpEntity, JSONObject.class).getBody();
+    }
+}
diff --git a/oying-common/src/main/java/com/oying/utils/RedisUtils.java b/oying-common/src/main/java/com/oying/utils/RedisUtils.java
index ccd9126..d50d82c 100644
--- a/oying-common/src/main/java/com/oying/utils/RedisUtils.java
+++ b/oying-common/src/main/java/com/oying/utils/RedisUtils.java
@@ -31,6 +31,16 @@
     }
 
     /**
+     * 判断key是否过期
+     *
+     * @param key
+     * @return
+     */
+    public boolean isExpire(Object key) {
+        return getExpire(key) > 1 ? false : true;
+    }
+
+    /**
      * 指定缓存失效时间
      *
      * @param key  键
diff --git a/oying-system/src/main/java/com/oying/modules/security/config/WeiXinProperties.java b/oying-system/src/main/java/com/oying/modules/security/config/WeiXinProperties.java
new file mode 100644
index 0000000..bcaca43
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/security/config/WeiXinProperties.java
@@ -0,0 +1,31 @@
+package com.oying.modules.security.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "wx")
+public class WeiXinProperties {
+    // APPID
+    private String appId;
+    // APP密钥
+    private String appSecret;
+    // 跳转小程序类型
+    private String miniProgramState;
+    /*access_token的KEY*/
+    private String tokenKey;
+    /*access_token的失效时间间隔,微信是2小时,此处隔7200秒就重新获取*/
+    private Long tokenTime;
+    /*POST 获取稳定版接口调用凭据 获取小程序全局唯一后台接口调用凭据,token有效期为7200s,开发者需要进行妥善保存。*/
+    private String getStableAccessToken;
+    /*GET 小程序登录 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程*/
+    private String code2Session;
+    /*POST 获取手机号 该接口用于将code换取用户手机号。 说明,每个code只能使用一次,code的有效期为5min。*/
+    private String getPhoneNumber;
+    /*POST 该接口用于发送订阅消息。*/
+    private String sendMessage;
+    /* 是否生成环境*/
+    private boolean enabled;
+}
diff --git a/oying-system/src/main/java/com/oying/modules/security/service/WeiXinService.java b/oying-system/src/main/java/com/oying/modules/security/service/WeiXinService.java
new file mode 100644
index 0000000..2fe0146
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/security/service/WeiXinService.java
@@ -0,0 +1,109 @@
+package com.oying.modules.security.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.oying.modules.security.config.WeiXinProperties;
+import com.oying.utils.HttpRequest;
+import com.oying.utils.RedisUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class WeiXinService {
+
+    @Resource
+    private WeiXinProperties weiXinProperties;
+    @Resource
+    private RedisUtils redisUtils;
+    @Value("${wx.enabled}")
+    private Boolean wxEnabled;
+
+    /**
+     * POST 获取稳定版接口调用凭据 获取小程序全局唯一后台接口调用凭据,token有效期为7200s,开发者需要进行妥善保存。
+     *
+     * @return accessToken
+     */
+    public String getStableAccessToken() {
+        String accessToken;
+        if (redisUtils.isExpire(weiXinProperties.getTokenKey())) {
+            // 获取接口调用凭据
+            String url = weiXinProperties.getGetStableAccessToken();
+            Map<String, Object> map = new LinkedHashMap<>();
+            map.put("grant_type", "client_credential");
+            map.put("appid", weiXinProperties.getAppId());
+            map.put("secret", weiXinProperties.getAppSecret());
+            map.put("force_refresh", false);
+            JSONObject jsonObject = HttpRequest.exchangeJsonObject(HttpMethod.POST, url, map);
+            accessToken = jsonObject.getString("access_token");
+            redisUtils.set(weiXinProperties.getTokenKey(), accessToken, weiXinProperties.getTokenTime());
+        } else {
+            // 查询接口调用凭据
+            accessToken = (String) redisUtils.get(weiXinProperties.getTokenKey());
+        }
+        return accessToken;
+    }
+
+    /**
+     * GET 小程序登录 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程
+     *
+     * @param js_code 登录时获取的 code,可通过wx.login获取
+     * @return JSONObject
+     */
+    public JSONObject code2Session(String js_code) {
+        String url = weiXinProperties.getCode2Session();
+        url = url.replace("{appid}", weiXinProperties.getAppId())
+                .replace("{secret}", weiXinProperties.getAppSecret())
+                .replace("{js_code}", js_code);
+        return HttpRequest.exchangeJsonObject(HttpMethod.GET, url, null);
+    }
+
+    /**
+     * POST 获取手机号 该接口用于将code换取用户手机号。 说明,每个code只能使用一次,code的有效期为5min。
+     *
+     * @param code 手机号获取凭证
+     * @return JSONObject
+     */
+    public JSONObject getPhoneNumber(String code) {
+        String url = weiXinProperties.getGetPhoneNumber();
+        url = url.replace("{accessToken}", getStableAccessToken());
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("code", code);
+        return HttpRequest.exchangeJsonObject(HttpMethod.POST, url, map);
+    }
+
+    /**
+     * POST 该接口用于发送订阅消息。
+     *
+     * @param data 请求参数
+     * @return JSONObject
+     */
+    public JSONObject sendMessage(Map<String, Object> data, String openId, String templateId, String page) {
+        if (wxEnabled) {
+            String url = weiXinProperties.getSendMessage();
+            url = url.replace("{accessToken}", getStableAccessToken());
+            Map<String, Object> map = getSendMessageDto(data, openId, templateId, page);
+            return HttpRequest.exchangeJsonObject(HttpMethod.POST, url, map);
+        }
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("message", "测试环境");
+        return jsonObject;
+    }
+
+    private Map<String, Object> getSendMessageDto(Map<String, Object> data, String openId, String templateId, String page) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("touser", openId);
+        map.put("template_id", templateId);
+        map.put("page", page);
+        map.put("miniprogram_state", weiXinProperties.getMiniProgramState());
+        map.put("lang", "zh_CN");
+        map.put("data", data);
+        return map;
+    }
+}
diff --git a/oying-system/src/main/resources/config/application-dev.yml b/oying-system/src/main/resources/config/application-dev.yml
index 1880cf2..ce0a962 100644
--- a/oying-system/src/main/resources/config/application-dev.yml
+++ b/oying-system/src/main/resources/config/application-dev.yml
@@ -116,3 +116,57 @@
   # 文件大小 /M
   maxSize: 100
   avatarMaxSize: 5
+
+  # 短信验证码key 时间
+sms:
+  key: lyhd-sms-key-
+  time: 300
+
+#微信配置
+wx:
+  # 测试环境
+  enabled: false
+  # AppID
+  app-id: wx2273296a5569cbad
+  # AppSecret
+  app-secret: 4526d72d885be322b17d0694cd6d03f1
+  # 跳转小程序类型 developer为开发版;trial为体验版;formal为正式版;默认为正式版
+  mini-program-state: trial
+  #  access_token的KEY
+  token-key: wx_access_token
+  #  access_token的失效时间间隔,微信是2小时,此处隔7200秒就重新获取
+  token-time: 7200
+  # 微信URL调用
+  #  POST 获取稳定版接口调用凭据 获取小程序全局唯一后台接口调用凭据,token有效期为7200s,开发者需要进行妥善保存。
+  get-stable-access-token: https://api.weixin.qq.com/cgi-bin/stable_token
+  #  GET 小程序登录 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程
+  code2-session: https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={js_code}&grant_type=authorization_code
+  #  POST 获取手机号 该接口用于将code换取用户手机号。 说明,每个code只能使用一次,code的有效期为5min。
+  get-phone-number: https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={accessToken}
+  #  POST 该接口用于发送订阅消息。
+  send-message: https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={accessToken}
+
+# 汇旺财
+swift-pass:
+  # 密钥1
+  key-1: qqqqqqqqqqqqqqqqqqqq
+  # 私钥
+  mch-private-key: qqqqqqqqqqqq
+  # 公钥
+  plat-public-key: qqqqq
+  # 门店编号1
+  mch-id-1: 1111
+  # 签名方式
+  sign-type: MD5
+  # 原生JS
+  is-raw: 1
+  # 是否小程序支付
+  is-minipg: 1
+  # AppID
+  app-id: wx2273296a5569cbad
+  # 请求url
+  req-url: https://pay.hstypay.com/v2/pay/gateway
+  # 支付通知地址
+  notify-url: https://localhost/lyhd/api/swiftPass/alipayCallback
+  # 退款通知地址
+  refund-url: https://localhost/lyhd/api/swiftPass/returnNotify
diff --git a/oying-system/src/main/resources/config/application-prod.yml b/oying-system/src/main/resources/config/application-prod.yml
index f3cc9c8..cf83150 100644
--- a/oying-system/src/main/resources/config/application-prod.yml
+++ b/oying-system/src/main/resources/config/application-prod.yml
@@ -127,3 +127,56 @@
   # 文件大小 /M
   maxSize: 100
   avatarMaxSize: 5
+  # 短信验证码key 时间
+sms:
+  key: lyhd-sms-key-
+  time: 300
+
+#微信配置
+wx:
+  # 测试环境
+  enabled: false
+  # AppID
+  app-id: wx2273296a5569cbad
+  # AppSecret
+  app-secret: 4526d72d885be322b17d0694cd6d03f1
+  # 跳转小程序类型 developer为开发版;trial为体验版;formal为正式版;默认为正式版
+  mini-program-state: trial
+  #  access_token的KEY
+  token-key: wx_access_token
+  #  access_token的失效时间间隔,微信是2小时,此处隔7200秒就重新获取
+  token-time: 7200
+  # 微信URL调用
+  #  POST 获取稳定版接口调用凭据 获取小程序全局唯一后台接口调用凭据,token有效期为7200s,开发者需要进行妥善保存。
+  get-stable-access-token: https://api.weixin.qq.com/cgi-bin/stable_token
+  #  GET 小程序登录 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程
+  code2-session: https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={js_code}&grant_type=authorization_code
+  #  POST 获取手机号 该接口用于将code换取用户手机号。 说明,每个code只能使用一次,code的有效期为5min。
+  get-phone-number: https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={accessToken}
+  #  POST 该接口用于发送订阅消息。
+  send-message: https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={accessToken}
+
+# 汇旺财
+swift-pass:
+  # 密钥1
+  key-1: qqqqqqqqqqqqqqqqqqqq
+  # 私钥
+  mch-private-key: qqqqqqqqqqqq
+  # 公钥
+  plat-public-key: qqqqq
+  # 门店编号1
+  mch-id-1: 1111
+  # 签名方式
+  sign-type: MD5
+  # 原生JS
+  is-raw: 1
+  # 是否小程序支付
+  is-minipg: 1
+  # AppID
+  app-id: wx2273296a5569cbad
+  # 请求url
+  req-url: https://pay.hstypay.com/v2/pay/gateway
+  # 支付通知地址
+  notify-url: https://localhost/lyhd/api/swiftPass/alipayCallback
+  # 退款通知地址
+  refund-url: https://localhost/lyhd/api/swiftPass/returnNotify
diff --git a/pom.xml b/pom.xml
index a2fef79..489ecc4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -210,6 +210,20 @@
             <version>${fastjson2.version}</version>
         </dependency>
 
+        <!-- 请求跳过ssl证书验证 -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+        </dependency>
+
         <!-- Java图形验证码 -->
         <dependency>
             <groupId>com.github.whvcse</groupId>

--
Gitblit v1.9.3