diff --git a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java index 64bff2d..ff78bcf 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java @@ -42,7 +42,6 @@ import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeUnit; import static com.gxwebsoft.common.core.constants.PlatformConstants.MP_WEIXIN; import static com.gxwebsoft.common.core.constants.RedisConstants.ACCESS_TOKEN_KEY; @@ -246,27 +245,30 @@ public class WxLoginController extends BaseController { * @param userParam 需要传微信凭证code */ private String getPhoneByCode(UserParam userParam) { - // 获取手机号码 - String apiUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + getAccessToken(); HashMap paramMap = new HashMap<>(); if (StrUtil.isBlank(userParam.getCode())) { throw new BusinessException("code不能为空"); } paramMap.put("code", userParam.getCode()); - // 执行post请求 - String post = HttpUtil.post(apiUrl, JSON.toJSONString(paramMap)); - JSONObject json = JSON.parseObject(post); - if (json.get("errcode").equals(0)) { - JSONObject phoneInfo = JSON.parseObject(json.getString("phone_info")); - // 微信用户的手机号码 - final String phoneNumber = phoneInfo.getString("phoneNumber"); - // 验证手机号码 -// if (userParam.getNotVerifyPhone() == null && !Validator.isMobile(phoneNumber)) { -// String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString()); -// redisTemplate.delete(key); -// throw new BusinessException("手机号码格式不正确"); -// } - return phoneNumber; + + // access_token 失效/过期时自动刷新并重试一次 + for (int attempt = 0; attempt < 2; attempt++) { + String accessToken = (attempt == 0) ? getAccessToken(false) : getAccessToken(true); + String apiUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken; + + String post = HttpUtil.post(apiUrl, JSON.toJSONString(paramMap)); + JSONObject json = JSON.parseObject(post); + + Integer errcode = json.getInteger("errcode"); + if (errcode != null && errcode.equals(0)) { + JSONObject phoneInfo = JSON.parseObject(json.getString("phone_info")); + return phoneInfo.getString("phoneNumber"); + } + + if (errcode != null && (errcode == 40001 || errcode == 42001 || errcode == 40014)) { + continue; + } + return null; } return null; } @@ -285,6 +287,10 @@ public class WxLoginController extends BaseController { * ... */ public String getAccessToken() { + return getAccessToken(false); + } + + private String getAccessToken(boolean forceRefresh) { Integer tenantId = getTenantId(); String key = ACCESS_TOKEN_KEY.concat(":").concat(tenantId.toString()); @@ -294,9 +300,13 @@ public class WxLoginController extends BaseController { throw new BusinessException("请先配置小程序"); } + if (forceRefresh) { + redisTemplate.delete(key); + } + // 从缓存获取access_token String value = redisTemplate.opsForValue().get(key); - if (value != null) { + if (!forceRefresh && value != null) { // 解析access_token JSONObject response = JSON.parseObject(value); String accessToken = response.getString("access_token"); @@ -305,23 +315,38 @@ public class WxLoginController extends BaseController { } } - // 微信获取凭证接口 - String apiUrl = "https://api.weixin.qq.com/cgi-bin/token"; - // 组装url参数 - String url = apiUrl.concat("?grant_type=client_credential") - .concat("&appid=").concat(setting.getString("appId")) - .concat("&secret=").concat(setting.getString("appSecret")); + String appId = setting.getString("appId"); + String appSecret = setting.getString("appSecret"); - // 执行get请求 - String result = HttpUtil.get(url); - // 解析access_token + // 微信稳定版获取凭证接口(避免并发刷新导致旧token失效) + String apiUrl = "https://api.weixin.qq.com/cgi-bin/stable_token"; + JSONObject reqBody = new JSONObject(); + reqBody.put("grant_type", "client_credential"); + reqBody.put("appid", appId); + reqBody.put("secret", appSecret); + reqBody.put("force_refresh", forceRefresh); + + String result = HttpRequest.post(apiUrl) + .header("Content-Type", "application/json") + .body(reqBody.toJSONString()) + .execute() + .body(); JSONObject response = JSON.parseObject(result); - if (response.getString("access_token") != null) { - // 存入缓存 - redisTemplate.opsForValue().set(key, result, 7000L, TimeUnit.SECONDS); - return response.getString("access_token"); + + Integer errcode = response.getInteger("errcode"); + if (errcode != null && errcode != 0) { + throw new BusinessException("获取access_token失败: " + response.getString("errmsg") + " (errcode: " + errcode + ")"); } - throw new BusinessException("小程序配置不正确"); + + String accessToken = response.getString("access_token"); + Integer expiresIn = response.getInteger("expires_in"); + if (accessToken != null) { + long ttlSeconds = Math.max((expiresIn != null ? expiresIn : 7200) - 300L, 60L); + redisTemplate.opsForValue().set(key, result, ttlSeconds, TimeUnit.SECONDS); + return accessToken; + } + + throw new BusinessException("获取access_token失败: " + result); } @Operation(summary = "获取微信openId并更新") @@ -576,23 +601,29 @@ public class WxLoginController extends BaseController { throw new IOException("小程序配置不完整,缺少 appId 或 appSecret"); } - HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/cgi-bin/token") - .newBuilder() - .addQueryParameter("grant_type", "client_credential") - .addQueryParameter("appid", appId) - .addQueryParameter("secret", appSecret) - .build(); + HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/cgi-bin/stable_token").newBuilder().build(); + var root = om.createObjectNode(); + root.put("grant_type", "client_credential"); + root.put("appid", appId); + root.put("secret", appSecret); + root.put("force_refresh", false); - Request req = new Request.Builder().url(url).get().build(); + okhttp3.RequestBody reqBody = okhttp3.RequestBody.create( + root.toString(), MediaType.parse("application/json; charset=utf-8")); + Request req = new Request.Builder().url(url).post(reqBody).build(); try (Response resp = http.newCall(req).execute()) { String body = resp.body().string(); JsonNode json = om.readTree(body); + if (json.has("errcode") && json.get("errcode").asInt() != 0) { + throw new IOException("Get access_token failed: " + body); + } if (json.has("access_token")) { String token = json.get("access_token").asText(); long expiresIn = json.get("expires_in").asInt(7200); // 缓存完整的JSON响应,与其他方法保持一致 - redisUtil.set(key, body, expiresIn, TimeUnit.SECONDS); + long ttlSeconds = Math.max(expiresIn - 300L, 60L); + redisUtil.set(key, body, ttlSeconds, TimeUnit.SECONDS); tokenExpireEpoch = now + expiresIn; System.out.println("获取新的access_token成功(Local),租户ID: " + tenantId); return token; @@ -789,10 +820,21 @@ public class WxLoginController extends BaseController { String appId = wxConfig.getString("appId"); String appSecret = wxConfig.getString("appSecret"); - String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret; + String apiUrl = "https://api.weixin.qq.com/cgi-bin/stable_token"; System.out.println("调用微信API获取token - 租户ID: " + tenantId + ", AppID: " + (appId != null ? appId.substring(0, Math.min(8, appId.length())) + "..." : "null")); - System.out.println("微信API请求URL: " + apiUrl.replaceAll("secret=[^&]*", "secret=***")); - String result = HttpUtil.get(apiUrl); + System.out.println("微信API请求URL: " + apiUrl); + + JSONObject reqBody = new JSONObject(); + reqBody.put("grant_type", "client_credential"); + reqBody.put("appid", appId); + reqBody.put("secret", appSecret); + reqBody.put("force_refresh", false); + + String result = HttpRequest.post(apiUrl) + .header("Content-Type", "application/json") + .body(reqBody.toJSONString()) + .execute() + .body(); System.out.println("微信API响应: " + result); JSONObject json = JSON.parseObject(result); @@ -800,14 +842,15 @@ public class WxLoginController extends BaseController { if (json.containsKey("errcode")) { Integer errcode = json.getInteger("errcode"); String errmsg = json.getString("errmsg"); - System.err.println("微信API错误 - errcode: " + errcode + ", errmsg: " + errmsg); - - if (errcode == 40125) { - throw new RuntimeException("微信AppSecret配置错误,请检查并更新正确的AppSecret"); - } else if (errcode == 40013) { - throw new RuntimeException("微信AppID配置错误,请检查并更新正确的AppID"); - } else { - throw new RuntimeException("微信API调用失败: " + errmsg + " (errcode: " + errcode + ")"); + if (errcode != null && errcode != 0) { + System.err.println("微信API错误 - errcode: " + errcode + ", errmsg: " + errmsg); + if (errcode == 40125) { + throw new RuntimeException("微信AppSecret配置错误,请检查并更新正确的AppSecret"); + } else if (errcode == 40013) { + throw new RuntimeException("微信AppID配置错误,请检查并更新正确的AppID"); + } else { + throw new RuntimeException("微信API调用失败: " + errmsg + " (errcode: " + errcode + ")"); + } } } @@ -816,7 +859,8 @@ public class WxLoginController extends BaseController { Integer expiresIn = json.getInteger("expires_in"); // 缓存access_token,存储完整JSON响应(与getAccessToken方法保持一致) - redisUtil.set(key, result, (long) (expiresIn - 300), TimeUnit.SECONDS); + long ttlSeconds = Math.max((expiresIn != null ? expiresIn : 7200) - 300L, 60L); + redisUtil.set(key, result, ttlSeconds, TimeUnit.SECONDS); System.out.println("获取新的access_token成功,租户ID: " + tenantId); return accessToken; diff --git a/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMpCustomerMapper.xml b/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMpCustomerMapper.xml index a971636..ba973f6 100644 --- a/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMpCustomerMapper.xml +++ b/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMpCustomerMapper.xml @@ -73,6 +73,7 @@ AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') + OR a.to_user LIKE CONCAT('%', #{param.keywords}, '%') ) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5be3164..dcba0f0 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,7 +4,7 @@ server: # 多环境配置 spring: profiles: - active: glt2 + active: ysb2 application: name: server