From 738ef83c4cb3f157b344fafc6290aba861cfc3d5 Mon Sep 17 00:00:00 2001 From: lzx_win <2602107437@qq.com> Date: Tue, 21 Oct 2025 17:12:27 +0800 Subject: [PATCH] 1 --- .../java/org/jeecg/config/shiro/ShiroRealm.java | 2 +- .../main/java/org/jeecg/api/Final/UserType.java | 8 + .../api/controller/AppletLoginController.java | 26 +- .../org/jeecg/api/service/AppletLoginService.java | 9 +- .../api/service/impl/AppletLoginServiceImpl.java | 96 +++++++- .../api/service/impl/AppletOrderServiceImpl.java | 16 +- .../org/jeecg/api/wxUtils/WxHttpClientUtil.java | 264 +++++++++++++++++++++ .../java/org/jeecg/api/wxUtils/WxHttpUtils.java | 179 ++++++++++++++ .../java/org/jeecg/modules/pay/MpWxPayService.java | 3 +- .../java/org/jeecg/modules/pay/config/WxPay.java | 2 + .../system/service/impl/SysBaseApiImpl.java | 2 +- .../src/main/resources/application-dev.yml | 7 +- .../src/main/resources/pay_weixin.properties | 1 + 13 files changed, 589 insertions(+), 26 deletions(-) create mode 100644 module-common/src/main/java/org/jeecg/api/Final/UserType.java create mode 100644 module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpClientUtil.java create mode 100644 module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpUtils.java diff --git a/module-base/base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java b/module-base/base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java index d33917a..3745146 100644 --- a/module-base/base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java +++ b/module-base/base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java @@ -210,7 +210,7 @@ public class ShiroRealm extends AuthorizingRealm { // 查询用户信息 log.debug("———校验token是否有效————checkUserTokenIsEffect——————— "+ token); HanHaiMember user = commonApi.getUserByNameHanHaiXcxOpenId(openid); - if (user == null || user.getAppletOpenid() == null) { + if (user == null) { throw new AuthenticationException("用户不存在!"); } // // 判断用户状态 diff --git a/module-common/src/main/java/org/jeecg/api/Final/UserType.java b/module-common/src/main/java/org/jeecg/api/Final/UserType.java new file mode 100644 index 0000000..7d3414a --- /dev/null +++ b/module-common/src/main/java/org/jeecg/api/Final/UserType.java @@ -0,0 +1,8 @@ +package org.jeecg.api.Final; + +public class UserType { + + public static final String OFFICIAL = "official"; + public static final String APPLET = "applet"; + +} diff --git a/module-common/src/main/java/org/jeecg/api/controller/AppletLoginController.java b/module-common/src/main/java/org/jeecg/api/controller/AppletLoginController.java index 759b464..62a91f5 100644 --- a/module-common/src/main/java/org/jeecg/api/controller/AppletLoginController.java +++ b/module-common/src/main/java/org/jeecg/api/controller/AppletLoginController.java @@ -3,7 +3,10 @@ package org.jeecg.api.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.api.Final.UserType; import org.jeecg.api.bean.LoginReq; import org.jeecg.api.bean.PageBean; import org.jeecg.api.req.UserInfoReq; @@ -12,6 +15,7 @@ import org.jeecg.common.api.vo.Result; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.util.Map; @Api(tags="授权登录") @RestController @@ -26,8 +30,26 @@ public class AppletLoginController { //微信小程序授权登录 @ApiOperation(value="微信小程序授权登录", notes="微信小程序授权登录") @GetMapping(value = "/appletLogin") - public Result appletLogin(LoginReq loginReq){ - return appletLoginService.appletLogin(loginReq); + public Result appletLogin(LoginReq loginReq, @RequestParam(required = false, defaultValue = UserType.APPLET) String type){ + return appletLoginService.appletLogin(loginReq, type); + } + + /** + * 获取公众号签名 + * + * @param url URL + * @return 结果 + */ + @PostMapping("/getSignPackage") + @Operation(summary = "获取公众号签名", description = "获取公众号签名") + public Result> getSignPackage(String url) { + + if (StringUtils.isBlank(url)) { + return Result.error("url网址不能为空"); + } + + log.info("收到小程序登录请求,code: {}", url); + return appletLoginService.getSignPackage(url); } //绑定手机号码 diff --git a/module-common/src/main/java/org/jeecg/api/service/AppletLoginService.java b/module-common/src/main/java/org/jeecg/api/service/AppletLoginService.java index d65140e..4a10e2f 100644 --- a/module-common/src/main/java/org/jeecg/api/service/AppletLoginService.java +++ b/module-common/src/main/java/org/jeecg/api/service/AppletLoginService.java @@ -1,19 +1,22 @@ package org.jeecg.api.service; import org.jeecg.api.bean.LoginReq; -import org.jeecg.api.bean.PageBean; import org.jeecg.api.req.UserInfoReq; import org.jeecg.common.api.vo.Result; +import java.util.Map; + public interface AppletLoginService { /** * 微信小程序登录接口 + * * @param loginReq + * @param type * @return */ - Result appletLogin(LoginReq loginReq); + Result appletLogin(LoginReq loginReq, String type); /** * 微信小程序登录绑定手机号码接口 @@ -37,4 +40,6 @@ public interface AppletLoginService { Result phoneLogin(String phone, String code, String register); Result phoneSendCode(String phone); + + Result> getSignPackage(String url); } diff --git a/module-common/src/main/java/org/jeecg/api/service/impl/AppletLoginServiceImpl.java b/module-common/src/main/java/org/jeecg/api/service/impl/AppletLoginServiceImpl.java index 8c1a161..ec2150e 100644 --- a/module-common/src/main/java/org/jeecg/api/service/impl/AppletLoginServiceImpl.java +++ b/module-common/src/main/java/org/jeecg/api/service/impl/AppletLoginServiceImpl.java @@ -1,12 +1,16 @@ package org.jeecg.api.service.impl; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.jeecg.api.Final.UserType; import org.jeecg.api.bean.LoginReq; import org.jeecg.api.req.UserInfoReq; import org.jeecg.api.service.AppletLoginService; import org.jeecg.api.untils.HttpConf; +import org.jeecg.api.wxUtils.WxHttpClientUtil; +import org.jeecg.api.wxUtils.WxHttpUtils; import org.jeecg.common.api.vo.Result; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.exception.JeecgBootException; @@ -38,6 +42,9 @@ import java.util.*; @Service public class AppletLoginServiceImpl implements AppletLoginService { + @Autowired + private WxHttpUtils wxHttpUtils; + @Value("${wechat.mpAppId}") private String mpAppId; @Value("${wechat.mpAppSecret}") @@ -74,45 +81,99 @@ public class AppletLoginServiceImpl implements AppletLoginService { + private JSONObject openLogin(String code, String type){ + log.info("开始小程序登录,code: {}", code); + // 调用微信API获取openid和session_key + String loginUrl = "https://api.weixin.qq.com/sns/jscode2session"; + Map params = new HashMap<>(); + + if (UserType.OFFICIAL.equals(type)){ + loginUrl = "https://api.weixin.qq.com/sns/oauth2/access_token"; + params.put("appid", wxHttpUtils.getOfficialAppid()); + params.put("secret", wxHttpUtils.getOfficialSecret()); + params.put("code", code); + params.put("grant_type", "authorization_code"); + }else { + params.put("appid", wxHttpUtils.getAppid()); + params.put("secret", wxHttpUtils.getSecret()); + params.put("js_code", code); + params.put("grant_type", "authorization_code"); + } + + String response = WxHttpClientUtil.doGet(loginUrl, params); + + JSONObject jsonResponse = JSON.parseObject(response); + + // 检查微信API返回结果 + if (jsonResponse.containsKey("errcode") && jsonResponse.getInteger("errcode") != 0) { + log.error("微信登录失败: {}", response); + throw new JeecgBootException("微信登录失败: " + jsonResponse.getString("errmsg")); + } + return jsonResponse; + } /** * 微信小程序登录接口 + * * @param loginReq + * @param type * @return */ @Transactional(rollbackFor = {Exception.class}) @Override - public Result appletLogin(LoginReq loginReq) { + public Result appletLogin(LoginReq loginReq, String type) { + + JSONObject json_test = openLogin(loginReq.getCode(), type); + + Result result = new Result<>(); Map map = new HashMap<>(); - if (StringUtils.isBlank(loginReq.getCode())) { - throw new JeecgBootException("小程序code为空"); - } - String loginUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + mpAppId + "&secret=" + mpAppSecret+ "&js_code=" + loginReq.getCode() + "&grant_type=authorization_code"; - //使用工具类 - JSONObject json_test = httpConf.getJSONObject(loginUrl); +// if (StringUtils.isBlank(loginReq.getCode())) { +// throw new JeecgBootException("小程序code为空"); +// } +// String loginUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + mpAppId + "&secret=" + mpAppSecret+ "&js_code=" + loginReq.getCode() + "&grant_type=authorization_code"; +// //使用工具类 +// JSONObject json_test = httpConf.getJSONObject(loginUrl); + String wxOpenid = json_test.getString("openid"); String sessionKey = json_test.getString("session_key"); + String unionid = json_test.getString("unionid"); + + if (StringUtils.isBlank(wxOpenid)) { throw new JeecgBootException("未获取到openid"); } + Random random = new Random(); int randomNumber = 100000 + random.nextInt(900000); - HanHaiMember member = memberService.lambdaQuery().eq(HanHaiMember::getAppletOpenid,wxOpenid).one(); + HanHaiMember member = memberService.lambdaQuery() + .eq(UserType.APPLET.equals(type), HanHaiMember::getAppletOpenid, wxOpenid) + .eq(UserType.OFFICIAL.equals(type), HanHaiMember::getOfficialOpenid, wxOpenid) + .or() + .eq(StringUtils.isNoneEmpty(unionid), HanHaiMember::getWxUnionid, unionid) + .one(); if (member == null) { //如果user等于null说明该用户第一次登录,数据库没有该用户信息。 loginReq.setOpenid(wxOpenid); loginReq.setSession_key(sessionKey); member = new HanHaiMember(); - member.setAppletOpenid(wxOpenid); + + if (UserType.OFFICIAL.equals(type)){ + member.setOfficialOpenid(wxOpenid);// + }else { + member.setAppletOpenid(wxOpenid); + } member.setHeadImage(loginReq.getHeadimgurl()); - // 生成token返回给小程序端 - String token = JwtUtil.sign(member.getAppletOpenid(), wxOpenid); member.setIntentioCode(String.valueOf(randomNumber)); + memberService.save(member); + + // 生成token返回给小程序端 + String token = JwtUtil.sign(member.getId(), member.getId()); + redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); // 设置超时时间 redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 100); @@ -124,7 +185,7 @@ public class AppletLoginServiceImpl implements AppletLoginService { return result; } else { // 生成token返回给小程序端 - String token = JwtUtil.sign(member.getAppletOpenid(), wxOpenid); + String token = JwtUtil.sign(member.getId(), member.getId()); redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); // 设置超时时间 redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 100); @@ -334,7 +395,16 @@ public class AppletLoginServiceImpl implements AppletLoginService { return Result.error("发送验证码异常:" + e.getMessage()); } } - + + @Override + public Result> getSignPackage(String url) { + try { + return Result.ok(wxHttpUtils.getSignPackage(url)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** * 生成6位数字验证码 */ diff --git a/module-common/src/main/java/org/jeecg/api/service/impl/AppletOrderServiceImpl.java b/module-common/src/main/java/org/jeecg/api/service/impl/AppletOrderServiceImpl.java index 097ab4c..0519f07 100644 --- a/module-common/src/main/java/org/jeecg/api/service/impl/AppletOrderServiceImpl.java +++ b/module-common/src/main/java/org/jeecg/api/service/impl/AppletOrderServiceImpl.java @@ -11,6 +11,7 @@ import org.jeecg.api.bean.PageBean; import org.jeecg.api.service.AppletAchievementService; import org.jeecg.api.service.AppletMoneyLogService; import org.jeecg.api.service.AppletOrderService; +import org.jeecg.api.wxUtils.WxHttpUtils; import org.jeecg.common.api.vo.Result; import org.jeecg.config.shiro.ShiroRealm; import org.jeecg.modules.CommonChapterSubscribe.entity.CommonChapterSubscribe; @@ -101,6 +102,8 @@ public class AppletOrderServiceImpl implements AppletOrderService { @Autowired private AppletAchievementService appletAchievementService; + @Autowired + private WxHttpUtils wxHttpUtils; //查询互动打赏礼物信息列表 @Override @@ -135,8 +138,6 @@ public class AppletOrderServiceImpl implements AppletOrderService { } - - //支付成功支付回调 @Override public Object payNotify(String requestBody) { @@ -206,7 +207,13 @@ public class AppletOrderServiceImpl implements AppletOrderService { .status("0") .build(); + String openid = member.getAppletOpenid(); + String appid = wxHttpUtils.getAppid(); + if (StringUtils.isEmpty(member.getAppletOpenid()) && StringUtils.isNotEmpty(member.getOfficialOpenid())){ + openid = member.getOfficialOpenid(); + appid = wxHttpUtils.getOfficialAppid(); + } commonOrderService.save(order); Object appOrder = null; @@ -237,8 +244,9 @@ public class AppletOrderServiceImpl implements AppletOrderService { order.getId(), i, order.getId(), - member.getAppletOpenid(), - "{}"); + openid, + "{}", + appid); } diff --git a/module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpClientUtil.java b/module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpClientUtil.java new file mode 100644 index 0000000..a3a1467 --- /dev/null +++ b/module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpClientUtil.java @@ -0,0 +1,264 @@ +package org.jeecg.api.wxUtils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContextBuilder; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.net.URI; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Map; + +/** + * 微信API专用HTTP客户端工具类 + * 具有超时配置、重试机制和异常处理 + * + * @author system + * @date 2025-01-25 + */ +@Slf4j +public class WxHttpClientUtil { + + // 超时配置常量 + private static final int CONNECTION_REQUEST_TIMEOUT = 10000; // 10秒 + private static final int CONNECT_TIMEOUT = 15000; // 15秒 + private static final int SOCKET_TIMEOUT = 30000; // 30秒 + private static final int MAX_RETRY_COUNT = 3; // 最大重试次数 + + /** + * 创建带超时配置的SSL客户端 + */ + private static CloseableHttpClient createWxHttpClient() { + try { + // SSL配置 - 信任所有证书 + SSLContext sslContext = new SSLContextBuilder() + .loadTrustMaterial(null, new TrustStrategy() { + @Override + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return true; + } + }).build(); + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + sslContext, + SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER + ); + + // 请求配置 + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT) + .setConnectTimeout(CONNECT_TIMEOUT) + .setSocketTimeout(SOCKET_TIMEOUT) + .build(); + + // 重试处理器 + DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(MAX_RETRY_COUNT, true); + + return HttpClients.custom() + .setSSLSocketFactory(sslsf) + .setDefaultRequestConfig(requestConfig) + .setRetryHandler(retryHandler) + .build(); + + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { + log.error("创建SSL客户端失败: {}", e.getMessage(), e); + // 返回默认客户端作为后备 + return HttpClients.createDefault(); + } + } + + /** + * 执行GET请求(微信API专用) + * @param url 请求URL + * @param params 请求参数 + * @return 响应字符串 + */ + public static String doGet(String url, Map params) { + return doGetWithRetry(url, params, 0); + } + + /** + * 执行GET请求(微信API专用) + * @param url 请求URL + * @return 响应字符串 + */ + public static String doGet(String url) { + return doGet(url, null); + } + + /** + * 带重试机制的GET请求 + */ + private static String doGetWithRetry(String url, Map params, int retryCount) { + CloseableHttpClient httpClient = null; + CloseableHttpResponse response = null; + + try { + log.info("开始请求微信API: {}, 重试次数: {}", url, retryCount); + + httpClient = createWxHttpClient(); + + // 构建URI + URIBuilder builder = new URIBuilder(url); + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + builder.addParameter(entry.getKey(), entry.getValue()); + } + } + URI uri = builder.build(); + + // 创建GET请求 + HttpGet httpGet = new HttpGet(uri); + httpGet.setHeader("User-Agent", "WxHttpClient/1.0"); + httpGet.setHeader("Accept", "application/json, text/plain, */*"); + + // 执行请求 + response = httpClient.execute(httpGet); + + // 检查响应状态 + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == HttpStatus.SC_OK) { + String result = EntityUtils.toString(response.getEntity(), "UTF-8"); + log.info("微信API请求成功: {}", url); + return result; + } else { + log.warn("微信API返回非200状态码: {}, URL: {}", statusCode, url); + throw new RuntimeException("HTTP状态码异常: " + statusCode); + } + + } catch (Exception e) { + log.error("微信API请求失败: {}, 错误: {}, 重试次数: {}", url, e.getMessage(), retryCount); + + // 如果还有重试机会,进行重试 + if (retryCount < MAX_RETRY_COUNT) { + log.info("准备进行第{}次重试...", retryCount + 1); + try { + Thread.sleep(1000 * (retryCount + 1)); // 递增延迟 + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + return doGetWithRetry(url, params, retryCount + 1); + } + + // 重试次数用尽,抛出异常 + throw new RuntimeException("微信API请求失败,已重试" + MAX_RETRY_COUNT + "次: " + e.getMessage(), e); + + } finally { + // 关闭资源 + if (response != null) { + try { + response.close(); + } catch (IOException e) { + log.error("关闭响应失败: {}", e.getMessage()); + } + } + if (httpClient != null) { + try { + httpClient.close(); + } catch (IOException e) { + log.error("关闭客户端失败: {}", e.getMessage()); + } + } + } + } + + /** + * 执行POST请求(微信API专用) + * @param url 请求URL + * @param jsonBody JSON请求体 + * @return 响应字符串 + */ + public static String doPost(String url, String jsonBody) { + return doPostWithRetry(url, jsonBody, 0); + } + + /** + * 带重试机制的POST请求 + */ + private static String doPostWithRetry(String url, String jsonBody, int retryCount) { + CloseableHttpClient httpClient = null; + CloseableHttpResponse response = null; + + try { + log.info("开始POST请求微信API: {}, 重试次数: {}", url, retryCount); + + httpClient = createWxHttpClient(); + + // 创建POST请求 + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("Content-Type", "application/json; charset=UTF-8"); + httpPost.setHeader("User-Agent", "WxHttpClient/1.0"); + + // 设置请求体 + if (jsonBody != null) { + StringEntity entity = new StringEntity(jsonBody, "UTF-8"); + httpPost.setEntity(entity); + } + + // 执行请求 + response = httpClient.execute(httpPost); + + // 检查响应状态 + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == HttpStatus.SC_OK) { + String result = EntityUtils.toString(response.getEntity(), "UTF-8"); + log.info("微信API POST请求成功: {}", url); + return result; + } else { + log.warn("微信API POST返回非200状态码: {}, URL: {}", statusCode, url); + throw new RuntimeException("HTTP状态码异常: " + statusCode); + } + + } catch (Exception e) { + log.error("微信API POST请求失败: {}, 错误: {}, 重试次数: {}", url, e.getMessage(), retryCount); + + // 如果还有重试机会,进行重试 + if (retryCount < MAX_RETRY_COUNT) { + log.info("准备进行第{}次重试...", retryCount + 1); + try { + Thread.sleep(1000 * (retryCount + 1)); // 递增延迟 + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + return doPostWithRetry(url, jsonBody, retryCount + 1); + } + + // 重试次数用尽,抛出异常 + throw new RuntimeException("微信API POST请求失败,已重试" + MAX_RETRY_COUNT + "次: " + e.getMessage(), e); + + } finally { + // 关闭资源 + if (response != null) { + try { + response.close(); + } catch (IOException e) { + log.error("关闭响应失败: {}", e.getMessage()); + } + } + if (httpClient != null) { + try { + httpClient.close(); + } catch (IOException e) { + log.error("关闭客户端失败: {}", e.getMessage()); + } + } + } + } +} \ No newline at end of file diff --git a/module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpUtils.java b/module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpUtils.java new file mode 100644 index 0000000..2ef5dc1 --- /dev/null +++ b/module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpUtils.java @@ -0,0 +1,179 @@ +package org.jeecg.api.wxUtils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.TreeMap; + +@Getter +@Component +public class WxHttpUtils { + + @Value("${wechat.mpAppId}") + private String appid; + @Value("${wechat.mpAppSecret}") + private String secret;// + @Value("${wechat.mchId}") + private String mchId;// + + + @Value("${wechat.official.appid}") + private String officialAppid; + @Value("${wechat.official.appsecret}") + private String officialSecret;// + + private static String shipmentUrl = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token="; + private static final String GET_USER_PHONE_NUMBER = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"; + + private static String jsapiTicket = null; // 用于缓存jsapi_ticket + + /** + * 获取令牌 + * + * @return + */ + public String getAccessToken(String mAppId, String mSecret) { + String requestUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + mAppId + "&secret=" + mSecret; + + try { + // 使用增强版HTTP客户端,具有超时配置和重试机制 + String response = WxHttpClientUtil.doGet(requestUrl); + Map map = JSON.parseObject(response, new TypeReference>() {}); + + String accessToken = map.get("access_token"); + if (accessToken == null || accessToken.isEmpty()) { + throw new RuntimeException("获取access_token失败,响应: " + response); + } + + return accessToken; + } catch (Exception e) { + throw new RuntimeException("获取微信access_token失败: " + e.getMessage(), e); + } + } + + public String getAccessToken() { + return getAccessToken(appid, secret); + } + + public String getPhoneNumber(String code) throws Exception { + URL url = new URL(GET_USER_PHONE_NUMBER + "?access_token=" + this.getAccessToken()); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json; utf-8"); + conn.setRequestProperty("Accept", "application/json"); + conn.setDoOutput(true); + + JSONObject jsonInput = new JSONObject(); + jsonInput.put("code", code); + + try (DataOutputStream os = new DataOutputStream(conn.getOutputStream())) { + byte[] input = jsonInput.toString().getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + + try (BufferedReader br = new BufferedReader( + new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + StringBuilder response = new StringBuilder(); + String responseLine; + while ((responseLine = br.readLine()) != null) { + response.append(responseLine.trim()); + } + //获取手机号码 + return response.toString(); + } + } + + + // 获取jsapi_ticket + private String getJsApiTicket() throws Exception { + String accessToken = getAccessToken(officialAppid, officialSecret); + String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi"; + jsapiTicket = sendGet(url, "UTF-8"); + System.out.println("jsapiTicket=========="+jsapiTicket); + return jsapiTicket; + } + + // 发送GET请求并获取响应 + private String sendGet(String url, String encoding) throws Exception { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("GET"); + connection.connect(); + + BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), encoding)); + String inputLine; + StringBuilder content = new StringBuilder(); + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + in.close(); + connection.disconnect(); + return content.toString(); + } + + /** + * 获取公众号签名 + * + * @param url URL + * @return 结果 + */ + public Map getSignPackage(String url) throws Exception { + String jsapiTicket = getJsApiTicket(); + + + JSONObject jsonObject = JSONObject.parseObject(jsapiTicket); // 解析JSON字符串 + jsapiTicket = jsonObject.getString("ticket"); // 提取access_token + + + String nonceStr = createNonceStr(); + long timestamp = System.currentTimeMillis() / 1000; + + String string1 = "jsapi_ticket=" + jsapiTicket + + "&noncestr=" + nonceStr + + "×tamp=" + timestamp + + "&url=" + url; // 确保URL是编码过的 + + String signature = sha1(string1); + + Map ret = new TreeMap<>(); + ret.put("appId", officialAppid); + ret.put("timestamp", timestamp); + ret.put("nonceStr", nonceStr); + ret.put("signature", signature); + ret.put("jsapi_ticket", jsapiTicket); + ret.put("url",url); + + return ret; + } + + // 生成随机字符串 + private String createNonceStr() { + return String.valueOf((long) (Math.random() * 100000000)); + } + + // SHA1加密 + private String sha1(String input) throws NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] messageDigest = md.digest(input.getBytes("UTF-8")); + StringBuilder hexString = new StringBuilder(); + for (byte b : messageDigest) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) hexString.append('0'); + hexString.append(hex); + } + return hexString.toString(); + } +} diff --git a/module-pay/src/main/java/org/jeecg/modules/pay/MpWxPayService.java b/module-pay/src/main/java/org/jeecg/modules/pay/MpWxPayService.java index 92ff734..ae2ee6c 100644 --- a/module-pay/src/main/java/org/jeecg/modules/pay/MpWxPayService.java +++ b/module-pay/src/main/java/org/jeecg/modules/pay/MpWxPayService.java @@ -90,7 +90,7 @@ public class MpWxPayService { */ public Object createOrder(String productName, String clientIp, String productId, Integer price, String orderNo, - String openId, String body){ + String openId, String body, String appid){ WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest(); request.setDeviceInfo("WEB"); //设备号 request.setTradeType("JSAPI"); //交易类型 @@ -102,6 +102,7 @@ public class MpWxPayService { request.setNotifyUrl(wxPay.notifyOneUrl);//设置回调路径 request.setProductId(productId); //商品id request.setOpenid(openId); //JSAPI OPENID + request.setAppid(appid); if (dev){ request.setTotalFee(price); request.setNotifyUrl(wxPay.notifyUrlOneDev); diff --git a/module-pay/src/main/java/org/jeecg/modules/pay/config/WxPay.java b/module-pay/src/main/java/org/jeecg/modules/pay/config/WxPay.java index 9f9891f..bbcf10e 100644 --- a/module-pay/src/main/java/org/jeecg/modules/pay/config/WxPay.java +++ b/module-pay/src/main/java/org/jeecg/modules/pay/config/WxPay.java @@ -21,6 +21,8 @@ public class WxPay { */ public String appId; + public String officialAppId; + /** * 微信支付mchKey */ diff --git a/module-system/src/main/java/org/jeecg/modules/system/service/impl/SysBaseApiImpl.java b/module-system/src/main/java/org/jeecg/modules/system/service/impl/SysBaseApiImpl.java index 5bf4f47..ce9dc05 100644 --- a/module-system/src/main/java/org/jeecg/modules/system/service/impl/SysBaseApiImpl.java +++ b/module-system/src/main/java/org/jeecg/modules/system/service/impl/SysBaseApiImpl.java @@ -157,7 +157,7 @@ public class SysBaseApiImpl implements ISysBaseAPI { if(oConvertUtils.isEmpty(username)) { return null; } - HanHaiMember user = hanHaiMemberService.lambdaQuery().eq(HanHaiMember::getAppletOpenid,username).one(); + HanHaiMember user = hanHaiMemberService.lambdaQuery().eq(HanHaiMember::getId,username).one(); if(user==null) { return null; } diff --git a/module-system/src/main/resources/application-dev.yml b/module-system/src/main/resources/application-dev.yml index 7e14d43..5bf325d 100644 --- a/module-system/src/main/resources/application-dev.yml +++ b/module-system/src/main/resources/application-dev.yml @@ -1,5 +1,5 @@ server: - port: 8002 + port: 8003 tomcat: max-swallow-size: -1 error: @@ -333,9 +333,12 @@ third-app: ##配置微信 - 推广项目 wechat: + official: # 公众号 + appid: wx3c85955875a7b099 + appsecret: 3d30690b7d2774e35d5b30521212df18 mpAppId: wxd21ddb1fbf3f5150 mpAppSecret: 2465111425acd2c1cf2b2a5f1df63ca8 - mchId: + mchId: 1711831449 mchKey: keyPath: notifyUrl: diff --git a/module-system/src/main/resources/pay_weixin.properties b/module-system/src/main/resources/pay_weixin.properties index 98d78be..24774cb 100644 --- a/module-system/src/main/resources/pay_weixin.properties +++ b/module-system/src/main/resources/pay_weixin.properties @@ -1,5 +1,6 @@ pay.mchId=1711831449 pay.appId=wxd21ddb1fbf3f5150 +pay.officialAppId= pay.mchKey=0fdb77429ffdf206c151af76a663041c pay.keyPath=classpath:apiclient_cert.pem pay.notifyUrl=https://prod-api.budingxiaoshuo.com/novel-admin/my_order/payNotify