From 96cb0d6cc3ff342648b212bc69e7d34ac66ea183 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Sat, 19 Jul 2025 15:35:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E5=B0=8F=E7=A8=8B=E5=BA=8F=20URL=20SCHEME=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/common/feignService/ShopService.java | 2 +- .../src/main/resources/application.yml | 3 +- .../wechat/controller/WxQrCodeContorller.java | 12 +- .../wechat/service/WxURLSchemeService.java | 17 +++ .../service/impl/WxURLSchemeServiceImpl.java | 126 ++++++++++++++++++ 5 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/WxURLSchemeService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/impl/WxURLSchemeServiceImpl.java diff --git a/mall-common/src/main/java/com/suisung/mall/common/feignService/ShopService.java b/mall-common/src/main/java/com/suisung/mall/common/feignService/ShopService.java index 60287686..d4630bd6 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/feignService/ShopService.java +++ b/mall-common/src/main/java/com/suisung/mall/common/feignService/ShopService.java @@ -266,7 +266,7 @@ public interface ShopService { @PostMapping(value = "/admin/shop/mq-message/sendMqMsg") CommonResult sendMqMsg(@RequestParam(name = "exchange") String exchange, @RequestParam(name = "routing_key") String routing_key, @RequestParam(name = "data") Object data); - @PostMapping(value = "/admin/wxqrcode/getWxQrCode") + @PostMapping(value = "/admin/shop/wxqrcode/getWxQrCode") CommonResult getWxQrCode(@RequestParam(name = "path") String path, @RequestBody Map params); @GetMapping(value = "/admin/shop/shopController/getWaitPayNum") diff --git a/mall-gateway/src/main/resources/application.yml b/mall-gateway/src/main/resources/application.yml index 1a7e1bd9..f0235133 100644 --- a/mall-gateway/src/main/resources/application.yml +++ b/mall-gateway/src/main/resources/application.yml @@ -63,7 +63,6 @@ secure: - "/**/*.png" - "/**/*.ico" - "/webjars/springfox-swagger-ui/**" - # - "/actuator/**" - "/mall-auth/oauth/token" - "/mall-auth/rsa/publicKey" - "/admin/account/account-user-base/register" @@ -72,7 +71,6 @@ secure: - "/shop/static/**" - "/admin/account/open/**" - "/admin/account/account-user-base/doLogin" - #- "/mobile/pay/index/lkl_wxPay_notify_url" #拉卡拉微信支付回调 - "/mobile/pay/index/notify_url" - "/mobile/pay/index/return_url" - "/admin/pay/pay-user-resource/userInfoImport" @@ -91,6 +89,7 @@ secure: - "/shop/sync/third/**" - "/esProduct/**" - "/admin/oss/upload/**" + - "/admin/shop/wxqrcode/common/wxurlscheme" - "/mobile/**/**/test/case" - "/**/**/testcase" universal: diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/controller/WxQrCodeContorller.java b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/controller/WxQrCodeContorller.java index a77097ba..48d5c1fd 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/controller/WxQrCodeContorller.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/controller/WxQrCodeContorller.java @@ -7,6 +7,7 @@ import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.utils.I18nUtil; import com.suisung.mall.shop.wechat.service.WxQrCodeService; +import com.suisung.mall.shop.wechat.service.WxURLSchemeService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; @@ -16,12 +17,15 @@ import java.util.Map; @Api(tags = "微信小程序码服务控制器") @RestController -@RequestMapping("/admin/wxqrcode") +@RequestMapping("/admin/shop/wxqrcode") public class WxQrCodeContorller { @Autowired private WxQrCodeService wxQrCodeService; + @Autowired + private WxURLSchemeService wxURLSchemeService; + @ApiOperation(value = "获取小程序码", notes = "获取小程序码") @RequestMapping(value = "/getWxQrCode", method = RequestMethod.POST) public CommonResult getWxQrCode(@RequestParam(value = "preparedUrl") String preparedUrl, @@ -40,4 +44,10 @@ public class WxQrCodeContorller { return wxQrCodeService.genUnlimitedWxQrCode2(params.getStr("page"), params.getStr("scene")); } + @ApiOperation(value = "生成小发同城小程序首页跳转 URL Scheme", notes = "生成小发同城小程序首页跳转 URL Scheme") + @PostMapping(value = "/common/wxurlscheme") + public CommonResult generateCommonWxUrlScheme() { + return wxURLSchemeService.generateCommonWxUrlScheme(); + } + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/WxURLSchemeService.java b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/WxURLSchemeService.java new file mode 100644 index 00000000..73c47ecb --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/WxURLSchemeService.java @@ -0,0 +1,17 @@ +package com.suisung.mall.shop.wechat.service; + +import com.suisung.mall.common.api.CommonResult; + +/** + * 微信小程序 URL Scheme 跳转链接 + */ +public interface WxURLSchemeService { + + /** + * 生成小程序公共 URL Scheme 跳转链接, 跳转到小发同城小程序首页 (加 redis 缓存) + * 官方参考链接:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-scheme/generateScheme.html + * + * @return + */ + CommonResult generateCommonWxUrlScheme(); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/impl/WxURLSchemeServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/impl/WxURLSchemeServiceImpl.java new file mode 100644 index 00000000..6408090b --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/wechat/service/impl/WxURLSchemeServiceImpl.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.wechat.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.utils.RestTemplateHttpUtil; +import com.suisung.mall.core.web.service.RedisService; +import com.suisung.mall.shop.wechat.service.WxURLSchemeService; +import com.suisung.mall.shop.wechat.utils.WxUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Service +public class WxURLSchemeServiceImpl implements WxURLSchemeService { + private final WxUtil wxUtil; + + @Resource + private RedisService redisService; + + public WxURLSchemeServiceImpl(WxUtil wxUtil) { + this.wxUtil = wxUtil; + } + + /** + * 生成小程序公共 URL Scheme 跳转链接 + * === 功能说明 === + * 1. 优先从Redis缓存获取Scheme + * 2. 无缓存时调用微信API生成 + * 3. 结果缓存30天 + * === 技术实现 === + * - 使用Redis缓存热点数据 + * - 使用RestTemplate调用微信API + * - 采用TimeUnit计算时间间隔 + * + * @return CommonResult 包含openlink的JSON对象 + */ + @Override + public CommonResult generateCommonWxUrlScheme() { + // 常量定义 + final String REDIS_KEY = "rdsCommonWxUrlScheme"; + final long EXPIRE_DAYS = 30; + + // === 第一阶段:尝试从Redis获取缓存 === + Object cachedScheme = redisService.get(REDIS_KEY); + if (cachedScheme != null) { + JSONObject result = new JSONObject(); + result.put("openlink", cachedScheme); + return CommonResult.success(result); + } + + // === 第二阶段:构建微信API请求 === + String accessToken; + try { + accessToken = wxUtil.getAccessToken(); // 获取微信access_token + } catch (Exception e) { + log.error("获取微信access_token失败", e); + return CommonResult.failed("系统繁忙,请稍后重试"); + } + + String apiUrl = "https://api.weixin.qq.com/wxa/generatescheme?access_token=" + accessToken; + + // 请求头设置 + Map headers = new HashMap<>(); + headers.put("Accept", "application/json"); + headers.put("Content-Type", "application/json; charset=utf-8"); + + // 请求体构建 + JSONObject requestBody = new JSONObject(); + JSONObject jumpWxa = new JSONObject(); + jumpWxa.put("path", "pages/index/index"); // 固定跳转路径 + requestBody.put("jump_wxa", jumpWxa); + requestBody.put("is_expire", true); + requestBody.put("expire_type", 0); + long expireSeconds = TimeUnit.DAYS.toSeconds(EXPIRE_DAYS); // 30天有效期 + requestBody.put("expire_time", System.currentTimeMillis() / 1000 + expireSeconds); + + // === 第三阶段:调用微信API === + JSONObject wechatResponse; + try { + wechatResponse = RestTemplateHttpUtil.sendPost(apiUrl, headers, requestBody, JSONObject.class); + } catch (Exception e) { + log.error("调用微信生成URL Scheme API失败", e); + return CommonResult.failed("系统繁忙,请稍后重试"); + } + + // === 第四阶段:处理微信响应 === + if (wechatResponse == null || wechatResponse.getInt("errcode") != 0) { + String errorMsg = wechatResponse != null ? + wechatResponse.getStr("errmsg") : "微信接口无响应"; + log.error("微信接口返回错误:{}", errorMsg); + return CommonResult.failed(errorMsg); + } + + String openLink = wechatResponse.getStr("openlink"); + if (StrUtil.isBlank(openLink)) { + log.error("微信返回的openlink为空"); + return CommonResult.failed("微信返回数据异常"); + } + + // === 第五阶段:缓存结果 === + try { + redisService.set(REDIS_KEY, openLink, expireSeconds); + } catch (Exception e) { + log.error("Redis缓存失败", e); + // 缓存失败不影响主流程 + } + + JSONObject result = new JSONObject(); + result.put("openlink", openLink); + return CommonResult.success(result); + } +}