Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
# Conflicts: # yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java # yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java # yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java # yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java # yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java # yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java # yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java # yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java # yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/ProductBrowseHistoryController.java # yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryRespVO.java # yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java # yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java # yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/message/TradeMessageServiceImpl.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationOrderHandler.java # yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java # yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialWxaQrcodeReqVO.java # yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java # yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java
This commit is contained in:
commit
f44b623cfb
2
pom.xml
2
pom.xml
@ -31,7 +31,7 @@
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.1.0-jdk8-snapshot</revision>
|
||||
<revision>2.2.0-jdk8-snapshot</revision>
|
||||
<!-- Maven 相关 -->
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.1.0-jdk8-snapshot</revision>
|
||||
<revision>2.2.0-jdk8-snapshot</revision>
|
||||
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.boot.version>2.7.18</spring.boot.version>
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.framework.ip.core;
|
||||
|
||||
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@ -54,7 +55,7 @@ public class Area {
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
@JsonManagedReference
|
||||
@JsonBackReference
|
||||
private List<Area> children;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package cn.iocoder.yudao.module.promotion.enums;
|
||||
|
||||
/**
|
||||
* 通知模板枚举类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface MessageTemplateConstants {
|
||||
|
||||
//======================= 小程序订阅消息模版 =======================
|
||||
|
||||
String COMBINATION_SUCCESS = "拼团结果通知";
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.member.controller.app.social.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "用户 APP - 获得小程序订阅模版 Response VO")
|
||||
@Data
|
||||
public class AppSocialWxaSubscribeTemplateRespVO {
|
||||
|
||||
@Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "9Aw5ZV1j9xdWTFEkqCpZ7mIBbSC34khK55OtzUPl0rU")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "模版标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单支付通知")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "{ {result.DATA} }\\n\\n领奖金额:{ {withdrawMoney.DATA} }\\n领奖时间: { {withdrawTime.DATA} }")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "模板内容示例", requiredMode = Schema.RequiredMode.REQUIRED, example = "下单时间:2016年8月8日")
|
||||
private String example;
|
||||
|
||||
@Schema(description = "模版类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer type; // 2 为一次性订阅,3 为长期订阅
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package cn.iocoder.yudao.module.pay.enums;
|
||||
|
||||
/**
|
||||
* 通知模板枚举类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public interface MessageTemplateConstants {
|
||||
|
||||
// ======================= 小程序订阅消息 =======================
|
||||
|
||||
String WXA_WALLET_RECHARGER_PAID = "充值成功通知";
|
||||
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
package cn.iocoder.yudao.module.system.api.social;
|
||||
|
||||
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 javax.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社交应用的 API 接口
|
||||
*
|
||||
@ -17,8 +17,8 @@ public interface SocialClientApi {
|
||||
/**
|
||||
* 获得社交平台的授权 URL
|
||||
*
|
||||
* @param socialType 社交平台的类型 {@link SocialTypeEnum}
|
||||
* @param userType 用户类型
|
||||
* @param socialType 社交平台的类型 {@link SocialTypeEnum}
|
||||
* @param userType 用户类型
|
||||
* @param redirectUri 重定向 URL
|
||||
* @return 社交平台的授权 URL
|
||||
*/
|
||||
@ -28,15 +28,17 @@ public interface SocialClientApi {
|
||||
* 创建微信公众号 JS SDK 初始化所需的签名
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param url 访问的 URL 地址
|
||||
* @param url 访问的 URL 地址
|
||||
* @return 签名
|
||||
*/
|
||||
SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url);
|
||||
|
||||
//======================= 微信小程序独有 =======================
|
||||
|
||||
/**
|
||||
* 获得微信小程序的手机信息
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param userType 用户类型
|
||||
* @param phoneCode 手机授权码
|
||||
* @return 手机信息
|
||||
*/
|
||||
@ -50,4 +52,18 @@ public interface SocialClientApi {
|
||||
*/
|
||||
byte[] getWxaQrcode(@Valid SocialWxQrcodeReqDTO reqVO);
|
||||
|
||||
/**
|
||||
* 获得微信小程订阅模板
|
||||
*
|
||||
* @return 小程序订阅消息模版
|
||||
*/
|
||||
List<SocialWxaSubscribeTemplateRespDTO> getWxaSubscribeTemplateList(Integer userType);
|
||||
|
||||
/**
|
||||
* 发送微信小程序订阅消息
|
||||
*
|
||||
* @param reqDTO 请求
|
||||
*/
|
||||
void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.system.api.social.dto;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 微信小程序订阅消息发送 Request DTO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Data
|
||||
public class SocialWxaSubscribeMessageSendReqDTO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* 关联 MemberUserDO 的 id 编号
|
||||
* 关联 AdminUserDO 的 id 编号
|
||||
*/
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* 关联 {@link UserTypeEnum}
|
||||
*/
|
||||
@NotNull(message = "用户类型不能为空")
|
||||
private Integer userType;
|
||||
|
||||
/**
|
||||
* 消息模版标题
|
||||
*/
|
||||
@NotEmpty(message = "消息模版标题不能为空")
|
||||
private String templateTitle;
|
||||
|
||||
/**
|
||||
* 点击模板卡片后的跳转页面,仅限本小程序内的页面
|
||||
*
|
||||
* 支持带参数,(示例 index?foo=bar )。该字段不填则模板无跳转。
|
||||
*/
|
||||
private String page;
|
||||
|
||||
/**
|
||||
* 模板内容的参数
|
||||
*/
|
||||
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,42 @@
|
||||
package cn.iocoder.yudao.module.system.api.social.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* 小程序订阅消息模版 Response DTO
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Data
|
||||
public class SocialWxaSubscribeTemplateRespDTO {
|
||||
|
||||
/**
|
||||
* 模版编号
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 模版标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 模版内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 模板内容示例
|
||||
*/
|
||||
private String example;
|
||||
|
||||
/**
|
||||
* 模版类型
|
||||
*
|
||||
* 2:为一次性订阅
|
||||
* 3:为长期订阅
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
}
|
@ -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,16 +1,25 @@
|
||||
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.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.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.hutool.core.collection.CollUtil.findOne;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* 社交应用的 API 实现类
|
||||
@ -19,10 +28,13 @@ import javax.annotation.Resource;
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class SocialClientApiImpl implements SocialClientApi {
|
||||
|
||||
@Resource
|
||||
private SocialClientService socialClientService;
|
||||
@Resource
|
||||
private SocialUserService socialUserService;
|
||||
|
||||
@Override
|
||||
public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
|
||||
@ -35,6 +47,8 @@ public class SocialClientApiImpl implements SocialClientApi {
|
||||
return BeanUtils.toBean(signature, SocialWxJsapiSignatureRespDTO.class);
|
||||
}
|
||||
|
||||
//======================= 微信小程序独有 =======================
|
||||
|
||||
@Override
|
||||
public SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
|
||||
WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode);
|
||||
@ -46,4 +60,38 @@ public class SocialClientApiImpl implements SocialClientApi {
|
||||
return socialClientService.getWxaQrcode(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SocialWxaSubscribeTemplateRespDTO> getWxaSubscribeTemplateList(Integer userType) {
|
||||
List<TemplateInfo> list = socialClientService.getSubscribeTemplateList(userType);
|
||||
return convertList(list, item -> BeanUtils.toBean(item, SocialWxaSubscribeTemplateRespDTO.class).setId(item.getPriTmplId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO) {
|
||||
// 1.1 获得订阅模版列表
|
||||
List<TemplateInfo> templateList = socialClientService.getSubscribeTemplateList(reqDTO.getUserType());
|
||||
if (CollUtil.isEmpty(templateList)) {
|
||||
log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
|
||||
return;
|
||||
}
|
||||
// 1.2 获得需要使用的模版
|
||||
TemplateInfo template = findOne(templateList, item ->
|
||||
ObjUtil.equal(item.getTitle(), reqDTO.getTemplateTitle()));
|
||||
if (template == null) {
|
||||
log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 3. 发送订阅消息
|
||||
socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
@ -28,6 +30,8 @@ public class SocialClientController {
|
||||
|
||||
@Resource
|
||||
private SocialClientService socialClientService;
|
||||
@Resource
|
||||
private SocialClientApi socialClientApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建社交客户端")
|
||||
@ -70,4 +74,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,12 +3,16 @@ 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;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import com.xingyuv.jushauth.model.AuthUser;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
@ -70,6 +74,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;
|
||||
@ -35,20 +41,25 @@ import com.xingyuv.justauth.AuthRequestFactory;
|
||||
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 javax.annotation.Resource;
|
||||
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.*;
|
||||
|
||||
@ -62,7 +73,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||
public class SocialClientServiceImpl implements SocialClientService {
|
||||
|
||||
/**
|
||||
* 小程序版本
|
||||
* 小程序码要打开的小程序版本
|
||||
*
|
||||
* 1. release:正式版
|
||||
* 2. trial:体验版
|
||||
@ -70,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;
|
||||
@ -254,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;
|
||||
@ -38,6 +39,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
@ -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;
|
||||
|
@ -174,6 +174,7 @@ logging:
|
||||
cn.iocoder.yudao.module.statistics.dal.mysql: debug
|
||||
cn.iocoder.yudao.module.crm.dal.mysql: debug
|
||||
cn.iocoder.yudao.module.erp.dal.mysql: debug
|
||||
cn.iocoder.yudao.module.ai.dal.mysql: debug
|
||||
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示
|
||||
|
||||
debug: false
|
||||
@ -195,10 +196,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 的前缀
|
||||
@ -220,6 +223,8 @@ yudao:
|
||||
demo: false # 关闭演示模式
|
||||
wxa-code:
|
||||
env-version: develop # 小程序版本: 正式版为 "release";体验版为 "trial";开发版为 "develop"
|
||||
wxa-subscribe-message:
|
||||
miniprogram-state: developer # 跳转小程序类型:开发版为 “developer”;体验版为 “trial”为;正式版为 “formal”
|
||||
tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
||||
|
||||
justauth:
|
||||
|
Loading…
x
Reference in New Issue
Block a user