From 6f68edd22fb4fbfc29d76acf891dd4f72d30dfd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Tue, 19 Aug 2025 19:43:45 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- quick_payment_check.sql | 33 ---- .../com/gxwebsoft/shop/OrderQueryTest.java | 182 ------------------ 2 files changed, 215 deletions(-) delete mode 100644 quick_payment_check.sql delete mode 100644 src/test/java/com/gxwebsoft/shop/OrderQueryTest.java diff --git a/quick_payment_check.sql b/quick_payment_check.sql deleted file mode 100644 index f67b301..0000000 --- a/quick_payment_check.sql +++ /dev/null @@ -1,33 +0,0 @@ --- 快速支付配置检查SQL --- 请在数据库中执行此查询 - -SELECT - '=== 支付配置检查 ===' as title, - tenant_id as '租户ID', - CASE - WHEN mch_id IS NULL OR mch_id = '' THEN '❌ 未配置' - ELSE CONCAT('✅ ', mch_id) - END as '商户号', - CASE - WHEN app_id IS NULL OR app_id = '' THEN '❌ 未配置' - ELSE CONCAT('✅ ', app_id) - END as '应用ID', - CASE - WHEN merchant_serial_number IS NULL OR merchant_serial_number = '' THEN '❌ 未配置' - ELSE '✅ 已配置' - END as '证书序列号', - CASE - WHEN api_key IS NULL OR api_key = '' THEN '❌ 未配置' - WHEN LENGTH(api_key) != 32 THEN CONCAT('❌ 长度错误(', LENGTH(api_key), '位)') - ELSE '✅ 已配置(32位)' - END as 'API密钥', - CASE - WHEN status = 1 THEN '✅ 已启用' - ELSE '❌ 未启用' - END as '状态' -FROM sys_payment -WHERE type = 0 -- 微信支付 -ORDER BY tenant_id; - --- 如果上面的查询没有返回结果,说明没有微信支付配置,请执行: --- SELECT COUNT(*) as '微信支付配置数量' FROM sys_payment WHERE type = 0; diff --git a/src/test/java/com/gxwebsoft/shop/OrderQueryTest.java b/src/test/java/com/gxwebsoft/shop/OrderQueryTest.java deleted file mode 100644 index a82a008..0000000 --- a/src/test/java/com/gxwebsoft/shop/OrderQueryTest.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.gxwebsoft.shop; - -import com.gxwebsoft.shop.entity.ShopOrder; -import com.gxwebsoft.shop.service.ShopOrderService; -import com.gxwebsoft.shop.service.OrderCancelService; -import com.gxwebsoft.shop.config.OrderConfigProperties; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; - -/** - * 订单查询测试 - */ -@Slf4j -@SpringBootTest -public class OrderQueryTest { - - @Autowired - private ShopOrderService shopOrderService; - - @Autowired - private OrderCancelService orderCancelService; - - @Autowired - private OrderConfigProperties orderConfig; - - @Test - public void testQuerySpecificOrder() { - String orderNo = "1957754623870595072"; - log.info("查询订单号: {}", orderNo); - - ShopOrder order = shopOrderService.getByOutTradeNo(orderNo); - if (order != null) { - log.info("订单信息:"); - log.info(" 订单ID: {}", order.getOrderId()); - log.info(" 订单号: {}", order.getOrderNo()); - log.info(" 订单状态: {} (0=待支付, 1=待发货, 2=已取消, 3=已完成)", order.getOrderStatus()); - log.info(" 支付状态: {} (false=未支付, true=已支付)", order.getPayStatus()); - log.info(" 创建时间: {}", order.getCreateTime()); - log.info(" 支付时间: {}", order.getPayTime()); - log.info(" 取消时间: {}", order.getCancelTime()); - log.info(" 租户ID: {}", order.getTenantId()); - log.info(" 订单金额: {}", order.getTotalPrice()); - log.info(" 取消原因: {}", order.getCancelReason()); - - // 检查是否符合自动取消条件 - checkAutoCancelConditions(order); - - // 计算什么时候会符合自动取消条件 - calculateCancelTime(order); - } else { - log.warn("未找到订单号为 {} 的订单", orderNo); - } - } - - private void checkAutoCancelConditions(ShopOrder order) { - log.info("\n=== 检查自动取消条件 ==="); - - // 1. 检查订单状态 - boolean statusOk = (order.getOrderStatus() != null && order.getOrderStatus() == 0); - log.info("1. 订单状态检查: {} (需要为0-待支付)", statusOk ? "✓通过" : "✗不通过"); - - // 2. 检查支付状态 - boolean payStatusOk = (order.getPayStatus() != null && !order.getPayStatus()); - log.info("2. 支付状态检查: {} (需要为false-未支付)", payStatusOk ? "✓通过" : "✗不通过"); - - // 3. 检查创建时间是否超时 - if (order.getCreateTime() != null) { - LocalDateTime createTime = order.getCreateTime(); - LocalDateTime now = LocalDateTime.now(); - - // 获取超时配置 - Integer timeoutMinutes = getTimeoutMinutes(order.getTenantId()); - LocalDateTime expireTime = createTime.plusMinutes(timeoutMinutes); - - boolean timeoutOk = now.isAfter(expireTime); - long minutesElapsed = java.time.Duration.between(createTime, now).toMinutes(); - - log.info("3. 超时检查:"); - log.info(" 创建时间: {}", createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - log.info(" 当前时间: {}", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - log.info(" 超时配置: {}分钟", timeoutMinutes); - log.info(" 过期时间: {}", expireTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - log.info(" 已过时间: {}分钟", minutesElapsed); - log.info(" 是否超时: {} (需要超过{}分钟)", timeoutOk ? "✓是" : "✗否", timeoutMinutes); - - // 4. 综合判断 - boolean shouldCancel = statusOk && payStatusOk && timeoutOk; - log.info("\n=== 综合判断 ==="); - log.info("是否符合自动取消条件: {}", shouldCancel ? "✓是" : "✗否"); - - if (shouldCancel) { - log.info("该订单符合自动取消任务的处理条件"); - } else { - log.info("该订单不符合自动取消任务的处理条件"); - if (!statusOk) log.info(" - 订单状态不是待支付状态"); - if (!payStatusOk) log.info(" - 订单已支付"); - if (!timeoutOk) log.info(" - 订单未超时"); - } - } else { - log.warn("订单创建时间为空,无法判断是否超时"); - } - } - - private void calculateCancelTime(ShopOrder order) { - log.info("\n=== 计算自动取消时间点 ==="); - - if (order.getCreateTime() == null) { - log.warn("订单创建时间为空,无法计算取消时间"); - return; - } - - // 获取超时配置 - Integer timeoutMinutes = getTimeoutMinutes(order.getTenantId()); - LocalDateTime createTime = order.getCreateTime(); - LocalDateTime cancelTime = createTime.plusMinutes(timeoutMinutes); - LocalDateTime now = LocalDateTime.now(); - - log.info("订单创建时间: {}", createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - log.info("当前时间: {}", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - log.info("超时配置: {}分钟", timeoutMinutes); - log.info("预计取消时间: {}", cancelTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - - if (now.isBefore(cancelTime)) { - long minutesLeft = java.time.Duration.between(now, cancelTime).toMinutes(); - long secondsLeft = java.time.Duration.between(now, cancelTime).getSeconds() % 60; - log.info("距离自动取消还有: {}分{}秒", minutesLeft, secondsLeft); - log.info("状态: ⏰ 等待中"); - } else { - long minutesOverdue = java.time.Duration.between(cancelTime, now).toMinutes(); - log.info("已超时: {}分钟", minutesOverdue); - log.info("状态: ⚠️ 应该被取消"); - - // 检查为什么没有被取消 - if (order.getPayStatus() != null && order.getPayStatus()) { - log.info("原因: 订单已支付,不会被自动取消"); - } else if (order.getOrderStatus() != null && order.getOrderStatus() != 0) { - log.info("原因: 订单状态不是待支付({}), 不会被自动取消", order.getOrderStatus()); - } else { - log.info("原因: 订单符合取消条件,可能定时任务尚未执行或执行失败"); - } - } - } - - private Integer getTimeoutMinutes(Integer tenantId) { - // 检查是否有租户特殊配置 - List tenantConfigs = orderConfig.getAutoCancel().getTenantConfigs(); - if (tenantConfigs != null) { - for (OrderConfigProperties.TenantCancelConfig config : tenantConfigs) { - if (config.isEnabled() && config.getTenantId().equals(tenantId)) { - return config.getTimeoutMinutes(); - } - } - } - - // 使用默认配置 - return orderConfig.getAutoCancel().getDefaultTimeoutMinutes(); - } - - @Test - public void testFindExpiredOrders() { - log.info("=== 测试查找超时订单 ==="); - - Integer defaultTimeout = orderConfig.getAutoCancel().getDefaultTimeoutMinutes(); - Integer batchSize = orderConfig.getAutoCancel().getBatchSize(); - - log.info("默认超时时间: {}分钟", defaultTimeout); - log.info("批量大小: {}", batchSize); - - List expiredOrders = orderCancelService.findExpiredUnpaidOrders(defaultTimeout, batchSize); - log.info("找到{}个超时订单", expiredOrders.size()); - - for (ShopOrder order : expiredOrders) { - log.info("超时订单: {} - 创建时间: {}", order.getOrderNo(), order.getCreateTime()); - } - } -} From ec2316625e5b348ff9dc41e32d9a2ff035c38b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Fri, 22 Aug 2025 14:58:52 +0800 Subject: [PATCH 2/3] =?UTF-8?q?refactor(wx-login):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E7=99=BB=E5=BD=95=E5=92=8C=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E7=A0=81=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化了微信小程序配置获取方式,支持跨租户查询 - 重构了 access_token 获取逻辑,提高代码复用性 -改进了小程序码生成方法,增加了错误处理和日志记录 - 调整了导航数据转换逻辑,统一字段命名 - 新增了微信小程序配置检查和创建示例配置的调试接口 --- debug_navigation_data.sql | 110 ++++++++ .../service/impl/CmsWebsiteServiceImpl.java | 1 + .../impl/CmsWebsiteServiceImplHelper.java | 17 +- .../system/controller/WxLoginController.java | 238 ++++++++++++++---- .../common/system/service/SettingService.java | 10 +- .../service/impl/SettingServiceImpl.java | 53 +++- .../service/impl/HouseInfoServiceImpl.java | 87 +++---- .../service/impl/ShopWebsiteServiceImpl.java | 4 +- .../java/com/gxwebsoft/shop/vo/MenuVo.java | 18 +- 9 files changed, 419 insertions(+), 119 deletions(-) create mode 100644 debug_navigation_data.sql diff --git a/debug_navigation_data.sql b/debug_navigation_data.sql new file mode 100644 index 0000000..35bb568 --- /dev/null +++ b/debug_navigation_data.sql @@ -0,0 +1,110 @@ +-- 检查导航数据的SQL查询 +-- 请将 {tenantId} 替换为实际的租户ID + +-- 1. 检查网站基本信息 +SELECT + website_id, + tenant_id, + tenant_name, + website_name, + running, + version, + expiration_time, + create_time +FROM cms_website +WHERE tenant_id = {tenantId}; + +-- 2. 检查顶部导航数据 +SELECT + navigation_id, + title, + parent_id, + model, + path, + icon, + color, + hide, + top, + bottom, + position, + sort_number, + target, + tenant_id, + deleted +FROM cms_navigation +WHERE tenant_id = {tenantId} + AND deleted = 0 + AND hide = 0 + AND (top = 0 OR position = 1) +ORDER BY sort_number ASC, position ASC, navigation_id ASC; + +-- 3. 检查底部导航数据 +SELECT + navigation_id, + title, + parent_id, + model, + path, + icon, + color, + hide, + top, + bottom, + position, + sort_number, + target, + tenant_id, + deleted +FROM cms_navigation +WHERE tenant_id = {tenantId} + AND deleted = 0 + AND hide = 0 + AND (bottom = 0 OR position = 2) +ORDER BY sort_number ASC, position ASC, navigation_id ASC; + +-- 4. 检查所有导航数据(包括隐藏的) +SELECT + navigation_id, + title, + parent_id, + model, + path, + icon, + color, + hide, + top, + bottom, + position, + sort_number, + target, + tenant_id, + deleted, + status +FROM cms_navigation +WHERE tenant_id = {tenantId} +ORDER BY deleted ASC, hide ASC, sort_number ASC; + +-- 5. 统计导航数据 +SELECT + '总导航数' as type, + COUNT(*) as count +FROM cms_navigation +WHERE tenant_id = {tenantId} +UNION ALL +SELECT + '有效导航数' as type, + COUNT(*) as count +FROM cms_navigation +WHERE tenant_id = {tenantId} AND deleted = 0 +UNION ALL +SELECT + '顶部导航数' as type, + COUNT(*) as count +FROM cms_navigation +WHERE tenant_id = {tenantId} AND deleted = 0 AND hide = 0 AND (top = 0 OR position = 1) +UNION ALL +SELECT + '底部导航数' as type, + COUNT(*) as count +FROM cms_navigation +WHERE tenant_id = {tenantId} AND deleted = 0 AND hide = 0 AND (bottom = 0 OR position = 2); diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/CmsWebsiteServiceImpl.java b/src/main/java/com/gxwebsoft/cms/service/impl/CmsWebsiteServiceImpl.java index 65c9ade..fbb60db 100644 --- a/src/main/java/com/gxwebsoft/cms/service/impl/CmsWebsiteServiceImpl.java +++ b/src/main/java/com/gxwebsoft/cms/service/impl/CmsWebsiteServiceImpl.java @@ -334,6 +334,7 @@ public class CmsWebsiteServiceImpl extends ServiceImpl convertNavigationToVO(List navigations) { if (navigations == null) { @@ -127,18 +128,18 @@ public class CmsWebsiteServiceImplHelper { return navigations.stream().map(nav -> { MenuVo navVO = new MenuVo(); - navVO.setNavigationId(nav.getNavigationId()); - navVO.setNavigationName(nav.getTitle()); // 修复:使用 title 字段 - navVO.setNavigationUrl(nav.getPath()); // 修复:使用 path 字段 - navVO.setNavigationIcon(nav.getIcon()); // 修复:使用 icon 字段 - navVO.setNavigationColor(nav.getColor()); // 修复:使用 color 字段 + navVO.setId(nav.getNavigationId()); + navVO.setName(nav.getTitle()); + navVO.setPath(nav.getPath()); + navVO.setIcon(nav.getIcon()); + navVO.setColor(nav.getColor()); navVO.setParentId(nav.getParentId()); - navVO.setSort(nav.getSortNumber()); // 修复:使用 sortNumber 字段 + navVO.setSort(nav.getSortNumber()); navVO.setHide(nav.getHide()); navVO.setTop(nav.getTop()); - // 安全转换 target 字段:将字符串值映射为整数 + navVO.setPath(nav.getPath()); navVO.setTarget(convertTargetToInteger(nav.getTarget())); - navVO.setNavigationType(nav.getModel()); // 修复:使用 model 字段 + navVO.setType(nav.getModel()); // 递归处理子导航 if (nav.getChildren() != null) { 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 2d441a9..1263b25 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java @@ -38,6 +38,7 @@ import java.io.File; import java.io.IOException; import java.time.Instant; import java.util.HashMap; +import java.util.Map; import java.util.concurrent.TimeUnit; import static com.gxwebsoft.common.core.constants.PlatformConstants.MP_WEIXIN; @@ -224,7 +225,7 @@ public class WxLoginController extends BaseController { // 获取openid private JSONObject getOpenIdByCode(UserParam userParam) { // 获取微信小程序配置信息 - JSONObject setting = settingService.getBySettingKey("mp-weixin"); + JSONObject setting = settingService.getBySettingKey("mp-weixin",getTenantId()); // 获取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"; // 执行get请求 @@ -277,24 +278,34 @@ public class WxLoginController extends BaseController { * 获取接口调用凭据AccessToken * ... */ - 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) { throw new BusinessException("请先配置小程序"); } + // 从缓存获取access_token String value = redisTemplate.opsForValue().get(key); if (value != null) { // 解析access_token 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"; // 组装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请求 String result = HttpUtil.get(url); // 解析access_token @@ -321,7 +332,7 @@ public class WxLoginController extends BaseController { // 请求微信接口获取openid String apiUrl = "https://api.weixin.qq.com/sns/jscode2session"; final HashMap 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 appSecret = setting.getString("appSecret"); map.put("appid", appId); @@ -349,7 +360,7 @@ public class WxLoginController extends BaseController { String apiUrl = "https://api.weixin.qq.com/sns/jscode2session"; final HashMap 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 appSecret = setting.getString("appSecret"); map.put("appid", appId); @@ -408,29 +419,62 @@ public class WxLoginController extends BaseController { } @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(); - final HashMap map = new HashMap<>(); - map.put("scene", orderNo); - map.put("page", "pages/index/index"); - map.put("env_version", "develop"); - String jsonBody = JSON.toJSONString(map); - System.out.println("请求的 JSON body = " + jsonBody); - // 获取图片 Buffer - byte[] qrCode = HttpRequest.post(apiUrl) - .body(JSON.toJSONString(map)) - .execute().bodyBytes(); + @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 map = new HashMap<>(); + map.put("scene", scene); + map.put("page", "pages/index/index"); + map.put("env_version", "trial"); + + String jsonBody = JSON.toJSONString(map); + System.out.println("请求的 JSON body = " + jsonBody); + + // 获取微信 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.setHeader("Cache-Control", "no-cache"); + response.setHeader("Content-Disposition", "inline; filename=qrcode.png"); + + // 输出图片 + response.getOutputStream().write(responseBytes); + System.out.println("二维码生成成功,大小: " + responseBytes.length + " bytes"); - // 设置响应头 - response.setContentType("image/png"); - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Content-Disposition", "inline; filename=encrypted_qrcode.png"); + } catch (Exception e) { + System.err.println("生成二维码失败: " + e.getMessage()); + e.printStackTrace(); - // 输出图片 - response.getOutputStream().write(qrCode); - System.out.println("response = " + response); + // 返回错误信息 + response.setContentType("application/json;charset=UTF-8"); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.getWriter().write("{\"error\":\"生成二维码失败: " + e.getMessage() + "\"}"); + } } @Operation(summary = "获取微信小程序码-用户ID") @@ -474,31 +518,48 @@ public class WxLoginController extends BaseController { /** 获取/刷新 access_token */ public String getLocalAccessToken() throws IOException { - long now = Instant.now().getEpochSecond(); - String key ="AccessToken:Local:10550"; - if (redisUtil.get(key) != null && now < tokenExpireEpoch - 60) { - return redisUtil.get(key); - } - HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/cgi-bin/token") - .newBuilder() - .addQueryParameter("grant_type", "client_credential") - .addQueryParameter("appid", "wx51962d6ac21f2ed2") - .addQueryParameter("secret", "d821f98de8a6c1ba7bc7e0ee84bcbc8e") - .build(); - Request req = new Request.Builder().url(url).get().build(); - try (Response resp = http.newCall(req).execute()) { - String body = resp.body().string(); - JsonNode json = om.readTree(body); - if (json.has("access_token")) { - String token = json.get("access_token").asText(); - long expiresIn = json.get("expires_in").asInt(7200); - redisUtil.set(key,token,expiresIn,TimeUnit.SECONDS); - tokenExpireEpoch = now + expiresIn; - return token; - } else { - throw new IOException("Get access_token failed: " + body); + long now = Instant.now().getEpochSecond(); + String key = "AccessToken:Local:" + getTenantId(); + + if (redisUtil.get(key) != null && now < tokenExpireEpoch - 60) { + 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") + .newBuilder() + .addQueryParameter("grant_type", "client_credential") + .addQueryParameter("appid", appId) + .addQueryParameter("secret", appSecret) + .build(); + + Request req = new Request.Builder().url(url).get().build(); + try (Response resp = http.newCall(req).execute()) { + String body = resp.body().string(); + JsonNode json = om.readTree(body); + if (json.has("access_token")) { + String token = json.get("access_token").asText(); + long expiresIn = json.get("expires_in").asInt(7200); + redisUtil.set(key, token, expiresIn, TimeUnit.SECONDS); + tokenExpireEpoch = now + expiresIn; + return token; + } else { + throw new IOException("Get access_token failed: " + body); + } } - } } /** @@ -508,5 +569,74 @@ public class WxLoginController extends BaseController { return config.getUploadPath() + "file/"; } + @Operation(summary = "调试:检查微信小程序配置") + @GetMapping("/debug/checkWxConfig") + public ApiResult debugCheckWxConfig() { + Integer tenantId = getTenantId(); + Map 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 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 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 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 createSampleWxConfig() { + Map sample = new HashMap<>(); + sample.put("appId", "wx_your_app_id_here"); + sample.put("appSecret", "your_app_secret_here"); + return sample; + } } diff --git a/src/main/java/com/gxwebsoft/common/system/service/SettingService.java b/src/main/java/com/gxwebsoft/common/system/service/SettingService.java index 9432620..1478c90 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/SettingService.java +++ b/src/main/java/com/gxwebsoft/common/system/service/SettingService.java @@ -46,7 +46,15 @@ public interface SettingService extends IService { * @param key key * @return Setting */ - JSONObject getBySettingKey(String key); + JSONObject getBySettingKey(String key,Integer tenantId); + + /** + * 跨租户获取设置内容 + * @param key 设置键 + * @param tenantId 租户ID + * @return JSONObject + */ + JSONObject getBySettingKeyIgnoreTenant(String key, Integer tenantId); Setting getData(String settingKey); diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java index baf97ec..5a2485d 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java @@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.common.core.annotation.IgnoreTenant; import com.gxwebsoft.common.core.config.ConfigProperties; import com.gxwebsoft.common.core.exception.BusinessException; import com.gxwebsoft.common.core.web.PageParam; @@ -16,6 +17,7 @@ import com.gxwebsoft.common.system.service.SettingService; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAConfig; import com.wechat.pay.java.service.payments.jsapi.JsapiService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; @@ -44,8 +46,10 @@ public class SettingServiceImpl extends ServiceImpl impl @Value("${spring.profiles.active:prod}") private String activeProfile; + @Autowired + private SettingService settingService; - @Override + @Override public PageResult pageRel(SettingParam param) { PageParam page = new PageParam<>(param); //page.setDefaultOrder("create_time desc"); @@ -70,7 +74,10 @@ public class SettingServiceImpl extends ServiceImpl impl } @Override - public JSONObject getBySettingKey(String key) { + public JSONObject getBySettingKey(String key, Integer tenantId) { + System.out.println("tenantId = " + tenantId); + final JSONObject settingKey = settingService.getBySettingKey("setting_key", tenantId); + System.out.println("settingKey = " + settingKey); Setting setting = this.getOne(new QueryWrapper().eq("setting_key", key), false); System.out.println("setting1 = " + setting); if(setting == null){ @@ -99,6 +106,48 @@ public class SettingServiceImpl extends ServiceImpl impl return JSON.parseObject(setting.getContent()); } + @Override + @IgnoreTenant("跨租户获取指定租户的设置配置") + public JSONObject getBySettingKeyIgnoreTenant(String key, Integer tenantId) { + System.out.println("跨租户查询设置 - key: " + key + ", tenantId: " + tenantId); + final List list = list(new LambdaQueryWrapper().eq(Setting::getTenantId, tenantId)); + System.out.println("list = " + list); + + // 使用跨租户查询,指定租户ID + Setting setting = this.getOne(new QueryWrapper() + .eq("setting_key", key) + .eq("tenant_id", tenantId), false); + + System.out.println("跨租户查询结果: " + setting); + + if(setting == null){ + if ("mp-weixin".equals(key)) { + throw new BusinessException("租户 " + tenantId + " 的小程序未配置,请先在系统设置中配置微信小程序信息"); + } + if ("payment".equals(key)) { + throw new BusinessException("租户 " + tenantId + " 的支付未配置"); + } + if ("sms".equals(key)) { + throw new BusinessException("租户 " + tenantId + " 的短信未配置"); + } + if ("wx-work".equals(key)){ + throw new BusinessException("租户 " + tenantId + " 的企业微信未配置"); + } + if ("setting".equals(key)) { + throw new BusinessException("租户 " + tenantId + " 的基本信息未配置"); + } + if ("wx-official".equals(key)) { + throw new BusinessException("租户 " + tenantId + " 的微信公众号未配置"); + } + if ("printer".equals(key)) { + throw new BusinessException("租户 " + tenantId + " 的打印机未配置"); + } + throw new BusinessException("租户 " + tenantId + " 的配置项 " + key + " 未找到"); + } + + return JSON.parseObject(setting.getContent()); + } + @Override public Setting getData(String settingKey) { return query().eq("setting_key", settingKey).one(); diff --git a/src/main/java/com/gxwebsoft/house/service/impl/HouseInfoServiceImpl.java b/src/main/java/com/gxwebsoft/house/service/impl/HouseInfoServiceImpl.java index fb81d24..9167777 100644 --- a/src/main/java/com/gxwebsoft/house/service/impl/HouseInfoServiceImpl.java +++ b/src/main/java/com/gxwebsoft/house/service/impl/HouseInfoServiceImpl.java @@ -16,6 +16,7 @@ import com.gxwebsoft.common.core.config.ConfigProperties; import com.gxwebsoft.common.core.utils.CommonUtil; import com.gxwebsoft.common.core.utils.ImageUtil; import com.gxwebsoft.common.core.utils.RedisUtil; +import com.gxwebsoft.common.system.controller.WxLoginController; import com.gxwebsoft.common.system.service.SettingService; import com.gxwebsoft.house.mapper.HouseInfoMapper; import com.gxwebsoft.house.service.HouseInfoService; @@ -57,6 +58,8 @@ public class HouseInfoServiceImpl extends ServiceImpl map = new HashMap<>(); // 设置小程序页面路径:sub_pages/house/detail/ + houseId map.put("path", "sub_pages/house/detail/?houseId=" + houseId); @@ -269,47 +272,47 @@ public class HouseInfoServiceImpl extends ServiceImpl children; } From a9758b9d3add643e9ec7f91d57aceb9f63591829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Fri, 22 Aug 2025 15:52:55 +0800 Subject: [PATCH 3/3] =?UTF-8?q?refactor(shop):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=95=86=E5=9F=8E=E4=BF=A1=E6=81=AF=E8=8E=B7=E5=8F=96=E5=92=8C?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 ShopMainController 中的 debug 日志 - 修改 ShopWebsiteServiceImpl 中的缓存键前缀为 PascalCase - 删除 ShopWebsiteServiceImpl 中的冗余打印语句 - 删除整个 debug_navigation_data.sql 文件 --- debug_navigation_data.sql | 110 ------------------ .../shop/controller/ShopMainController.java | 1 + .../service/impl/ShopWebsiteServiceImpl.java | 3 +- 3 files changed, 2 insertions(+), 112 deletions(-) delete mode 100644 debug_navigation_data.sql diff --git a/debug_navigation_data.sql b/debug_navigation_data.sql deleted file mode 100644 index 35bb568..0000000 --- a/debug_navigation_data.sql +++ /dev/null @@ -1,110 +0,0 @@ --- 检查导航数据的SQL查询 --- 请将 {tenantId} 替换为实际的租户ID - --- 1. 检查网站基本信息 -SELECT - website_id, - tenant_id, - tenant_name, - website_name, - running, - version, - expiration_time, - create_time -FROM cms_website -WHERE tenant_id = {tenantId}; - --- 2. 检查顶部导航数据 -SELECT - navigation_id, - title, - parent_id, - model, - path, - icon, - color, - hide, - top, - bottom, - position, - sort_number, - target, - tenant_id, - deleted -FROM cms_navigation -WHERE tenant_id = {tenantId} - AND deleted = 0 - AND hide = 0 - AND (top = 0 OR position = 1) -ORDER BY sort_number ASC, position ASC, navigation_id ASC; - --- 3. 检查底部导航数据 -SELECT - navigation_id, - title, - parent_id, - model, - path, - icon, - color, - hide, - top, - bottom, - position, - sort_number, - target, - tenant_id, - deleted -FROM cms_navigation -WHERE tenant_id = {tenantId} - AND deleted = 0 - AND hide = 0 - AND (bottom = 0 OR position = 2) -ORDER BY sort_number ASC, position ASC, navigation_id ASC; - --- 4. 检查所有导航数据(包括隐藏的) -SELECT - navigation_id, - title, - parent_id, - model, - path, - icon, - color, - hide, - top, - bottom, - position, - sort_number, - target, - tenant_id, - deleted, - status -FROM cms_navigation -WHERE tenant_id = {tenantId} -ORDER BY deleted ASC, hide ASC, sort_number ASC; - --- 5. 统计导航数据 -SELECT - '总导航数' as type, - COUNT(*) as count -FROM cms_navigation -WHERE tenant_id = {tenantId} -UNION ALL -SELECT - '有效导航数' as type, - COUNT(*) as count -FROM cms_navigation -WHERE tenant_id = {tenantId} AND deleted = 0 -UNION ALL -SELECT - '顶部导航数' as type, - COUNT(*) as count -FROM cms_navigation -WHERE tenant_id = {tenantId} AND deleted = 0 AND hide = 0 AND (top = 0 OR position = 1) -UNION ALL -SELECT - '底部导航数' as type, - COUNT(*) as count -FROM cms_navigation -WHERE tenant_id = {tenantId} AND deleted = 0 AND hide = 0 AND (bottom = 0 OR position = 2); diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopMainController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopMainController.java index 1cf862c..5b96b22 100644 --- a/src/main/java/com/gxwebsoft/shop/controller/ShopMainController.java +++ b/src/main/java/com/gxwebsoft/shop/controller/ShopMainController.java @@ -42,6 +42,7 @@ public class ShopMainController extends BaseController { try { // 使用专门的商城信息获取方法 ShopVo shopVo = shopWebsiteService.getShopInfo(tenantId); +// log.debug("获取商城信息成功: {}", shopVo); return success(shopVo); } catch (IllegalArgumentException e) { return fail(e.getMessage(), null); diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopWebsiteServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopWebsiteServiceImpl.java index a9ec410..9eda00b 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/ShopWebsiteServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/ShopWebsiteServiceImpl.java @@ -32,7 +32,7 @@ public class ShopWebsiteServiceImpl implements ShopWebsiteService { /** * 商城信息缓存键前缀 */ - private static final String SHOP_INFO_KEY_PREFIX = "shop_info:"; + private static final String SHOP_INFO_KEY_PREFIX = "ShopInfo:"; @Override public ShopVo getShopInfo(Integer tenantId) { @@ -55,7 +55,6 @@ public class ShopWebsiteServiceImpl implements ShopWebsiteService { // 直接调用 CMS 服务获取站点信息,然后使用商城专用缓存 ShopVo shopVO = cmsWebsiteService.getSiteInfo(tenantId); - System.out.println("shopVO = " + shopVO); if (shopVO == null) { throw new RuntimeException("请先创建商城"); }