【功能新增】微信小程序的订阅消息
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
package cn.iocoder.yudao.module.system.api.social;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.*;
|
||||
import cn.iocoder.yudao.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -11,9 +9,14 @@ import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.SpringQueryMap;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory =
|
||||
@Tag(name = "RPC 服务 - 社交应用")
|
||||
public interface SocialClientApi {
|
||||
@@ -51,6 +54,14 @@ public interface SocialClientApi {
|
||||
|
||||
@GetMapping(PREFIX + "/get-wxa-qrcode")
|
||||
@Operation(summary = "获得小程序二维码")
|
||||
CommonResult<byte[]> getWxaQrcode(@Valid SocialWxQrcodeReqDTO reqVO);
|
||||
CommonResult<byte[]> getWxaQrcode(@SpringQueryMap SocialWxQrcodeReqDTO reqVO);
|
||||
|
||||
@GetMapping(PREFIX + "/get-wxa-subscribe-template-list")
|
||||
@Operation(summary = "获得微信小程订阅模板")
|
||||
CommonResult<List<SocialWxaSubscribeTemplateRespDTO>> getWxaSubscribeTemplateList(@RequestParam("userType") Integer userType);
|
||||
|
||||
@PostMapping(PREFIX + "/send-wxa-subscribe-message")
|
||||
@Operation(summary = "发送微信小程序订阅消息")
|
||||
CommonResult<Boolean> sendWxaSubscribeMessage(@Valid @RequestBody SocialWxaSubscribeMessageSendReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.system.api.social.dto;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "RPC 服务 - 微信小程序订阅消息发送 Request DTO")
|
||||
@Data
|
||||
public class SocialWxaSubscribeMessageSendReqDTO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@InEnum(UserTypeEnum.class)
|
||||
@NotNull(message = "用户类型不能为空")
|
||||
private Integer userType;
|
||||
|
||||
@Schema(description = "消息模版标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "模版标题")
|
||||
@NotEmpty(message = "消息模版标题不能为空")
|
||||
private String templateTitle;
|
||||
|
||||
@Schema(description = "点击模板卡片后的跳转页面,仅限本小程序内的页面", example = "pages/index?foo=bar")
|
||||
private String page; // 支持带参数,(示例 index?foo=bar )。该字段不填则模板无跳转。
|
||||
|
||||
@Schema(description = "模板内容的参数")
|
||||
private Map<String, String> messages;
|
||||
|
||||
public SocialWxaSubscribeMessageSendReqDTO addMessage(String key, String value) {
|
||||
if (messages == null) {
|
||||
messages = new HashMap<>();
|
||||
}
|
||||
messages.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.system.api.social.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "RPC 服务 - 小程序订阅消息模版 Response DTO")
|
||||
@Data
|
||||
public class SocialWxaSubscribeTemplateRespDTO {
|
||||
|
||||
@Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "模版标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "模版标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "模版内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "模板内容示例", requiredMode = Schema.RequiredMode.REQUIRED, example = "模版内容示例")
|
||||
private String example;
|
||||
|
||||
@Schema(description = "模版类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer type; // 2:为一次性订阅;3:为长期订阅
|
||||
|
||||
}
|
||||
@@ -117,8 +117,10 @@ public interface ErrorCodeConstants {
|
||||
|
||||
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_002_018_200, "获得手机号失败");
|
||||
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR = new ErrorCode(1_002_018_201, "获得小程序码失败");
|
||||
ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_202, "社交客户端不存在");
|
||||
ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_203, "社交客户端已存在配置");
|
||||
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR = new ErrorCode(1_002_018_202, "获得小程序订阅消息模版失败");
|
||||
ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR = new ErrorCode(1_002_018_203, "发送小程序订阅消息失败");
|
||||
ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在");
|
||||
ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置");
|
||||
|
||||
|
||||
// ========== OAuth2 客户端 1-002-020-000 =========
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
package cn.iocoder.yudao.module.system.api.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.*;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
|
||||
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.hutool.core.collection.CollUtil.findOne;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* 社交应用的 API 实现类
|
||||
@@ -22,10 +31,13 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class SocialClientApiImpl implements SocialClientApi {
|
||||
|
||||
@Resource
|
||||
private SocialClientService socialClientService;
|
||||
@Resource
|
||||
private SocialUserService socialUserService;
|
||||
|
||||
@Override
|
||||
public CommonResult<String> getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
|
||||
@@ -49,4 +61,39 @@ public class SocialClientApiImpl implements SocialClientApi {
|
||||
return success(socialClientService.getWxaQrcode(reqVO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<SocialWxaSubscribeTemplateRespDTO>> getWxaSubscribeTemplateList(Integer userType) {
|
||||
List<TemplateInfo> list = socialClientService.getSubscribeTemplateList(userType);
|
||||
return success(convertList(list, item -> BeanUtils.toBean(item, SocialWxaSubscribeTemplateRespDTO.class).setId(item.getPriTmplId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO) {
|
||||
// 1.1 获得订阅模版列表
|
||||
List<TemplateInfo> templateList = socialClientService.getSubscribeTemplateList(reqDTO.getUserType());
|
||||
if (CollUtil.isEmpty(templateList)) {
|
||||
log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
|
||||
return success(false);
|
||||
}
|
||||
// 1.2 获得需要使用的模版
|
||||
TemplateInfo template = findOne(templateList, item ->
|
||||
ObjUtil.equal(item.getTitle(), reqDTO.getTemplateTitle()));
|
||||
if (template == null) {
|
||||
log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
|
||||
return success(false);
|
||||
}
|
||||
|
||||
// 2. 获得社交用户
|
||||
SocialUserRespDTO socialUser = socialUserService.getSocialUserByUserId(reqDTO.getUserType(), reqDTO.getUserId(),
|
||||
SocialTypeEnum.WECHAT_MINI_APP.getType());
|
||||
if (StrUtil.isBlankIfStr(socialUser.getOpenid())) {
|
||||
log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:会员 openid 缺失]", reqDTO);
|
||||
return success(false);
|
||||
}
|
||||
|
||||
// 3. 发送订阅消息
|
||||
socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
### 请求 /system/social-client/send-subscribe-message 接口 => 发送测试订阅消息
|
||||
POST {{baseUrl}}/system/social-client/send-subscribe-message
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
#Authorization: Bearer test100
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
{
|
||||
"userId": 247,
|
||||
"userType": 1,
|
||||
"socialType": 34,
|
||||
"templateTitle": "充值成功通知",
|
||||
"page": "",
|
||||
"messages": {
|
||||
"character_string1":"5616122165165",
|
||||
"amount2":"1000.00",
|
||||
"time3":"2024-01-01 10:10:10",
|
||||
"phrase4": "充值成功"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.system.controller.admin.socail;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientRespVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
|
||||
@@ -11,13 +13,12 @@ import cn.iocoder.yudao.module.system.service.social.SocialClientService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 社交客户端")
|
||||
@@ -28,6 +29,8 @@ public class SocialClientController {
|
||||
|
||||
@Resource
|
||||
private SocialClientService socialClientService;
|
||||
@Resource
|
||||
private SocialClientApi socialClientApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建社交客户端")
|
||||
@@ -70,4 +73,11 @@ public class SocialClientController {
|
||||
return success(BeanUtils.toBean(pageResult, SocialClientRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/send-subscribe-message")
|
||||
@Operation(summary = "发送订阅消息") // 用于测试
|
||||
@PreAuthorize("@ss.hasPermission('system:social-client:query')")
|
||||
public void sendSubscribeMessage(@RequestBody SocialWxaSubscribeMessageSendReqDTO reqDTO) {
|
||||
socialClientApi.sendWxaSubscribeMessage(reqDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -98,4 +98,13 @@ public interface RedisKeyConstants {
|
||||
* VALUE 数据格式:String 模版信息
|
||||
*/
|
||||
String SMS_TEMPLATE = "sms_template";
|
||||
|
||||
/**
|
||||
* 小程序订阅模版的缓存
|
||||
*
|
||||
* KEY 格式:wxa_subscribe_template:{userType}
|
||||
* VALUE 数据格式 String, 模版信息
|
||||
*/
|
||||
String WXA_SUBSCRIBE_TEMPLATE = "wxa_subscribe_template";
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.social;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
|
||||
@@ -10,6 +11,9 @@ import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import com.xingyuv.jushauth.model.AuthUser;
|
||||
import jakarta.validation.Valid;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社交应用 Service 接口
|
||||
@@ -69,6 +73,25 @@ public interface SocialClientService {
|
||||
*/
|
||||
byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO);
|
||||
|
||||
/**
|
||||
* 获得微信小程订阅模板
|
||||
*
|
||||
* 缓存的目的:考虑到微信小程序订阅消息选择好模版后几乎不会变动,缓存增加查询效率
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @return 微信小程订阅模板
|
||||
*/
|
||||
List<TemplateInfo> getSubscribeTemplateList(Integer userType);
|
||||
|
||||
/**
|
||||
* 发送微信小程序订阅消息
|
||||
*
|
||||
* @param reqDTO 请求
|
||||
* @param templateId 模版编号
|
||||
* @param openId 会员 openId
|
||||
*/
|
||||
void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId);
|
||||
|
||||
// =================== 客户端管理 ===================
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package cn.iocoder.yudao.module.system.service.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaSubscribeService;
|
||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
|
||||
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
|
||||
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
@@ -15,10 +19,12 @@ import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
|
||||
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
|
||||
@@ -36,22 +42,26 @@ import jakarta.annotation.Resource;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
|
||||
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR;
|
||||
|
||||
/**
|
||||
* 社交应用 Service 实现类
|
||||
@@ -63,7 +73,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_CLI
|
||||
public class SocialClientServiceImpl implements SocialClientService {
|
||||
|
||||
/**
|
||||
* 小程序版本
|
||||
* 小程序码要打开的小程序版本
|
||||
*
|
||||
* 1. release:正式版
|
||||
* 2. trial:体验版
|
||||
@@ -71,6 +81,15 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
*/
|
||||
@Value("${yudao.wxa-code.env-version:release}")
|
||||
public String envVersion;
|
||||
/**
|
||||
* 订阅消息跳转小程序类型
|
||||
*
|
||||
* 1. developer:开发版
|
||||
* 2. trial:体验版
|
||||
* 3. formal:正式版
|
||||
*/
|
||||
@Value("${yudao.wxa-subscribe-message.miniprogram-state:formal}")
|
||||
public String miniprogramState;
|
||||
|
||||
@Resource
|
||||
private AuthRequestFactory authRequestFactory;
|
||||
@@ -255,11 +274,58 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
null,
|
||||
ObjUtil.defaultIfNull(reqVO.getHyaline(), SocialWxQrcodeReqDTO.HYALINE));
|
||||
} catch (WxErrorException e) {
|
||||
log.error("[getWxQrcode][reqVO({})) 获得小程序码失败]", reqVO, e);
|
||||
log.error("[getWxQrcode][reqVO({}) 获得小程序码失败]", reqVO, e);
|
||||
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(cacheNames = RedisKeyConstants.WXA_SUBSCRIBE_TEMPLATE, key = "#userType", condition = "#result != null")
|
||||
public List<TemplateInfo> getSubscribeTemplateList(Integer userType) {
|
||||
WxMaService service = getWxMaService(userType);
|
||||
try {
|
||||
WxMaSubscribeService subscribeService = service.getSubscribeService();
|
||||
return subscribeService.getTemplateList();
|
||||
} catch (WxErrorException e) {
|
||||
log.error("[getSubscribeTemplate][userType({}) 获得小程序订阅消息模版]", userType, e);
|
||||
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId) {
|
||||
WxMaService service = getWxMaService(reqDTO.getUserType());
|
||||
try {
|
||||
WxMaSubscribeService subscribeService = service.getSubscribeService();
|
||||
subscribeService.sendSubscribeMsg(buildMessageSendReqDTO(reqDTO, templateId, openId));
|
||||
} catch (WxErrorException e) {
|
||||
log.error("[sendSubscribeMessage][reqVO({}) templateId({}) openId({}) 发送小程序订阅消息失败]", reqDTO, templateId, openId, e);
|
||||
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建发送消息请求参数
|
||||
*
|
||||
* @param reqDTO 请求
|
||||
* @param templateId 模版编号
|
||||
* @param openId 会员 openId
|
||||
* @return 微信小程序订阅消息请求参数
|
||||
*/
|
||||
private WxMaSubscribeMessage buildMessageSendReqDTO(SocialWxaSubscribeMessageSendReqDTO reqDTO,
|
||||
String templateId, String openId) {
|
||||
// 设置订阅消息基本参数
|
||||
WxMaSubscribeMessage subscribeMessage = new WxMaSubscribeMessage().setLang(WxMaConstants.MiniProgramLang.ZH_CN)
|
||||
.setMiniprogramState(miniprogramState).setTemplateId(templateId).setToUser(openId).setPage(reqDTO.getPage());
|
||||
// 设置具体消息参数
|
||||
Map<String, String> messages = reqDTO.getMessages();
|
||||
if (CollUtil.isNotEmpty(messages)) {
|
||||
reqDTO.getMessages().keySet().forEach(key -> findAndThen(messages, key, value ->
|
||||
subscribeMessage.addData(new WxMaSubscribeMessage.MsgData(key, value))));
|
||||
}
|
||||
return subscribeMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 clientId + clientSecret 对应的 WxMpService 对象
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
|
||||
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
|
||||
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||
@@ -32,6 +33,7 @@ import com.mzt.logapi.context.LogRecordContext;
|
||||
import com.mzt.logapi.service.impl.DiffParseFunction;
|
||||
import com.mzt.logapi.starter.annotation.LogRecord;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
@@ -325,7 +327,7 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
}
|
||||
|
||||
private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
|
||||
Long deptId, Set<Long> postIds) {
|
||||
Long deptId, Set<Long> postIds) {
|
||||
// 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
|
||||
return DataPermissionUtils.executeIgnore(() -> {
|
||||
// 校验用户存在
|
||||
@@ -443,7 +445,14 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>())
|
||||
.updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build();
|
||||
importUsers.forEach(importUser -> {
|
||||
// 校验,判断是否有不符合的原因
|
||||
// 2.1.1 校验字段是否符合要求
|
||||
try {
|
||||
ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class).setPassword(initPassword));
|
||||
} catch (ConstraintViolationException ex){
|
||||
respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
|
||||
return;
|
||||
}
|
||||
// 2.1.2 校验,判断是否有不符合的原因
|
||||
try {
|
||||
validateUserForCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(),
|
||||
importUser.getDeptId(), null);
|
||||
@@ -451,7 +460,8 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
|
||||
return;
|
||||
}
|
||||
// 判断如果不存在,在进行插入
|
||||
|
||||
// 2.2.1 判断如果不存在,在进行插入
|
||||
AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername());
|
||||
if (existUser == null) {
|
||||
userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class)
|
||||
@@ -459,7 +469,7 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
respVO.getCreateUsernames().add(importUser.getUsername());
|
||||
return;
|
||||
}
|
||||
// 如果存在,判断是否允许更新
|
||||
// 2.2.2 如果存在,判断是否允许更新
|
||||
if (!isUpdateSupport) {
|
||||
respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg());
|
||||
return;
|
||||
|
||||
@@ -158,10 +158,12 @@ wx:
|
||||
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
|
||||
# appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的)
|
||||
# secret: 333ae72f41552af1e998fe1f54e1584a
|
||||
appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
|
||||
secret: 6f270509224a7ae1296bbf1c8cb97aed
|
||||
# appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
|
||||
# secret: 6f270509224a7ae1296bbf1c8cb97aed
|
||||
# appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
|
||||
# secret: 4a1a04e07f6a4a0751b39c3064a92c8b
|
||||
appid: wx66186af0759f47c9 # 测试号(puhui 提供的)
|
||||
secret: 3218bcbd112cbc614c7264ceb20144ac
|
||||
config-storage:
|
||||
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
|
||||
key-prefix: wa # Redis Key 的前缀
|
||||
|
||||
@@ -11,10 +11,7 @@ import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
|
||||
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
|
||||
@@ -434,6 +431,8 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
|
||||
public void testImportUserList_01() {
|
||||
// 准备参数
|
||||
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
|
||||
o.setEmail(randomEmail());
|
||||
o.setMobile(randomMobile());
|
||||
});
|
||||
// mock 方法,模拟失败
|
||||
doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any());
|
||||
@@ -456,6 +455,8 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
|
||||
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
|
||||
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
||||
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
|
||||
o.setEmail(randomEmail());
|
||||
o.setMobile(randomMobile());
|
||||
});
|
||||
// mock deptService 的方法
|
||||
DeptDO dept = randomPojo(DeptDO.class, o -> {
|
||||
@@ -490,6 +491,8 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
|
||||
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
||||
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
|
||||
o.setUsername(dbUser.getUsername());
|
||||
o.setEmail(randomEmail());
|
||||
o.setMobile(randomMobile());
|
||||
});
|
||||
// mock deptService 的方法
|
||||
DeptDO dept = randomPojo(DeptDO.class, o -> {
|
||||
@@ -520,6 +523,8 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
|
||||
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
|
||||
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
|
||||
o.setUsername(dbUser.getUsername());
|
||||
o.setEmail(randomEmail());
|
||||
o.setMobile(randomMobile());
|
||||
});
|
||||
// mock deptService 的方法
|
||||
DeptDO dept = randomPojo(DeptDO.class, o -> {
|
||||
|
||||
Reference in New Issue
Block a user