Commit 6f757e5297d7e77f9906b7265cb3545ddb41a016

Authored by YunaiV
1 parent d2562750

saas:支持社交应用的多租户配置(mp)

Showing 13 changed files with 216 additions and 46 deletions
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
@@ -59,3 +59,8 @@ tenant-id: {{appTenentId}} @@ -59,3 +59,8 @@ tenant-id: {{appTenentId}}
59 POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70 59 POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70
60 Content-Type: application/json 60 Content-Type: application/json
61 tenant-id: {{appTenentId}} 61 tenant-id: {{appTenentId}}
  62 +
  63 +### 请求 /auth/create-weixin-jsapi-signature 接口 => 成功
  64 +POST {{appApi}}/member/auth/create-weixin-jsapi-signature?url=http://www.iocoder.cn
  65 +Authorization: Bearer {{appToken}}
  66 +tenant-id: {{appTenentId}}
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
1 package cn.iocoder.yudao.module.member.controller.app.auth; 1 package cn.iocoder.yudao.module.member.controller.app.auth;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 6 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
6 import cn.iocoder.yudao.framework.security.config.SecurityProperties; 7 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
7 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 8 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
8 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; 9 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
  10 +import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
9 import cn.iocoder.yudao.module.member.service.auth.MemberAuthService; 11 import cn.iocoder.yudao.module.member.service.auth.MemberAuthService;
  12 +import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
  13 +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
10 import io.swagger.v3.oas.annotations.Operation; 14 import io.swagger.v3.oas.annotations.Operation;
11 import io.swagger.v3.oas.annotations.Parameter; 15 import io.swagger.v3.oas.annotations.Parameter;
12 import io.swagger.v3.oas.annotations.Parameters; 16 import io.swagger.v3.oas.annotations.Parameters;
@@ -34,6 +38,9 @@ public class AppAuthController { @@ -34,6 +38,9 @@ public class AppAuthController {
34 private MemberAuthService authService; 38 private MemberAuthService authService;
35 39
36 @Resource 40 @Resource
  41 + private SocialClientApi socialClientApi;
  42 +
  43 + @Resource
37 private SecurityProperties securityProperties; 44 private SecurityProperties securityProperties;
38 45
39 @PostMapping("/login") 46 @PostMapping("/login")
@@ -109,4 +116,13 @@ public class AppAuthController { @@ -109,4 +116,13 @@ public class AppAuthController {
109 return success(authService.weixinMiniAppLogin(reqVO)); 116 return success(authService.weixinMiniAppLogin(reqVO));
110 } 117 }
111 118
  119 + @PostMapping("/create-weixin-jsapi-signature")
  120 + @Operation(summary = "创建微信 JS SDK 初始化所需的签名",
  121 + description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档")
  122 + public CommonResult<SocialWxJsapiSignatureRespDTO> createWeixinMpJsapiSignature(@RequestParam("url") String url) {
  123 + SocialWxJsapiSignatureRespDTO signature = socialClientApi.createWxMpJsapiSignature(
  124 + UserTypeEnum.MEMBER.getValue(), url);
  125 + return success(AuthConvert.INSTANCE.convert(signature));
  126 + }
  127 +
112 } 128 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AuthWeixinJsapiSignatureRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.member.controller.app.auth.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Builder;
  6 +import lombok.Data;
  7 +import lombok.NoArgsConstructor;
  8 +
  9 +@Schema(description = "用户 APP - 微信公众号 JSAPI 签名 Response VO")
  10 +@Data
  11 +@NoArgsConstructor
  12 +@AllArgsConstructor
  13 +@Builder
  14 +public class AuthWeixinJsapiSignatureRespVO {
  15 +
  16 + @Schema(description = "微信公众号的 appId", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
  17 + private String appId;
  18 +
  19 + @Schema(description = "匿名串", requiredMode = Schema.RequiredMode.REQUIRED, example = "world")
  20 + private String nonceStr;
  21 +
  22 + @Schema(description = "时间戳", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  23 + private Long timestamp;
  24 +
  25 + @Schema(description = "URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")
  26 + private String url;
  27 +
  28 + @Schema(description = "签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "阿巴阿巴")
  29 + private String signature;
  30 +
  31 +}
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
9 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; 9 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
10 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 10 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
11 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; 11 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
  12 +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
12 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; 13 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
13 import org.mapstruct.Mapper; 14 import org.mapstruct.Mapper;
14 import org.mapstruct.factory.Mappers; 15 import org.mapstruct.factory.Mappers;
@@ -29,4 +30,6 @@ public interface AuthConvert { @@ -29,4 +30,6 @@ public interface AuthConvert {
29 30
30 SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean); 31 SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean);
31 32
  33 + SocialWxJsapiSignatureRespDTO convert(SocialWxJsapiSignatureRespDTO bean);
  34 +
32 } 35 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java
1 package cn.iocoder.yudao.module.system.api.social; 1 package cn.iocoder.yudao.module.system.api.social;
2 2
  3 +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
3 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; 4 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
4 5
5 /** 6 /**
@@ -19,4 +20,13 @@ public interface SocialClientApi { @@ -19,4 +20,13 @@ public interface SocialClientApi {
19 */ 20 */
20 String getAuthorizeUrl(Integer type, Integer userType, String redirectUri); 21 String getAuthorizeUrl(Integer type, Integer userType, String redirectUri);
21 22
  23 + /**
  24 + * 创建微信 JS SDK 初始化所需的签名
  25 + *
  26 + * @param userType 用户类型
  27 + * @param url 访问的 URL 地址
  28 + * @return 签名
  29 + */
  30 + SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url);
  31 +
22 } 32 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.social.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 微信公众号 JSAPI 签名 Response DTO
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +@Data
  11 +public class SocialWxJsapiSignatureRespDTO {
  12 +
  13 + /**
  14 + * 微信公众号的 appId
  15 + */
  16 + private String appId;
  17 + /**
  18 + * 匿名串
  19 + */
  20 + private String nonceStr;
  21 + /**
  22 + * 时间戳
  23 + */
  24 + private Long timestamp;
  25 + /**
  26 + * URL
  27 + */
  28 + private String url;
  29 + /**
  30 + * 签名
  31 + */
  32 + private String signature;
  33 +
  34 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java
1 package cn.iocoder.yudao.module.system.api.social; 1 package cn.iocoder.yudao.module.system.api.social;
2 2
  3 +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
  4 +import cn.iocoder.yudao.module.system.convert.social.SocialClientConvert;
3 import cn.iocoder.yudao.module.system.service.social.SocialClientService; 5 import cn.iocoder.yudao.module.system.service.social.SocialClientService;
  6 +import me.chanjar.weixin.common.bean.WxJsapiSignature;
4 import org.springframework.stereotype.Service; 7 import org.springframework.stereotype.Service;
5 import org.springframework.validation.annotation.Validated; 8 import org.springframework.validation.annotation.Validated;
6 9
@@ -23,4 +26,10 @@ public class SocialClientApiImpl implements SocialClientApi { @@ -23,4 +26,10 @@ public class SocialClientApiImpl implements SocialClientApi {
23 return socialClientService.getAuthorizeUrl(type, userType, redirectUri); 26 return socialClientService.getAuthorizeUrl(type, userType, redirectUri);
24 } 27 }
25 28
  29 + @Override
  30 + public SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url) {
  31 + WxJsapiSignature signature = socialClientService.createWxMpJsapiSignature(userType, url);
  32 + return SocialClientConvert.INSTANCE.convert(signature);
  33 + }
  34 +
26 } 35 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/package-info.java deleted
1 -/**  
2 - * 占位,避免 package 无法提交到 Git 仓库  
3 - */  
4 -package cn.iocoder.yudao.module.system.controller.app;  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.http deleted
1 -### 请求 /login 接口 => 成功  
2 -POST {{appApi}}/system/wx-mp/create-jsapi-signature?url=http://www.iocoder.cn  
3 -Authorization: Bearer {{appToken}}  
4 -tenant-id: {{appTenentId}}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.java deleted
1 -package cn.iocoder.yudao.module.system.controller.app.weixin;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
4 -import io.swagger.v3.oas.annotations.tags.Tag;  
5 -import io.swagger.v3.oas.annotations.Operation;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import me.chanjar.weixin.common.bean.WxJsapiSignature;  
8 -import me.chanjar.weixin.common.error.WxErrorException;  
9 -import me.chanjar.weixin.mp.api.WxMpService;  
10 -import org.springframework.validation.annotation.Validated;  
11 -import org.springframework.web.bind.annotation.PostMapping;  
12 -import org.springframework.web.bind.annotation.RequestMapping;  
13 -import org.springframework.web.bind.annotation.RequestParam;  
14 -import org.springframework.web.bind.annotation.RestController;  
15 -  
16 -import javax.annotation.Resource;  
17 -  
18 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
19 -  
20 -@Tag(name = "微信公众号")  
21 -@RestController  
22 -@RequestMapping("/system/wx-mp")  
23 -@Validated  
24 -@Slf4j  
25 -public class AppWxMpController {  
26 -  
27 - @Resource  
28 - private WxMpService mpService;  
29 -  
30 - // TODO @芋艿:需要额外考虑个问题;多租户下,如果每个小程序一个微信公众号,则会存在多个 appid;  
31 - @PostMapping("/create-jsapi-signature")  
32 - @Operation(summary = "创建微信 JS SDK 初始化所需的签名",  
33 - description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档")  
34 - public CommonResult<WxJsapiSignature> createJsapiSignature(@RequestParam("url") String url) throws WxErrorException {  
35 - return success(mpService.createJsapiSignature(url));  
36 - }  
37 -  
38 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/social/SocialClientConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.convert.social;
  2 +
  3 +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
  4 +import me.chanjar.weixin.common.bean.WxJsapiSignature;
  5 +import org.mapstruct.Mapper;
  6 +import org.mapstruct.factory.Mappers;
  7 +
  8 +@Mapper
  9 +public interface SocialClientConvert {
  10 +
  11 + SocialClientConvert INSTANCE = Mappers.getMapper(SocialClientConvert.class);
  12 +
  13 + SocialWxJsapiSignatureRespDTO convert(WxJsapiSignature bean);
  14 +
  15 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.service.social; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.service.social;
2 2
3 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; 3 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
4 import com.xingyuv.jushauth.model.AuthUser; 4 import com.xingyuv.jushauth.model.AuthUser;
  5 +import me.chanjar.weixin.common.bean.WxJsapiSignature;
5 6
6 /** 7 /**
7 * 社交应用 Service 接口 8 * 社交应用 Service 接口
@@ -31,4 +32,13 @@ public interface SocialClientService { @@ -31,4 +32,13 @@ public interface SocialClientService {
31 */ 32 */
32 AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state); 33 AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state);
33 34
  35 + /**
  36 + * 创建微信 JS SDK 初始化所需的签名
  37 + *
  38 + * @param userType 用户类型
  39 + * @param url 访问的 URL 地址
  40 + * @return 签名
  41 + */
  42 + WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url);
  43 +
34 } 44 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java
@@ -4,21 +4,33 @@ import cn.hutool.core.bean.BeanUtil; @@ -4,21 +4,33 @@ import cn.hutool.core.bean.BeanUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.ReflectUtil; 5 import cn.hutool.core.util.ReflectUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  7 +import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
7 import cn.iocoder.yudao.framework.common.util.http.HttpUtils; 8 import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
8 import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory; 9 import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
9 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
10 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper; 11 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper;
11 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; 12 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
  13 +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
  14 +import com.google.common.cache.CacheLoader;
  15 +import com.google.common.cache.LoadingCache;
12 import com.xingyuv.jushauth.config.AuthConfig; 16 import com.xingyuv.jushauth.config.AuthConfig;
13 import com.xingyuv.jushauth.model.AuthCallback; 17 import com.xingyuv.jushauth.model.AuthCallback;
14 import com.xingyuv.jushauth.model.AuthResponse; 18 import com.xingyuv.jushauth.model.AuthResponse;
15 import com.xingyuv.jushauth.model.AuthUser; 19 import com.xingyuv.jushauth.model.AuthUser;
16 import com.xingyuv.jushauth.request.AuthRequest; 20 import com.xingyuv.jushauth.request.AuthRequest;
17 import com.xingyuv.jushauth.utils.AuthStateUtils; 21 import com.xingyuv.jushauth.utils.AuthStateUtils;
  22 +import lombok.SneakyThrows;
18 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
  24 +import me.chanjar.weixin.common.bean.WxJsapiSignature;
  25 +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
  26 +import me.chanjar.weixin.mp.api.WxMpService;
  27 +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
  28 +import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
  29 +import org.springframework.data.redis.core.StringRedisTemplate;
19 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
20 31
21 import javax.annotation.Resource; 32 import javax.annotation.Resource;
  33 +import java.time.Duration;
22 import java.util.Objects; 34 import java.util.Objects;
23 35
24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 36 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -38,6 +50,32 @@ public class SocialClientServiceImpl implements SocialClientService { @@ -38,6 +50,32 @@ public class SocialClientServiceImpl implements SocialClientService {
38 private YudaoAuthRequestFactory yudaoAuthRequestFactory; 50 private YudaoAuthRequestFactory yudaoAuthRequestFactory;
39 51
40 @Resource 52 @Resource
  53 + private WxMpService mpService;
  54 + @Resource
  55 + private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它
  56 + @Resource
  57 + private WxMpProperties mpProperties;
  58 + /**
  59 + * 缓存 WxMpService 对象
  60 + *
  61 + * key:使用微信公众号的 appId + secret 拼接,即 {@link SocialClientDO} 的 clientId 和 clientSecret 属性。
  62 + * 为什么 key 使用这种格式?因为 {@link SocialClientDO} 在管理后台可以变更,通过这个 key 存储它的单例。
  63 + *
  64 + * 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。
  65 + */
  66 + private final LoadingCache<String, WxMpService> mpServiceCache = CacheUtils.buildAsyncReloadingCache(
  67 + Duration.ofSeconds(10L),
  68 + new CacheLoader<String, WxMpService>() {
  69 +
  70 + @Override
  71 + public WxMpService load(String key) {
  72 + String[] keys = key.split(":");
  73 + return buildMpService(keys[0], keys[1]);
  74 + }
  75 +
  76 + });
  77 +
  78 + @Resource
41 private SocialClientMapper socialClientMapper; 79 private SocialClientMapper socialClientMapper;
42 80
43 @Override 81 @Override
@@ -91,4 +129,49 @@ public class SocialClientServiceImpl implements SocialClientService { @@ -91,4 +129,49 @@ public class SocialClientServiceImpl implements SocialClientService {
91 return request; 129 return request;
92 } 130 }
93 131
  132 + @Override
  133 + @SneakyThrows
  134 + public WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url) {
  135 + WxMpService mpService = buildMpService(userType);
  136 + return mpService.createJsapiSignature(url);
  137 + }
  138 +
  139 + /**
  140 + * 创建 clientId + clientSecret 对应的 WxMpService 对象
  141 + *
  142 + * @param userType 用户类型
  143 + * @return WxMpService 对象
  144 + */
  145 + private WxMpService buildMpService(Integer userType) {
  146 + // 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象
  147 + SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
  148 + SocialTypeEnum.WECHAT_MP.getType(), userType);
  149 + if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
  150 + return mpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
  151 + }
  152 + // 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMpService 对象
  153 + return mpService;
  154 + }
  155 +
  156 + /**
  157 + * 创建 clientId + clientSecret 对应的 WxMpService 对象
  158 + *
  159 + * @param clientId 微信公众号 appId
  160 + * @param clientSecret 微信公众号 secret
  161 + * @return WxMpService 对象
  162 + */
  163 + private WxMpService buildMpService(String clientId, String clientSecret) {
  164 + // 第一步,创建 WxMpRedisConfigImpl 对象
  165 + WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl(
  166 + new RedisTemplateWxRedisOps(stringRedisTemplate),
  167 + mpProperties.getConfigStorage().getKeyPrefix());
  168 + configStorage.setAppId(clientId);
  169 + configStorage.setSecret(clientSecret);
  170 +
  171 + // 第二步,创建 WxMpService 对象
  172 + WxMpService service = new WxMpServiceImpl();
  173 + service.setWxMpConfigStorage(configStorage);
  174 + return service;
  175 + }
  176 +
94 } 177 }