refactor(qrlogin): 重构二维码登录功能并添加租户支持
- 在 QrLoginController 中添加 tenantId 参数 - 在 QrLoginService 接口中修改 generateQrLoginToken 方法签名,添加 tenantId 参数 - 重构 QrLoginServiceImpl 中的 generateQrLoginToken 方法,使用 WxService 获取 AccessToken - 新增 WxService 类,用于获取微信 AccessToken,并支持租户 ID
This commit is contained in:
@@ -35,7 +35,7 @@ public class QrLoginController extends BaseController {
|
|||||||
@PostMapping("/generate")
|
@PostMapping("/generate")
|
||||||
public ApiResult<?> generateQrLoginToken() {
|
public ApiResult<?> generateQrLoginToken() {
|
||||||
try {
|
try {
|
||||||
QrLoginGenerateResponse response = qrLoginService.generateQrLoginToken();
|
QrLoginGenerateResponse response = qrLoginService.generateQrLoginToken(getTenantId());
|
||||||
return success("生成成功", response);
|
return success("生成成功", response);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return fail(e.getMessage());
|
return fail(e.getMessage());
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public interface QrLoginService {
|
|||||||
*
|
*
|
||||||
* @return QrLoginGenerateResponse
|
* @return QrLoginGenerateResponse
|
||||||
*/
|
*/
|
||||||
QrLoginGenerateResponse generateQrLoginToken();
|
QrLoginGenerateResponse generateQrLoginToken(Integer tenantId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查扫码登录状态
|
* 检查扫码登录状态
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import com.gxwebsoft.common.core.security.JwtSubject;
|
|||||||
import com.gxwebsoft.common.core.security.JwtUtil;
|
import com.gxwebsoft.common.core.security.JwtUtil;
|
||||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.common.system.service.SettingService;
|
|
||||||
import com.gxwebsoft.common.system.service.UserService;
|
import com.gxwebsoft.common.system.service.UserService;
|
||||||
|
import com.gxwebsoft.common.system.service.WxService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
@@ -47,15 +47,12 @@ public class QrLoginServiceImpl implements QrLoginService {
|
|||||||
private ConfigProperties configProperties;
|
private ConfigProperties configProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SettingService settingService;
|
private WxService wxService;
|
||||||
|
|
||||||
@Autowired
|
private static final String QR_LOGIN_TOKEN = "QR_LOGIN_TOKEN";
|
||||||
private RedisTemplate<String, String> redisTemplate;
|
|
||||||
|
|
||||||
private static final String ACCESS_TOKEN_KEY = "WX_ACCESS_TOKEN";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QrLoginGenerateResponse generateQrLoginToken() {
|
public QrLoginGenerateResponse generateQrLoginToken(Integer tenantId) {
|
||||||
// 生成唯一的扫码登录token
|
// 生成唯一的扫码登录token
|
||||||
String token = UUID.randomUUID().toString(true);
|
String token = UUID.randomUUID().toString(true);
|
||||||
|
|
||||||
@@ -85,7 +82,7 @@ public class QrLoginServiceImpl implements QrLoginService {
|
|||||||
|
|
||||||
// 生成微信小程序码
|
// 生成微信小程序码
|
||||||
try {
|
try {
|
||||||
String miniprogramQrCodeUrl = generateMiniprogramQrCode(token);
|
String miniprogramQrCodeUrl = generateMiniprogramQrCode(token,tenantId);
|
||||||
response.setMiniprogramQrCodeUrl(miniprogramQrCodeUrl);
|
response.setMiniprogramQrCodeUrl(miniprogramQrCodeUrl);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("生成微信小程序码失败: {}", e.getMessage());
|
log.warn("生成微信小程序码失败: {}", e.getMessage());
|
||||||
@@ -234,9 +231,10 @@ public class QrLoginServiceImpl implements QrLoginService {
|
|||||||
/**
|
/**
|
||||||
* 生成微信小程序码
|
* 生成微信小程序码
|
||||||
*/
|
*/
|
||||||
private String generateMiniprogramQrCode(String token) {
|
private String generateMiniprogramQrCode(String token, Integer tenantId) {
|
||||||
try {
|
try {
|
||||||
String accessToken = getWxAccessToken();
|
// 使用公共的 WxService 获取 AccessToken
|
||||||
|
String accessToken = wxService.getAccessToken(tenantId);
|
||||||
if (StrUtil.isBlank(accessToken)) {
|
if (StrUtil.isBlank(accessToken)) {
|
||||||
throw new RuntimeException("获取微信AccessToken失败");
|
throw new RuntimeException("获取微信AccessToken失败");
|
||||||
}
|
}
|
||||||
@@ -275,64 +273,7 @@ public class QrLoginServiceImpl implements QrLoginService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取微信AccessToken
|
|
||||||
*/
|
|
||||||
private String getWxAccessToken() {
|
|
||||||
try {
|
|
||||||
String key = ACCESS_TOKEN_KEY + ":10048"; // 默认租户ID,可以根据需要调整
|
|
||||||
|
|
||||||
// 从缓存获取
|
|
||||||
String cachedToken = redisTemplate.opsForValue().get(key);
|
|
||||||
if (StrUtil.isNotBlank(cachedToken)) {
|
|
||||||
try {
|
|
||||||
JSONObject tokenData = JSON.parseObject(cachedToken);
|
|
||||||
String accessToken = tokenData.getString("access_token");
|
|
||||||
if (StrUtil.isNotBlank(accessToken)) {
|
|
||||||
return accessToken;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 解析失败,可能是旧格式,直接使用
|
|
||||||
if (!cachedToken.startsWith("{")) {
|
|
||||||
return cachedToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 缓存中没有,重新获取
|
|
||||||
JSONObject setting = settingService.getBySettingKey("mp-weixin");
|
|
||||||
if (setting == null) {
|
|
||||||
throw new RuntimeException("请先配置微信小程序");
|
|
||||||
}
|
|
||||||
|
|
||||||
String appId = setting.getString("appId");
|
|
||||||
String appSecret = setting.getString("appSecret");
|
|
||||||
if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
|
|
||||||
throw new RuntimeException("微信小程序配置不完整");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用微信API获取AccessToken
|
|
||||||
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
|
|
||||||
+ appId + "&secret=" + appSecret;
|
|
||||||
String response = HttpRequest.get(apiUrl).execute().body();
|
|
||||||
|
|
||||||
JSONObject result = JSON.parseObject(response);
|
|
||||||
String accessToken = result.getString("access_token");
|
|
||||||
if (StrUtil.isNotBlank(accessToken)) {
|
|
||||||
// 存入缓存
|
|
||||||
JSONObject tokenData = new JSONObject();
|
|
||||||
tokenData.put("access_token", accessToken);
|
|
||||||
tokenData.put("expires_in", result.get("expires_in"));
|
|
||||||
redisTemplate.opsForValue().set(key, tokenData.toJSONString(), 7000L, TimeUnit.SECONDS);
|
|
||||||
return accessToken;
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("获取AccessToken失败: " + response);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("获取微信AccessToken失败: {}", e.getMessage(), e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件上传路径
|
* 获取文件上传路径
|
||||||
|
|||||||
110
src/main/java/com/gxwebsoft/common/system/service/WxService.java
Normal file
110
src/main/java/com/gxwebsoft/common/system/service/WxService.java
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package com.gxwebsoft.common.system.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.http.HttpRequest;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信公共服务类
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2025-09-08
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class WxService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SettingService settingService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate<String, String> redisTemplate;
|
||||||
|
|
||||||
|
private static final String ACCESS_TOKEN_KEY = "WX_ACCESS_TOKEN";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信AccessToken(使用默认租户)
|
||||||
|
*/
|
||||||
|
public String getAccessToken() {
|
||||||
|
return getAccessToken(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信AccessToken(支持指定租户ID)
|
||||||
|
*
|
||||||
|
* @param tenantId 租户ID,为null时使用默认值
|
||||||
|
* @return access_token
|
||||||
|
*/
|
||||||
|
public String getAccessToken(Integer tenantId) {
|
||||||
|
if (tenantId == null) {
|
||||||
|
tenantId = 10048; // 默认租户ID,可以根据需要调整
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = ACCESS_TOKEN_KEY + ":" + tenantId;
|
||||||
|
|
||||||
|
// 从缓存获取
|
||||||
|
String cachedToken = redisTemplate.opsForValue().get(key);
|
||||||
|
if (StrUtil.isNotBlank(cachedToken)) {
|
||||||
|
try {
|
||||||
|
JSONObject tokenData = JSON.parseObject(cachedToken);
|
||||||
|
String accessToken = tokenData.getString("access_token");
|
||||||
|
if (StrUtil.isNotBlank(accessToken)) {
|
||||||
|
log.debug("从缓存获取access_token: {}", accessToken);
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 解析失败,可能是旧格式,直接使用
|
||||||
|
if (!cachedToken.startsWith("{")) {
|
||||||
|
log.debug("从缓存获取access_token(旧格式): {}", cachedToken);
|
||||||
|
return cachedToken;
|
||||||
|
}
|
||||||
|
log.warn("解析缓存的access_token失败: {}", e.getMessage());
|
||||||
|
// 缓存数据异常,删除缓存,重新获取
|
||||||
|
redisTemplate.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存中没有,重新获取
|
||||||
|
try {
|
||||||
|
JSONObject setting = settingService.getBySettingKey("mp-weixin");
|
||||||
|
if (setting == null) {
|
||||||
|
throw new RuntimeException("请先配置微信小程序");
|
||||||
|
}
|
||||||
|
|
||||||
|
String appId = setting.getString("appId");
|
||||||
|
String appSecret = setting.getString("appSecret");
|
||||||
|
if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
|
||||||
|
throw new RuntimeException("微信小程序配置不完整");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用微信API获取AccessToken
|
||||||
|
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
|
||||||
|
+ appId + "&secret=" + appSecret;
|
||||||
|
String response = HttpRequest.get(apiUrl).execute().body();
|
||||||
|
|
||||||
|
JSONObject result = JSON.parseObject(response);
|
||||||
|
String accessToken = result.getString("access_token");
|
||||||
|
if (StrUtil.isNotBlank(accessToken)) {
|
||||||
|
// 存入缓存
|
||||||
|
JSONObject tokenData = new JSONObject();
|
||||||
|
tokenData.put("access_token", accessToken);
|
||||||
|
tokenData.put("expires_in", result.get("expires_in"));
|
||||||
|
redisTemplate.opsForValue().set(key, tokenData.toJSONString(), 7000L, TimeUnit.SECONDS);
|
||||||
|
log.info("获取新的access_token成功: {}", accessToken);
|
||||||
|
return accessToken;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("获取AccessToken失败: " + response);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取微信AccessToken失败: {}", e.getMessage(), e);
|
||||||
|
throw new RuntimeException("获取微信AccessToken失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user