主管理员 2 weeks ago
parent
commit
738ef83c4c
13 changed files with 589 additions and 26 deletions
  1. +1
    -1
      module-base/base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java
  2. +8
    -0
      module-common/src/main/java/org/jeecg/api/Final/UserType.java
  3. +24
    -2
      module-common/src/main/java/org/jeecg/api/controller/AppletLoginController.java
  4. +7
    -2
      module-common/src/main/java/org/jeecg/api/service/AppletLoginService.java
  5. +83
    -13
      module-common/src/main/java/org/jeecg/api/service/impl/AppletLoginServiceImpl.java
  6. +12
    -4
      module-common/src/main/java/org/jeecg/api/service/impl/AppletOrderServiceImpl.java
  7. +264
    -0
      module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpClientUtil.java
  8. +179
    -0
      module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpUtils.java
  9. +2
    -1
      module-pay/src/main/java/org/jeecg/modules/pay/MpWxPayService.java
  10. +2
    -0
      module-pay/src/main/java/org/jeecg/modules/pay/config/WxPay.java
  11. +1
    -1
      module-system/src/main/java/org/jeecg/modules/system/service/impl/SysBaseApiImpl.java
  12. +5
    -2
      module-system/src/main/resources/application-dev.yml
  13. +1
    -0
      module-system/src/main/resources/pay_weixin.properties

+ 1
- 1
module-base/base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java View File

@ -210,7 +210,7 @@ public class ShiroRealm extends AuthorizingRealm {
// 查询用户信息 // 查询用户信息
log.debug("———校验token是否有效————checkUserTokenIsEffect——————— "+ token); log.debug("———校验token是否有效————checkUserTokenIsEffect——————— "+ token);
HanHaiMember user = commonApi.getUserByNameHanHaiXcxOpenId(openid); HanHaiMember user = commonApi.getUserByNameHanHaiXcxOpenId(openid);
if (user == null || user.getAppletOpenid() == null) {
if (user == null) {
throw new AuthenticationException("用户不存在!"); throw new AuthenticationException("用户不存在!");
} }
// // 判断用户状态 // // 判断用户状态


+ 8
- 0
module-common/src/main/java/org/jeecg/api/Final/UserType.java View File

@ -0,0 +1,8 @@
package org.jeecg.api.Final;
public class UserType {
public static final String OFFICIAL = "official";
public static final String APPLET = "applet";
}

+ 24
- 2
module-common/src/main/java/org/jeecg/api/controller/AppletLoginController.java View File

@ -3,7 +3,10 @@ package org.jeecg.api.controller;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j; 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.LoginReq;
import org.jeecg.api.bean.PageBean; import org.jeecg.api.bean.PageBean;
import org.jeecg.api.req.UserInfoReq; import org.jeecg.api.req.UserInfoReq;
@ -12,6 +15,7 @@ import org.jeecg.common.api.vo.Result;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Map;
@Api(tags="授权登录") @Api(tags="授权登录")
@RestController @RestController
@ -26,8 +30,26 @@ public class AppletLoginController {
//微信小程序授权登录 //微信小程序授权登录
@ApiOperation(value="微信小程序授权登录", notes="微信小程序授权登录") @ApiOperation(value="微信小程序授权登录", notes="微信小程序授权登录")
@GetMapping(value = "/appletLogin") @GetMapping(value = "/appletLogin")
public Result<Object> appletLogin(LoginReq loginReq){
return appletLoginService.appletLogin(loginReq);
public Result<Object> 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<Map<String, Object>> getSignPackage(String url) {
if (StringUtils.isBlank(url)) {
return Result.error("url网址不能为空");
}
log.info("收到小程序登录请求,code: {}", url);
return appletLoginService.getSignPackage(url);
} }
//绑定手机号码 //绑定手机号码


+ 7
- 2
module-common/src/main/java/org/jeecg/api/service/AppletLoginService.java View File

@ -1,19 +1,22 @@
package org.jeecg.api.service; package org.jeecg.api.service;
import org.jeecg.api.bean.LoginReq; import org.jeecg.api.bean.LoginReq;
import org.jeecg.api.bean.PageBean;
import org.jeecg.api.req.UserInfoReq; import org.jeecg.api.req.UserInfoReq;
import org.jeecg.common.api.vo.Result; import org.jeecg.common.api.vo.Result;
import java.util.Map;
public interface AppletLoginService { public interface AppletLoginService {
/** /**
* 微信小程序登录接口 * 微信小程序登录接口
*
* @param loginReq * @param loginReq
* @param type
* @return * @return
*/ */
Result<Object> appletLogin(LoginReq loginReq);
Result<Object> appletLogin(LoginReq loginReq, String type);
/** /**
* 微信小程序登录绑定手机号码接口 * 微信小程序登录绑定手机号码接口
@ -37,4 +40,6 @@ public interface AppletLoginService {
Result<Object> phoneLogin(String phone, String code, String register); Result<Object> phoneLogin(String phone, String code, String register);
Result<Object> phoneSendCode(String phone); Result<Object> phoneSendCode(String phone);
Result<Map<String, Object>> getSignPackage(String url);
} }

+ 83
- 13
module-common/src/main/java/org/jeecg/api/service/impl/AppletLoginServiceImpl.java View File

@ -1,12 +1,16 @@
package org.jeecg.api.service.impl; package org.jeecg.api.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jeecg.api.Final.UserType;
import org.jeecg.api.bean.LoginReq; import org.jeecg.api.bean.LoginReq;
import org.jeecg.api.req.UserInfoReq; import org.jeecg.api.req.UserInfoReq;
import org.jeecg.api.service.AppletLoginService; import org.jeecg.api.service.AppletLoginService;
import org.jeecg.api.untils.HttpConf; 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.api.vo.Result;
import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException; import org.jeecg.common.exception.JeecgBootException;
@ -38,6 +42,9 @@ import java.util.*;
@Service @Service
public class AppletLoginServiceImpl implements AppletLoginService { public class AppletLoginServiceImpl implements AppletLoginService {
@Autowired
private WxHttpUtils wxHttpUtils;
@Value("${wechat.mpAppId}") @Value("${wechat.mpAppId}")
private String mpAppId; private String mpAppId;
@Value("${wechat.mpAppSecret}") @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<String, String> 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 loginReq
* @param type
* @return * @return
*/ */
@Transactional(rollbackFor = {Exception.class}) @Transactional(rollbackFor = {Exception.class})
@Override @Override
public Result<Object> appletLogin(LoginReq loginReq) {
public Result<Object> appletLogin(LoginReq loginReq, String type) {
JSONObject json_test = openLogin(loginReq.getCode(), type);
Result<Object> result = new Result<>(); Result<Object> result = new Result<>();
Map<String, Object> map = new HashMap<>(); Map<String, Object> 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 wxOpenid = json_test.getString("openid");
String sessionKey = json_test.getString("session_key"); String sessionKey = json_test.getString("session_key");
String unionid = json_test.getString("unionid");
if (StringUtils.isBlank(wxOpenid)) { if (StringUtils.isBlank(wxOpenid)) {
throw new JeecgBootException("未获取到openid"); throw new JeecgBootException("未获取到openid");
} }
Random random = new Random(); Random random = new Random();
int randomNumber = 100000 + random.nextInt(900000); 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) { if (member == null) {
//如果user等于null说明该用户第一次登录数据库没有该用户信息 //如果user等于null说明该用户第一次登录数据库没有该用户信息
loginReq.setOpenid(wxOpenid); loginReq.setOpenid(wxOpenid);
loginReq.setSession_key(sessionKey); loginReq.setSession_key(sessionKey);
member = new HanHaiMember(); member = new HanHaiMember();
member.setAppletOpenid(wxOpenid);
if (UserType.OFFICIAL.equals(type)){
member.setOfficialOpenid(wxOpenid);//
}else {
member.setAppletOpenid(wxOpenid);
}
member.setHeadImage(loginReq.getHeadimgurl()); member.setHeadImage(loginReq.getHeadimgurl());
// 生成token返回给小程序端
String token = JwtUtil.sign(member.getAppletOpenid(), wxOpenid);
member.setIntentioCode(String.valueOf(randomNumber)); member.setIntentioCode(String.valueOf(randomNumber));
memberService.save(member); memberService.save(member);
// 生成token返回给小程序端
String token = JwtUtil.sign(member.getId(), member.getId());
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
// 设置超时时间 // 设置超时时间
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 100); redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 100);
@ -124,7 +185,7 @@ public class AppletLoginServiceImpl implements AppletLoginService {
return result; return result;
} else { } else {
// 生成token返回给小程序端 // 生成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.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
// 设置超时时间 // 设置超时时间
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 100); 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()); return Result.error("发送验证码异常:" + e.getMessage());
} }
} }
@Override
public Result<Map<String, Object>> getSignPackage(String url) {
try {
return Result.ok(wxHttpUtils.getSignPackage(url));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** /**
* 生成6位数字验证码 * 生成6位数字验证码
*/ */


+ 12
- 4
module-common/src/main/java/org/jeecg/api/service/impl/AppletOrderServiceImpl.java View File

@ -11,6 +11,7 @@ import org.jeecg.api.bean.PageBean;
import org.jeecg.api.service.AppletAchievementService; import org.jeecg.api.service.AppletAchievementService;
import org.jeecg.api.service.AppletMoneyLogService; import org.jeecg.api.service.AppletMoneyLogService;
import org.jeecg.api.service.AppletOrderService; import org.jeecg.api.service.AppletOrderService;
import org.jeecg.api.wxUtils.WxHttpUtils;
import org.jeecg.common.api.vo.Result; import org.jeecg.common.api.vo.Result;
import org.jeecg.config.shiro.ShiroRealm; import org.jeecg.config.shiro.ShiroRealm;
import org.jeecg.modules.CommonChapterSubscribe.entity.CommonChapterSubscribe; import org.jeecg.modules.CommonChapterSubscribe.entity.CommonChapterSubscribe;
@ -101,6 +102,8 @@ public class AppletOrderServiceImpl implements AppletOrderService {
@Autowired @Autowired
private AppletAchievementService appletAchievementService; private AppletAchievementService appletAchievementService;
@Autowired
private WxHttpUtils wxHttpUtils;
//查询互动打赏礼物信息列表 //查询互动打赏礼物信息列表
@Override @Override
@ -135,8 +138,6 @@ public class AppletOrderServiceImpl implements AppletOrderService {
} }
//支付成功支付回调 //支付成功支付回调
@Override @Override
public Object payNotify(String requestBody) { public Object payNotify(String requestBody) {
@ -206,7 +207,13 @@ public class AppletOrderServiceImpl implements AppletOrderService {
.status("0") .status("0")
.build(); .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); commonOrderService.save(order);
Object appOrder = null; Object appOrder = null;
@ -237,8 +244,9 @@ public class AppletOrderServiceImpl implements AppletOrderService {
order.getId(), order.getId(),
i, i,
order.getId(), order.getId(),
member.getAppletOpenid(),
"{}");
openid,
"{}",
appid);
} }


+ 264
- 0
module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpClientUtil.java View File

@ -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<String, String> 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<String, String> 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<String, String> 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());
}
}
}
}
}

+ 179
- 0
module-common/src/main/java/org/jeecg/api/wxUtils/WxHttpUtils.java View File

@ -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<String, String> map = JSON.parseObject(response, new TypeReference<Map<String, String>>() {});
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<String, Object> 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 +
"&timestamp=" + timestamp +
"&url=" + url; // 确保URL是编码过的
String signature = sha1(string1);
Map<String, Object> 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();
}
}

+ 2
- 1
module-pay/src/main/java/org/jeecg/modules/pay/MpWxPayService.java View File

@ -90,7 +90,7 @@ public class MpWxPayService {
*/ */
public Object createOrder(String productName, String clientIp, public Object createOrder(String productName, String clientIp,
String productId, Integer price, String orderNo, String productId, Integer price, String orderNo,
String openId, String body){
String openId, String body, String appid){
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest(); WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
request.setDeviceInfo("WEB"); //设备号 request.setDeviceInfo("WEB"); //设备号
request.setTradeType("JSAPI"); //交易类型 request.setTradeType("JSAPI"); //交易类型
@ -102,6 +102,7 @@ public class MpWxPayService {
request.setNotifyUrl(wxPay.notifyOneUrl);//设置回调路径 request.setNotifyUrl(wxPay.notifyOneUrl);//设置回调路径
request.setProductId(productId); //商品id request.setProductId(productId); //商品id
request.setOpenid(openId); //JSAPI OPENID request.setOpenid(openId); //JSAPI OPENID
request.setAppid(appid);
if (dev){ if (dev){
request.setTotalFee(price); request.setTotalFee(price);
request.setNotifyUrl(wxPay.notifyUrlOneDev); request.setNotifyUrl(wxPay.notifyUrlOneDev);


+ 2
- 0
module-pay/src/main/java/org/jeecg/modules/pay/config/WxPay.java View File

@ -21,6 +21,8 @@ public class WxPay {
*/ */
public String appId; public String appId;
public String officialAppId;
/** /**
* 微信支付mchKey * 微信支付mchKey
*/ */


+ 1
- 1
module-system/src/main/java/org/jeecg/modules/system/service/impl/SysBaseApiImpl.java View File

@ -157,7 +157,7 @@ public class SysBaseApiImpl implements ISysBaseAPI {
if(oConvertUtils.isEmpty(username)) { if(oConvertUtils.isEmpty(username)) {
return null; return null;
} }
HanHaiMember user = hanHaiMemberService.lambdaQuery().eq(HanHaiMember::getAppletOpenid,username).one();
HanHaiMember user = hanHaiMemberService.lambdaQuery().eq(HanHaiMember::getId,username).one();
if(user==null) { if(user==null) {
return null; return null;
} }


+ 5
- 2
module-system/src/main/resources/application-dev.yml View File

@ -1,5 +1,5 @@
server: server:
port: 8002
port: 8003
tomcat: tomcat:
max-swallow-size: -1 max-swallow-size: -1
error: error:
@ -333,9 +333,12 @@ third-app:
##配置微信 - 推广项目 ##配置微信 - 推广项目
wechat: wechat:
official: # 公众号
appid: wx3c85955875a7b099
appsecret: 3d30690b7d2774e35d5b30521212df18
mpAppId: wxd21ddb1fbf3f5150 mpAppId: wxd21ddb1fbf3f5150
mpAppSecret: 2465111425acd2c1cf2b2a5f1df63ca8 mpAppSecret: 2465111425acd2c1cf2b2a5f1df63ca8
mchId:
mchId: 1711831449
mchKey: mchKey:
keyPath: keyPath:
notifyUrl: notifyUrl:


+ 1
- 0
module-system/src/main/resources/pay_weixin.properties View File

@ -1,5 +1,6 @@
pay.mchId=1711831449 pay.mchId=1711831449
pay.appId=wxd21ddb1fbf3f5150 pay.appId=wxd21ddb1fbf3f5150
pay.officialAppId=
pay.mchKey=0fdb77429ffdf206c151af76a663041c pay.mchKey=0fdb77429ffdf206c151af76a663041c
pay.keyPath=classpath:apiclient_cert.pem pay.keyPath=classpath:apiclient_cert.pem
pay.notifyUrl=https://prod-api.budingxiaoshuo.com/novel-admin/my_order/payNotify pay.notifyUrl=https://prod-api.budingxiaoshuo.com/novel-admin/my_order/payNotify


Loading…
Cancel
Save