|
@ -38,6 +38,7 @@ import java.io.File; |
|
|
import java.io.IOException; |
|
|
import java.io.IOException; |
|
|
import java.time.Instant; |
|
|
import java.time.Instant; |
|
|
import java.util.HashMap; |
|
|
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.PlatformConstants.MP_WEIXIN; |
|
@ -224,7 +225,7 @@ public class WxLoginController extends BaseController { |
|
|
// 获取openid
|
|
|
// 获取openid
|
|
|
private JSONObject getOpenIdByCode(UserParam userParam) { |
|
|
private JSONObject getOpenIdByCode(UserParam userParam) { |
|
|
// 获取微信小程序配置信息
|
|
|
// 获取微信小程序配置信息
|
|
|
JSONObject setting = settingService.getBySettingKey("mp-weixin"); |
|
|
|
|
|
|
|
|
JSONObject setting = settingService.getBySettingKey("mp-weixin",getTenantId()); |
|
|
// 获取openId
|
|
|
// 获取openId
|
|
|
String apiUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + setting.getString("appId") + "&secret=" + setting.getString("appSecret") + "&js_code=" + userParam.getCode() + "&grant_type=authorization_code"; |
|
|
String apiUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + setting.getString("appId") + "&secret=" + setting.getString("appSecret") + "&js_code=" + userParam.getCode() + "&grant_type=authorization_code"; |
|
|
// 执行get请求
|
|
|
// 执行get请求
|
|
@ -277,24 +278,34 @@ public class WxLoginController extends BaseController { |
|
|
* 获取接口调用凭据AccessToken |
|
|
* 获取接口调用凭据AccessToken |
|
|
* <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html">...</a> |
|
|
* <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html">...</a> |
|
|
*/ |
|
|
*/ |
|
|
private String getAccessToken() { |
|
|
|
|
|
String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString()); |
|
|
|
|
|
// 获取微信小程序配置信息
|
|
|
|
|
|
JSONObject setting = settingService.getBySettingKey("mp-weixin"); |
|
|
|
|
|
|
|
|
public String getAccessToken() { |
|
|
|
|
|
Integer tenantId = getTenantId(); |
|
|
|
|
|
String key = ACCESS_TOKEN_KEY.concat(":").concat(tenantId.toString()); |
|
|
|
|
|
|
|
|
|
|
|
// 使用跨租户方式获取微信小程序配置信息
|
|
|
|
|
|
JSONObject setting = settingService.getBySettingKeyIgnoreTenant("mp-weixin", tenantId); |
|
|
if (setting == null) { |
|
|
if (setting == null) { |
|
|
throw new BusinessException("请先配置小程序"); |
|
|
throw new BusinessException("请先配置小程序"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 从缓存获取access_token
|
|
|
// 从缓存获取access_token
|
|
|
String value = redisTemplate.opsForValue().get(key); |
|
|
String value = redisTemplate.opsForValue().get(key); |
|
|
if (value != null) { |
|
|
if (value != null) { |
|
|
// 解析access_token
|
|
|
// 解析access_token
|
|
|
JSONObject response = JSON.parseObject(value); |
|
|
JSONObject response = JSON.parseObject(value); |
|
|
// return response.getString("access_token");
|
|
|
|
|
|
|
|
|
String accessToken = response.getString("access_token"); |
|
|
|
|
|
if (accessToken != null) { |
|
|
|
|
|
return accessToken; |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 微信获取凭证接口
|
|
|
// 微信获取凭证接口
|
|
|
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token"; |
|
|
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token"; |
|
|
// 组装url参数
|
|
|
// 组装url参数
|
|
|
String url = apiUrl.concat("?grant_type=client_credential").concat("&appid=").concat(setting.getString("appId")).concat("&secret=").concat(setting.getString("appSecret")); |
|
|
|
|
|
|
|
|
String url = apiUrl.concat("?grant_type=client_credential") |
|
|
|
|
|
.concat("&appid=").concat(setting.getString("appId")) |
|
|
|
|
|
.concat("&secret=").concat(setting.getString("appSecret")); |
|
|
|
|
|
|
|
|
// 执行get请求
|
|
|
// 执行get请求
|
|
|
String result = HttpUtil.get(url); |
|
|
String result = HttpUtil.get(url); |
|
|
// 解析access_token
|
|
|
// 解析access_token
|
|
@ -321,7 +332,7 @@ public class WxLoginController extends BaseController { |
|
|
// 请求微信接口获取openid
|
|
|
// 请求微信接口获取openid
|
|
|
String apiUrl = "https://api.weixin.qq.com/sns/jscode2session"; |
|
|
String apiUrl = "https://api.weixin.qq.com/sns/jscode2session"; |
|
|
final HashMap<String, Object> map = new HashMap<>(); |
|
|
final HashMap<String, Object> map = new HashMap<>(); |
|
|
final JSONObject setting = settingService.getBySettingKey("mp-weixin"); |
|
|
|
|
|
|
|
|
final JSONObject setting = settingService.getBySettingKey("mp-weixin",getTenantId()); |
|
|
final String appId = setting.getString("appId"); |
|
|
final String appId = setting.getString("appId"); |
|
|
final String appSecret = setting.getString("appSecret"); |
|
|
final String appSecret = setting.getString("appSecret"); |
|
|
map.put("appid", appId); |
|
|
map.put("appid", appId); |
|
@ -349,7 +360,7 @@ public class WxLoginController extends BaseController { |
|
|
|
|
|
|
|
|
String apiUrl = "https://api.weixin.qq.com/sns/jscode2session"; |
|
|
String apiUrl = "https://api.weixin.qq.com/sns/jscode2session"; |
|
|
final HashMap<String, Object> map = new HashMap<>(); |
|
|
final HashMap<String, Object> map = new HashMap<>(); |
|
|
final JSONObject setting = settingService.getBySettingKey("mp-weixin"); |
|
|
|
|
|
|
|
|
final JSONObject setting = settingService.getBySettingKey("mp-weixin",getTenantId()); |
|
|
final String appId = setting.getString("appId"); |
|
|
final String appId = setting.getString("appId"); |
|
|
final String appSecret = setting.getString("appSecret"); |
|
|
final String appSecret = setting.getString("appSecret"); |
|
|
map.put("appid", appId); |
|
|
map.put("appid", appId); |
|
@ -408,29 +419,62 @@ public class WxLoginController extends BaseController { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Operation(summary = "获取微信小程序码-订单核销码-数量极多的业务场景") |
|
|
@Operation(summary = "获取微信小程序码-订单核销码-数量极多的业务场景") |
|
|
@GetMapping("/getOrderQRCodeUnlimited/{orderNo}") |
|
|
|
|
|
public void getOrderQRCodeUnlimited(@PathVariable("orderNo") String orderNo, HttpServletResponse response) throws IOException { |
|
|
|
|
|
System.out.println("orderNo = " + orderNo); |
|
|
|
|
|
String apiUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getLocalAccessToken(); |
|
|
|
|
|
|
|
|
@GetMapping("/getOrderQRCodeUnlimited/{scene}") |
|
|
|
|
|
public void getOrderQRCodeUnlimited(@PathVariable("scene") String scene, HttpServletResponse response) throws IOException { |
|
|
|
|
|
System.out.println("scene = " + scene); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
// 使用统一的 access_token 获取方法
|
|
|
|
|
|
String accessToken = getAccessToken(); |
|
|
|
|
|
String apiUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken; |
|
|
|
|
|
|
|
|
final HashMap<String, Object> map = new HashMap<>(); |
|
|
final HashMap<String, Object> map = new HashMap<>(); |
|
|
map.put("scene", orderNo); |
|
|
|
|
|
|
|
|
map.put("scene", scene); |
|
|
map.put("page", "pages/index/index"); |
|
|
map.put("page", "pages/index/index"); |
|
|
map.put("env_version", "develop"); |
|
|
|
|
|
|
|
|
map.put("env_version", "trial"); |
|
|
|
|
|
|
|
|
String jsonBody = JSON.toJSONString(map); |
|
|
String jsonBody = JSON.toJSONString(map); |
|
|
System.out.println("请求的 JSON body = " + jsonBody); |
|
|
System.out.println("请求的 JSON body = " + jsonBody); |
|
|
// 获取图片 Buffer
|
|
|
|
|
|
byte[] qrCode = HttpRequest.post(apiUrl) |
|
|
|
|
|
.body(JSON.toJSONString(map)) |
|
|
|
|
|
.execute().bodyBytes(); |
|
|
|
|
|
|
|
|
|
|
|
// 设置响应头
|
|
|
|
|
|
|
|
|
// 获取微信 API 响应
|
|
|
|
|
|
cn.hutool.http.HttpResponse httpResponse = HttpRequest.post(apiUrl) |
|
|
|
|
|
.body(jsonBody) |
|
|
|
|
|
.execute(); |
|
|
|
|
|
|
|
|
|
|
|
byte[] responseBytes = httpResponse.bodyBytes(); |
|
|
|
|
|
String contentType = httpResponse.header("Content-Type"); |
|
|
|
|
|
|
|
|
|
|
|
// 检查响应内容类型,判断是否为错误响应
|
|
|
|
|
|
if (contentType != null && contentType.contains("application/json")) { |
|
|
|
|
|
// 微信返回了错误信息(JSON格式)
|
|
|
|
|
|
String errorResponse = new String(responseBytes, "UTF-8"); |
|
|
|
|
|
System.err.println("微信 API 错误响应: " + errorResponse); |
|
|
|
|
|
|
|
|
|
|
|
// 返回错误信息给前端
|
|
|
|
|
|
response.setContentType("application/json;charset=UTF-8"); |
|
|
|
|
|
response.setStatus(HttpServletResponse.SC_BAD_REQUEST); |
|
|
|
|
|
response.getWriter().write(errorResponse); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 成功获取二维码图片
|
|
|
response.setContentType("image/png"); |
|
|
response.setContentType("image/png"); |
|
|
response.setHeader("Cache-Control", "no-cache"); |
|
|
response.setHeader("Cache-Control", "no-cache"); |
|
|
response.setHeader("Content-Disposition", "inline; filename=encrypted_qrcode.png"); |
|
|
|
|
|
|
|
|
response.setHeader("Content-Disposition", "inline; filename=qrcode.png"); |
|
|
|
|
|
|
|
|
// 输出图片
|
|
|
// 输出图片
|
|
|
response.getOutputStream().write(qrCode); |
|
|
|
|
|
System.out.println("response = " + response); |
|
|
|
|
|
|
|
|
response.getOutputStream().write(responseBytes); |
|
|
|
|
|
System.out.println("二维码生成成功,大小: " + responseBytes.length + " bytes"); |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
System.err.println("生成二维码失败: " + e.getMessage()); |
|
|
|
|
|
e.printStackTrace(); |
|
|
|
|
|
|
|
|
|
|
|
// 返回错误信息
|
|
|
|
|
|
response.setContentType("application/json;charset=UTF-8"); |
|
|
|
|
|
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
|
|
|
|
|
response.getWriter().write("{\"error\":\"生成二维码失败: " + e.getMessage() + "\"}"); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Operation(summary = "获取微信小程序码-用户ID") |
|
|
@Operation(summary = "获取微信小程序码-用户ID") |
|
@ -475,16 +519,33 @@ public class WxLoginController extends BaseController { |
|
|
/** 获取/刷新 access_token */ |
|
|
/** 获取/刷新 access_token */ |
|
|
public String getLocalAccessToken() throws IOException { |
|
|
public String getLocalAccessToken() throws IOException { |
|
|
long now = Instant.now().getEpochSecond(); |
|
|
long now = Instant.now().getEpochSecond(); |
|
|
String key ="AccessToken:Local:10550"; |
|
|
|
|
|
|
|
|
String key = "AccessToken:Local:" + getTenantId(); |
|
|
|
|
|
|
|
|
if (redisUtil.get(key) != null && now < tokenExpireEpoch - 60) { |
|
|
if (redisUtil.get(key) != null && now < tokenExpireEpoch - 60) { |
|
|
return redisUtil.get(key); |
|
|
return redisUtil.get(key); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 使用跨租户方式获取微信小程序配置信息
|
|
|
|
|
|
Integer tenantId = getTenantId(); |
|
|
|
|
|
JSONObject setting = settingService.getBySettingKeyIgnoreTenant("mp-weixin", tenantId); |
|
|
|
|
|
if (setting == null) { |
|
|
|
|
|
throw new IOException("请先配置小程序"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
String appId = setting.getString("appId"); |
|
|
|
|
|
String appSecret = setting.getString("appSecret"); |
|
|
|
|
|
|
|
|
|
|
|
if (appId == null || appSecret == null) { |
|
|
|
|
|
throw new IOException("小程序配置不完整,缺少 appId 或 appSecret"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/cgi-bin/token") |
|
|
HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/cgi-bin/token") |
|
|
.newBuilder() |
|
|
.newBuilder() |
|
|
.addQueryParameter("grant_type", "client_credential") |
|
|
.addQueryParameter("grant_type", "client_credential") |
|
|
.addQueryParameter("appid", "wx51962d6ac21f2ed2") |
|
|
|
|
|
.addQueryParameter("secret", "d821f98de8a6c1ba7bc7e0ee84bcbc8e") |
|
|
|
|
|
|
|
|
.addQueryParameter("appid", appId) |
|
|
|
|
|
.addQueryParameter("secret", appSecret) |
|
|
.build(); |
|
|
.build(); |
|
|
|
|
|
|
|
|
Request req = new Request.Builder().url(url).get().build(); |
|
|
Request req = new Request.Builder().url(url).get().build(); |
|
|
try (Response resp = http.newCall(req).execute()) { |
|
|
try (Response resp = http.newCall(req).execute()) { |
|
|
String body = resp.body().string(); |
|
|
String body = resp.body().string(); |
|
@ -492,7 +553,7 @@ public class WxLoginController extends BaseController { |
|
|
if (json.has("access_token")) { |
|
|
if (json.has("access_token")) { |
|
|
String token = json.get("access_token").asText(); |
|
|
String token = json.get("access_token").asText(); |
|
|
long expiresIn = json.get("expires_in").asInt(7200); |
|
|
long expiresIn = json.get("expires_in").asInt(7200); |
|
|
redisUtil.set(key,token,expiresIn,TimeUnit.SECONDS); |
|
|
|
|
|
|
|
|
redisUtil.set(key, token, expiresIn, TimeUnit.SECONDS); |
|
|
tokenExpireEpoch = now + expiresIn; |
|
|
tokenExpireEpoch = now + expiresIn; |
|
|
return token; |
|
|
return token; |
|
|
} else { |
|
|
} else { |
|
@ -508,5 +569,74 @@ public class WxLoginController extends BaseController { |
|
|
return config.getUploadPath() + "file/"; |
|
|
return config.getUploadPath() + "file/"; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Operation(summary = "调试:检查微信小程序配置") |
|
|
|
|
|
@GetMapping("/debug/checkWxConfig") |
|
|
|
|
|
public ApiResult<?> debugCheckWxConfig() { |
|
|
|
|
|
Integer tenantId = getTenantId(); |
|
|
|
|
|
Map<String, Object> result = new HashMap<>(); |
|
|
|
|
|
result.put("tenantId", tenantId); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
// 尝试获取配置
|
|
|
|
|
|
JSONObject setting = settingService.getBySettingKeyIgnoreTenant("mp-weixin", tenantId); |
|
|
|
|
|
result.put("hasConfig", true); |
|
|
|
|
|
result.put("config", setting); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
result.put("hasConfig", false); |
|
|
|
|
|
result.put("error", e.getMessage()); |
|
|
|
|
|
|
|
|
|
|
|
// 提供创建配置的建议
|
|
|
|
|
|
Map<String, Object> suggestion = new HashMap<>(); |
|
|
|
|
|
suggestion.put("message", "请在系统设置中创建微信小程序配置"); |
|
|
|
|
|
suggestion.put("configKey", "mp-weixin"); |
|
|
|
|
|
suggestion.put("tenantId", tenantId); |
|
|
|
|
|
suggestion.put("sampleConfig", createSampleWxConfig()); |
|
|
|
|
|
result.put("suggestion", suggestion); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return success("配置检查完成", result); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Operation(summary = "调试:创建示例微信小程序配置") |
|
|
|
|
|
@PostMapping("/debug/createSampleWxConfig") |
|
|
|
|
|
public ApiResult<?> debugCreateSampleWxConfig(@RequestBody Map<String, String> params) { |
|
|
|
|
|
Integer tenantId = getTenantId(); |
|
|
|
|
|
|
|
|
|
|
|
String appId = params.get("appId"); |
|
|
|
|
|
String appSecret = params.get("appSecret"); |
|
|
|
|
|
|
|
|
|
|
|
if (appId == null || appSecret == null) { |
|
|
|
|
|
return fail("请提供 appId 和 appSecret", null); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
// 创建配置对象
|
|
|
|
|
|
Setting setting = new Setting(); |
|
|
|
|
|
setting.setSettingKey("mp-weixin"); |
|
|
|
|
|
setting.setTenantId(tenantId); |
|
|
|
|
|
|
|
|
|
|
|
// 创建配置内容
|
|
|
|
|
|
Map<String, String> config = new HashMap<>(); |
|
|
|
|
|
config.put("appId", appId); |
|
|
|
|
|
config.put("appSecret", appSecret); |
|
|
|
|
|
setting.setContent(JSON.toJSONString(config)); |
|
|
|
|
|
setting.setComments("微信小程序配置"); |
|
|
|
|
|
setting.setSortNumber(1); |
|
|
|
|
|
|
|
|
|
|
|
// 保存配置
|
|
|
|
|
|
settingService.save(setting); |
|
|
|
|
|
|
|
|
|
|
|
return success("微信小程序配置创建成功", setting); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
return fail("创建配置失败: " + e.getMessage(), null); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Map<String, String> createSampleWxConfig() { |
|
|
|
|
|
Map<String, String> sample = new HashMap<>(); |
|
|
|
|
|
sample.put("appId", "wx_your_app_id_here"); |
|
|
|
|
|
sample.put("appSecret", "your_app_secret_here"); |
|
|
|
|
|
return sample; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|