Răsfoiți Sursa

新接入腾讯云短信模版
邀请码加入发送短信通知
新增邀请码加入批量手机号指定创建邀请码
新增邀请码可勾选同步短信通知功能

liyanbo 3 săptămâni în urmă
părinte
comite
81ea77ce80

+ 8 - 0
byzs-module-system/src/main/java/cn/iocoder/byzs/module/system/controller/admin/invitecode/InviteCodeController.java

@@ -98,4 +98,12 @@ public class InviteCodeController {
         ExcelUtils.write(response, "邀请码.xls", "数据", InviteCodeRespVO.class, list);
     }
 
+    @PostMapping("/send-sms")
+    @Operation(summary = "发送邀请码短信")
+    @Parameter(name = "id", description = "邀请码ID", required = true)
+    public CommonResult<Boolean> sendSms(@RequestParam("id") Long id) {
+        inviteCodeService.sendSmsById(id);
+        return success(true);
+    }
+
 }

+ 8 - 0
byzs-module-system/src/main/java/cn/iocoder/byzs/module/system/controller/admin/invitecode/vo/InviteCodeRespVO.java

@@ -76,4 +76,12 @@ public class InviteCodeRespVO {
     @ExcelProperty("指定手机号")
     private String onlyPhone;
 
+    @Schema(description = "通知状态")
+    @ExcelProperty("通知状态")
+    private String notifyStatus;
+
+    @Schema(description = "通知时间")
+    @ExcelProperty("通知时间")
+    private LocalDateTime notifyTime;
+
 }

+ 3 - 1
byzs-module-system/src/main/java/cn/iocoder/byzs/module/system/controller/admin/invitecode/vo/InviteCodeSaveReqVO.java

@@ -18,7 +18,9 @@ public class InviteCodeSaveReqVO {
     //批量数量
     private Integer batchCount;
     //批量手机号
-    private List<String> onlyPhoneList;
+    private List<String> batchPhones;
+    //是否发送短信通知
+    private Boolean sendSms;
 
     @Schema(description = "邀请码")
     private String code;

+ 11 - 0
byzs-module-system/src/main/java/cn/iocoder/byzs/module/system/dal/dataobject/invitecode/InviteCodeDO.java

@@ -70,5 +70,16 @@ public class InviteCodeDO extends BaseDO {
 
     private Long tenantId;
 
+    /**
+     * 通知状态
+     * 0:未通知,1:通知成功,2:通知失败
+     */
+    private String notifyStatus;
+
+    /**
+     * 通知时间
+     */
+    private LocalDateTime notifyTime;
+
 
 }

+ 1 - 0
byzs-module-system/src/main/java/cn/iocoder/byzs/module/system/enums/ErrorCodeConstants.java

@@ -38,6 +38,7 @@ public interface ErrorCodeConstants {
 
     // ========== 邀请码 1-002-003-000==========
     ErrorCode INVITE_CODE_NOT_EXISTS = new ErrorCode(1_002_030_004, "邀请码不存在");
+    ErrorCode INVITE_CODE_DUPLICATE = new ErrorCode(1_002_030_005, "邀请码重复");
 
     // ========== 用户权限有效期 1-002-031-000==========
     ErrorCode USER_WEB_EXPIRE_TIME_NOT_EXISTS = new ErrorCode(1_002_031_004, "用户平台权限有效期不存在");

+ 2 - 1
byzs-module-system/src/main/java/cn/iocoder/byzs/module/system/enums/sms/SmsSceneEnum.java

@@ -26,7 +26,8 @@ public enum SmsSceneEnum implements ArrayValuable<Integer> {
     ADMIN_MEMBER_REGISTER(22, "admin-sms-register", "后台用户 - 手机号注册"),
     ADMIN_MEMBER_RESET_PASSWORD(23, "admin-reset-password", "后台用户 - 忘记密码"),
 
-    AI_MEMBER_PUSH_INVITATION_CODE(30, "ai-sms-push-invitation-code", "前台用户 - 邀请码推送登录");
+    ALY_MEMBER_PUSH_INVITATION_CODE(30, "aly-sms-push-invitation-code", "阿里云 - 邀请码推送登录"),
+    TX_MEMBER_PUSH_INVITATION_CODE(31, "tx-sms-push-invitation-code", "腾讯云 - 邀请码推送登录");
 
     public static final Integer[] ARRAYS = Arrays.stream(values()).map(SmsSceneEnum::getScene).toArray(Integer[]::new);
 

+ 7 - 0
byzs-module-system/src/main/java/cn/iocoder/byzs/module/system/service/invitecode/InviteCodeService.java

@@ -77,4 +77,11 @@ public interface InviteCodeService {
      */
     PageResult<InviteCodeRespVO> getInviteCodePage(InviteCodePageReqVO pageReqVO);
 
+    /**
+     * 单独发送邀请码短信
+     *
+     * @param id 邀请码ID
+     */
+    void sendSmsById(Long id);
+
 }

+ 137 - 16
byzs-module-system/src/main/java/cn/iocoder/byzs/module/system/service/invitecode/InviteCodeServiceImpl.java

@@ -5,6 +5,7 @@ import cn.iocoder.byzs.framework.common.pojo.PageParam;
 import cn.iocoder.byzs.framework.common.pojo.PageResult;
 import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
 import cn.iocoder.byzs.framework.common.util.string.StrUtils;
+import cn.iocoder.byzs.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.byzs.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.byzs.module.system.controller.admin.invitecode.vo.InviteCodePageReqVO;
 import cn.iocoder.byzs.module.system.controller.admin.invitecode.vo.InviteCodeRespVO;
@@ -17,16 +18,20 @@ import cn.iocoder.byzs.module.system.dal.mysql.invitecode.InviteCodeMapper;
 import cn.iocoder.byzs.module.system.dal.mysql.permission.RoleMapper;
 import cn.iocoder.byzs.module.system.dal.mysql.tenant.TenantMapper;
 import cn.iocoder.byzs.module.system.dal.mysql.user.AdminUserMapper;
+import cn.iocoder.byzs.module.system.service.sms.SmsSendService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
 
 import static cn.iocoder.byzs.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.byzs.module.system.enums.ErrorCodeConstants.INVITE_CODE_DUPLICATE;
 import static cn.iocoder.byzs.module.system.enums.ErrorCodeConstants.INVITE_CODE_NOT_EXISTS;
+import static cn.iocoder.byzs.module.system.enums.sms.SmsSceneEnum.ALY_MEMBER_PUSH_INVITATION_CODE;
 
 /**
  * 邀请码 Service 实现类
@@ -49,6 +54,9 @@ public class InviteCodeServiceImpl implements InviteCodeService {
     @Resource
     private AdminUserMapper userMapper;
 
+    @Resource
+    private SmsSendService smsSendService;
+
     @Override
     public Long createInviteCode(InviteCodeSaveReqVO createReqVO) {
         // 批量生成邀请码
@@ -60,25 +68,40 @@ public class InviteCodeServiceImpl implements InviteCodeService {
             for (int i = 0; i < batchCount; i++) {
                 InviteCodeDO inviteCode = BeanUtils.toBean(createReqVO, InviteCodeDO.class);
                 // 生成随机邀请码
-                String randomCode = generateRandomCode(10);
-                // 如果有前缀,则拼接前缀
-                if (createReqVO.getCode() != null && !createReqVO.getCode().isEmpty()) {
-                    inviteCode.setCode(createReqVO.getCode() + randomCode);
-                } else {
-                    inviteCode.setCode(randomCode);
-                }
+                String randomCode;
+                String finalCode;
+                do {
+                    randomCode = generateRandomCode(10);
+                    // 如果有前缀,则拼接前缀
+                    if (createReqVO.getCode() != null && !createReqVO.getCode().isEmpty()) {
+                        finalCode = createReqVO.getCode() + randomCode;
+                    } else {
+                        finalCode = randomCode;
+                    }
+                    // 校验邀请码是否已存在
+                } while (isInviteCodeExists(finalCode));
+                inviteCode.setCode(finalCode);
                 inviteCodeList.add(inviteCode);
             }
 
             // 批量插入
             inviteCodeMapper.insertBatch(inviteCodeList);
 
+            // 如果需要发送短信通知
+            if (Boolean.TRUE.equals(createReqVO.getSendSms())) {
+                for (InviteCodeDO inviteCode : inviteCodeList) {
+                    if (inviteCode.getOnlyPhone() != null && !inviteCode.getOnlyPhone().isEmpty()) {
+                        sendInviteCodeSms(inviteCode);
+                    }
+                }
+            }
+
             // 返回第一个生成的邀请码ID
             return inviteCodeList.get(0).getId();
         }
         // 批量手机号生成邀请码
         else if ("batchPhone".equals(createReqVO.getType())) {
-            List<String> batchPhones = createReqVO.getOnlyPhoneList();
+            List<String> batchPhones = createReqVO.getBatchPhones();
 
             // 生成批量邀请码
             List<InviteCodeDO> inviteCodeList = new ArrayList<>(batchPhones.size());
@@ -87,19 +110,32 @@ public class InviteCodeServiceImpl implements InviteCodeService {
                 // 设置手机号
                 inviteCode.setOnlyPhone(phone);
                 // 生成随机邀请码
-                String randomCode = generateRandomCode(10);
-                // 如果有前缀,则拼接前缀
-                if (createReqVO.getCode() != null && !createReqVO.getCode().isEmpty()) {
-                    inviteCode.setCode(createReqVO.getCode() + randomCode);
-                } else {
-                    inviteCode.setCode(randomCode);
-                }
+                String randomCode;
+                String finalCode;
+                do {
+                    randomCode = generateRandomCode(10);
+                    // 如果有前缀,则拼接前缀
+                    if (createReqVO.getCode() != null && !createReqVO.getCode().isEmpty()) {
+                        finalCode = createReqVO.getCode() + randomCode;
+                    } else {
+                        finalCode = randomCode;
+                    }
+                    // 校验邀请码是否已存在
+                } while (isInviteCodeExists(finalCode));
+                inviteCode.setCode(finalCode);
                 inviteCodeList.add(inviteCode);
             }
 
             // 批量插入
             inviteCodeMapper.insertBatch(inviteCodeList);
 
+            // 如果需要发送短信通知
+            if (Boolean.TRUE.equals(createReqVO.getSendSms())) {
+                for (InviteCodeDO inviteCode : inviteCodeList) {
+                    sendInviteCodeSms(inviteCode);
+                }
+            }
+
             // 返回第一个生成的邀请码ID
             return inviteCodeList.get(0).getId();
         }
@@ -108,13 +144,41 @@ public class InviteCodeServiceImpl implements InviteCodeService {
             InviteCodeDO inviteCode = BeanUtils.toBean(createReqVO, InviteCodeDO.class);
             // 如果没有提供邀请码,则生成一个
             if (inviteCode.getCode() == null || inviteCode.getCode().isEmpty()) {
-                inviteCode.setCode(generateRandomCode(10));
+                String randomCode;
+                String finalCode;
+                do {
+                    randomCode = generateRandomCode(10);
+                    finalCode = randomCode;
+                    // 校验邀请码是否已存在
+                } while (isInviteCodeExists(finalCode));
+                inviteCode.setCode(finalCode);
+            } else {
+                // 校验用户提供的邀请码是否已存在
+                if (isInviteCodeExists(inviteCode.getCode())) {
+                    throw exception(INVITE_CODE_DUPLICATE);
+                }
             }
             inviteCodeMapper.insert(inviteCode);
+
+            // 如果需要发送短信通知
+            if (Boolean.TRUE.equals(createReqVO.getSendSms()) && inviteCode.getOnlyPhone() != null && !inviteCode.getOnlyPhone().isEmpty()) {
+                sendInviteCodeSms(inviteCode);
+            }
+
             return inviteCode.getId();
         }
     }
 
+    /**
+     * 校验邀请码是否已存在
+     * @param code 邀请码
+     * @return 是否存在
+     */
+    private boolean isInviteCodeExists(String code) {
+        return inviteCodeMapper.selectOne(new LambdaQueryWrapperX<InviteCodeDO>()
+                .eq(InviteCodeDO::getCode, code)) != null;
+    }
+
     /**
      * 生成指定长度的随机邀请码
      * @param length 邀请码长度
@@ -251,6 +315,10 @@ public class InviteCodeServiceImpl implements InviteCodeService {
                 respVO.setUseUserTenantName(tenantMap.get(inviteCode.getUseUserTenantId()));
             }
 
+            // 填充通知状态和通知时间
+            respVO.setNotifyStatus(inviteCode.getNotifyStatus());
+            respVO.setNotifyTime(inviteCode.getNotifyTime());
+
             return respVO;
         }).collect(Collectors.toList());
 
@@ -325,4 +393,57 @@ public class InviteCodeServiceImpl implements InviteCodeService {
         return new PageResult<>(pagedList, (long) total);
     }
 
+    /**
+     * 发送邀请码短信
+     * @param inviteCode 邀请码对象
+     */
+    public void sendInviteCodeSms(InviteCodeDO inviteCode) {
+        try {
+            // 校验手机号是否为空
+            if (inviteCode.getOnlyPhone() == null || inviteCode.getOnlyPhone().isEmpty()) {
+                // 手机号为空,不发送短信
+                inviteCode.setNotifyStatus("2");
+                inviteCode.setNotifyTime(LocalDateTime.now());
+                inviteCodeMapper.updateById(inviteCode);
+                return;
+            }
+
+            // 构建短信模板参数
+            Map<String, Object> templateParams = new HashMap<>();
+            templateParams.put("user", inviteCode.getOnlyPhone());
+            templateParams.put("code", inviteCode.getCode());
+            
+            // 发送短信(使用短信模板编码,需要根据实际情况配置)
+            // 这里假设模板编码为 "invite_code_notify"
+            smsSendService.sendSingleSmsToAdmin(inviteCode.getOnlyPhone(), null, ALY_MEMBER_PUSH_INVITATION_CODE.getTemplateCode(), templateParams);
+            
+            // 更新通知状态为成功
+            inviteCode.setNotifyStatus("1");
+            inviteCode.setNotifyTime(LocalDateTime.now());
+        } catch (Exception e) {
+            // 更新通知状态为失败
+            inviteCode.setNotifyStatus("2");
+            inviteCode.setNotifyTime(LocalDateTime.now());
+        } finally {
+            // 更新邀请码记录
+            inviteCodeMapper.updateById(inviteCode);
+        }
+    }
+
+    /**
+     * 单独发送邀请码短信
+     * @param id 邀请码ID
+     */
+    @Override
+    public void sendSmsById(Long id) {
+        InviteCodeDO inviteCode = inviteCodeMapper.selectById(id);
+        if (inviteCode == null) {
+            throw exception(INVITE_CODE_NOT_EXISTS);
+        }
+        if (inviteCode.getOnlyPhone() == null || inviteCode.getOnlyPhone().isEmpty()) {
+            throw exception(INVITE_CODE_NOT_EXISTS);
+        }
+        sendInviteCodeSms(inviteCode);
+    }
+
 }