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; }