mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-12 21:02:08 +08:00
短信管理单元测试完善
This commit is contained in:
+2
@@ -14,6 +14,8 @@ import java.lang.annotation.RetentionPolicy;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@WithSecurityContext(factory = WithMockLoginUserSecurityContextFactory.class)
|
||||
public @interface WithMockLoginUser {
|
||||
long userId() default 0L;
|
||||
|
||||
String username() default "";
|
||||
|
||||
long deptId() default 0L;
|
||||
|
||||
+2
@@ -22,6 +22,8 @@ public class WithMockLoginUserSecurityContextFactory implements WithSecurityCont
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
|
||||
LoginUser loginUser = new LoginUser();
|
||||
long annotationUserId = annotation.userId();
|
||||
loginUser.setId(annotationUserId == 0L ? RandomUtil.randomLong() : annotationUserId);
|
||||
loginUser.setNickname(RandomUtil.randomString(10));
|
||||
loginUser.setDeptId(RandomUtil.randomLong());
|
||||
loginUser.setOrganId(RandomUtil.randomLong());
|
||||
|
||||
-6
@@ -1,14 +1,12 @@
|
||||
package com.cf.imes.module.system.api.sms;
|
||||
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import com.cf.imes.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -29,8 +27,4 @@ public interface SmsCodeApi {
|
||||
@Operation(summary = "验证短信验证码,并进行使用")
|
||||
CommonResult<Boolean> useSmsCode(@Valid @RequestBody SmsCodeUseReqDTO reqDTO);
|
||||
|
||||
@GetMapping(PREFIX + "/validate")
|
||||
@Operation(summary = "检查验证码是否有效")
|
||||
CommonResult<Boolean> validateSmsCode(@Valid @RequestBody SmsCodeValidateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package com.cf.imes.module.system.api.sms;
|
||||
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
|
||||
import com.cf.imes.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO 晨丰:fallbackFactory =
|
||||
@Tag(name = "RPC 服务 - 短信发送")
|
||||
public interface SmsSendApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/sms/send";
|
||||
|
||||
@PostMapping(PREFIX + "/send-single-admin")
|
||||
@Operation(summary = "发送单条短信给 Admin 用户", description = "在 mobile 为空时,使用 userId 加载对应 Admin 的手机号")
|
||||
CommonResult<Long> sendSingleSmsToAdmin(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@PostMapping(PREFIX + "/send-single-member")
|
||||
@Operation(summary = "发送单条短信给 Member 用户", description = "在 mobile 为空时,使用 userId 加载对应 Member 的手机号")
|
||||
CommonResult<Long> sendSingleSmsToMember(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
}
|
||||
-7
@@ -3,7 +3,6 @@ package com.cf.imes.module.system.api.sms;
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import com.cf.imes.module.system.service.sms.SmsCodeService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -31,10 +30,4 @@ public class SmsCodeApiImpl implements SmsCodeApi {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> validateSmsCode(SmsCodeValidateReqDTO reqDTO) {
|
||||
smsCodeService.validateSmsCode(reqDTO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package com.cf.imes.module.system.api.sms;
|
||||
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
|
||||
import com.cf.imes.module.system.service.sms.SmsSendService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import static com.cf.imes.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class SmsSendApiImpl implements SmsSendApi {
|
||||
|
||||
@Resource
|
||||
private SmsSendService smsSendService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendSingleSmsToAdmin(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return success(smsSendService.sendSingleSmsToAdmin(reqDTO.getMobile(), reqDTO.getUserId(),
|
||||
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendSingleSmsToMember(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return success(smsSendService.sendSingleSmsToMember(reqDTO.getMobile(), reqDTO.getUserId(),
|
||||
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
|
||||
}
|
||||
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
package com.cf.imes.module.system.controller.admin.sms;
|
||||
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.framework.common.util.object.BeanUtils;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelRespVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelSimpleRespVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.cf.imes.module.system.service.sms.SmsChannelService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.cf.imes.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 短信渠道")
|
||||
@RestController
|
||||
@RequestMapping("system/sms-channel")
|
||||
public class SmsChannelController {
|
||||
|
||||
@Resource
|
||||
private SmsChannelService smsChannelService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建短信渠道")
|
||||
@PreAuthorize("@ss.hasPermission('system:sms-channel:create')")
|
||||
public CommonResult<Long> createSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO createReqVO) {
|
||||
return success(smsChannelService.createSmsChannel(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新短信渠道")
|
||||
@PreAuthorize("@ss.hasPermission('system:sms-channel:update')")
|
||||
public CommonResult<Boolean> updateSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO updateReqVO) {
|
||||
smsChannelService.updateSmsChannel(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除短信渠道")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('system:sms-channel:delete')")
|
||||
public CommonResult<Boolean> deleteSmsChannel(@RequestParam("id") Long id) {
|
||||
smsChannelService.deleteSmsChannel(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得短信渠道")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:sms-channel:query')")
|
||||
public CommonResult<SmsChannelRespVO> getSmsChannel(@RequestParam("id") Long id) {
|
||||
SmsChannelDO channel = smsChannelService.getSmsChannel(id);
|
||||
return success(BeanUtils.toBean(channel, SmsChannelRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得短信渠道分页")
|
||||
@PreAuthorize("@ss.hasPermission('system:sms-channel:query')")
|
||||
public CommonResult<PageResult<SmsChannelRespVO>> getSmsChannelPage(@Valid SmsChannelPageReqVO pageVO) {
|
||||
PageResult<SmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, SmsChannelRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping({"/list-all-simple", "/simple-list"})
|
||||
@Operation(summary = "获得短信渠道精简列表", description = "包含被禁用的短信渠道")
|
||||
public CommonResult<List<SmsChannelSimpleRespVO>> getSimpleSmsChannelList() {
|
||||
List<SmsChannelDO> list = smsChannelService.getSmsChannelList();
|
||||
list.sort(Comparator.comparing(SmsChannelDO::getId));
|
||||
return success(BeanUtils.toBean(list, SmsChannelSimpleRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package com.cf.imes.module.system.controller.admin.sms;
|
||||
|
||||
import com.cf.imes.framework.common.enums.UserTypeEnum;
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.framework.common.pojo.PageParam;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
@@ -96,7 +97,7 @@ public class SmsTemplateController {
|
||||
@Operation(summary = "发送短信")
|
||||
@PreAuthorize("@ss.hasPermission('system:sms-template:send-sms')")
|
||||
public CommonResult<Long> sendSms(@Valid @RequestBody SmsTemplateSendReqVO sendReqVO) {
|
||||
return success(smsSendService.sendSingleSmsToAdmin(sendReqVO.getMobile(), null,
|
||||
return success(smsSendService.sendSingleSms(sendReqVO.getMobile(), null, UserTypeEnum.ADMIN.getValue(),
|
||||
sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
|
||||
}
|
||||
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package com.cf.imes.module.system.controller.admin.sms.vo.channel;
|
||||
|
||||
import com.cf.imes.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 短信渠道分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class SmsChannelPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "任务状态", example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "短信签名,模糊匹配", example = "晨丰科技")
|
||||
private String signature;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package com.cf.imes.module.system.controller.admin.sms.vo.channel;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 短信渠道 Response VO")
|
||||
@Data
|
||||
public class SmsChannelRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "晨丰科技")
|
||||
@NotNull(message = "短信签名不能为空")
|
||||
private String signature;
|
||||
|
||||
@Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "启用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "启用状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "备注", example = "好吃!")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "短信 API 的账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "chenfeng")
|
||||
@NotNull(message = "短信 API 的账号不能为空")
|
||||
private String apiKey;
|
||||
|
||||
@Schema(description = "短信 API 的密钥", example = "yuanma")
|
||||
private String apiSecret;
|
||||
|
||||
@Schema(description = "短信发送回调 URL", example = "https://www.cf.com")
|
||||
@URL(message = "回调 URL 格式不正确")
|
||||
private String callbackUrl;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package com.cf.imes.module.system.controller.admin.sms.vo.channel;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 短信渠道创建/修改 Request VO")
|
||||
@Data
|
||||
public class SmsChannelSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "晨丰科技")
|
||||
@NotNull(message = "短信签名不能为空")
|
||||
private String signature;
|
||||
|
||||
@Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN")
|
||||
@NotNull(message = "渠道编码不能为空")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "启用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "启用状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "备注", example = "好吃!")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "短信 API 的账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "chenfeng")
|
||||
@NotNull(message = "短信 API 的账号不能为空")
|
||||
private String apiKey;
|
||||
|
||||
@Schema(description = "短信 API 的密钥", example = "yuanma")
|
||||
private String apiSecret;
|
||||
|
||||
@Schema(description = "短信发送回调 URL", example = "http://www.cf.com")
|
||||
@URL(message = "回调 URL 格式不正确")
|
||||
private String callbackUrl;
|
||||
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package com.cf.imes.module.system.controller.admin.sms.vo.channel;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 短信渠道精简 Response VO")
|
||||
@Data
|
||||
public class SmsChannelSimpleRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "晨丰科技")
|
||||
private String signature;
|
||||
|
||||
@Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN")
|
||||
private String code;
|
||||
|
||||
}
|
||||
+6
-6
@@ -3,8 +3,8 @@ package com.cf.imes.module.system.controller.admin.sms.vo.template;
|
||||
import com.cf.imes.module.system.validation.common.CommonStatus;
|
||||
import com.cf.imes.module.system.validation.sms.SmsTemplateTypeValid;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@@ -27,26 +27,26 @@ public class SmsTemplateSaveReqVO {
|
||||
|
||||
@Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01")
|
||||
@NotNull(message = "模板编码不能为空")
|
||||
@Length(min = 1, max = 64, message = "模板编码长度不能超过64个字符")
|
||||
@Size(min = 1, max = 64, message = "模板编码长度不能超过64个字符")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "chenfeng")
|
||||
@NotNull(message = "模板名称不能为空")
|
||||
@Length(min = 1, max = 64, message = "模板名称长度不能超过64个字符")
|
||||
@Size(min = 1, max = 64, message = "模板名称长度不能超过64个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "模板内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,{name}。你长的太{like}啦!")
|
||||
@NotNull(message = "模板内容不能为空")
|
||||
@Length(min = 1, max = 255, message = "模板内容长度不能超过255个字符")
|
||||
@Size(min = 1, max = 255, message = "模板内容长度不能超过255个字符")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "备注", example = "哈哈哈")
|
||||
@Length(max = 255, message = "备注长度不能超过255个字符")
|
||||
@Size(max = 255, message = "备注长度不能超过255个字符")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4383920")
|
||||
@NotNull(message = "短信 API 的模板编号不能为空")
|
||||
@Length(min = 1, max = 64, message = "短信 API 的模板编号长度不能超过64个字符")
|
||||
@Size(min = 1, max = 64, message = "短信 API 的模板编号长度不能超过64个字符")
|
||||
private String apiTemplateId;
|
||||
|
||||
}
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
package com.cf.imes.module.system.dal.dataobject.sms;
|
||||
|
||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||
import com.cf.imes.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.cf.imes.module.system.framework.sms.core.enums.SmsChannelEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 短信渠道 DO
|
||||
*
|
||||
* @author zzf
|
||||
* @since 2021-01-25
|
||||
*/
|
||||
@TableName(value = "system_sms_channel", autoResultMap = true)
|
||||
@KeySequence("system_sms_channel_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class SmsChannelDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 渠道编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 短信签名
|
||||
*/
|
||||
private String signature;
|
||||
/**
|
||||
* 渠道编码
|
||||
*
|
||||
* 枚举 {@link SmsChannelEnum}
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 启用状态
|
||||
*
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
/**
|
||||
* 短信 API 的账号
|
||||
*/
|
||||
private String apiKey;
|
||||
/**
|
||||
* 短信 API 的密钥
|
||||
*/
|
||||
private String apiSecret;
|
||||
/**
|
||||
* 短信发送回调 URL
|
||||
*/
|
||||
private String callbackUrl;
|
||||
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package com.cf.imes.module.system.dal.dataobject.sms;
|
||||
|
||||
import com.cf.imes.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 手机验证码 DO
|
||||
*
|
||||
* idx_mobile 索引:基于 {@link #mobile} 字段
|
||||
*
|
||||
* @author 晨丰科技
|
||||
*/
|
||||
@TableName("system_sms_code")
|
||||
@KeySequence("system_sms_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SmsCodeDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String mobile;
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 发送场景
|
||||
*
|
||||
* 枚举 {@link SmsCodeDO}
|
||||
*/
|
||||
private Integer scene;
|
||||
/**
|
||||
* 创建 IP
|
||||
*/
|
||||
private String createIp;
|
||||
/**
|
||||
* 今日发送的第几条
|
||||
*/
|
||||
private Integer todayIndex;
|
||||
/**
|
||||
* 是否使用
|
||||
*/
|
||||
private Boolean used;
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
private LocalDateTime usedTime;
|
||||
/**
|
||||
* 使用 IP
|
||||
*/
|
||||
private String usedIp;
|
||||
|
||||
}
|
||||
-2
@@ -41,13 +41,11 @@ public class SmsLogDO extends BaseDO {
|
||||
/**
|
||||
* 短信渠道编号
|
||||
*
|
||||
* 关联 {@link SmsChannelDO#getId()}
|
||||
*/
|
||||
private Long channelId;
|
||||
/**
|
||||
* 短信渠道编码
|
||||
*
|
||||
* 冗余 {@link SmsChannelDO#getCode()}
|
||||
*/
|
||||
private String channelCode;
|
||||
|
||||
|
||||
-2
@@ -80,13 +80,11 @@ public class SmsTemplateDO extends BaseDO {
|
||||
/**
|
||||
* 短信渠道编号
|
||||
*
|
||||
* 关联 {@link SmsChannelDO#getId()}
|
||||
*/
|
||||
private Long channelId;
|
||||
/**
|
||||
* 短信渠道编码
|
||||
*
|
||||
* 冗余 {@link SmsChannelDO#getCode()}
|
||||
*/
|
||||
private String channelCode;
|
||||
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package com.cf.imes.module.system.dal.mysql.sms;
|
||||
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
|
||||
|
||||
default PageResult<SmsChannelDO> selectPage(SmsChannelPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<SmsChannelDO>()
|
||||
.likeIfPresent(SmsChannelDO::getSignature, reqVO.getSignature())
|
||||
.eqIfPresent(SmsChannelDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(SmsChannelDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(SmsChannelDO::getId));
|
||||
}
|
||||
|
||||
default SmsChannelDO selectByCode(String code) {
|
||||
return selectOne(SmsChannelDO::getCode, code);
|
||||
}
|
||||
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package com.cf.imes.module.system.dal.mysql.sms;
|
||||
|
||||
import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.cf.imes.framework.mybatis.core.query.QueryWrapperX;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SmsCodeMapper extends BaseMapperX<SmsCodeDO> {
|
||||
|
||||
/**
|
||||
* 获得手机号的最后一个手机验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param scene 发送场景,选填
|
||||
* @param code 验证码 选填
|
||||
* @return 手机验证码
|
||||
*/
|
||||
default SmsCodeDO selectLastByMobile(String mobile, String code, Integer scene) {
|
||||
return selectOne(new QueryWrapperX<SmsCodeDO>()
|
||||
.eq("mobile", mobile)
|
||||
.eqIfPresent("scene", scene)
|
||||
.eqIfPresent("code", code)
|
||||
.orderByDesc("id")
|
||||
.limitN(1));
|
||||
}
|
||||
|
||||
}
|
||||
-8
@@ -9,14 +9,6 @@ import com.cf.imes.module.system.framework.sms.core.property.SmsProperties;
|
||||
* @since 2021/1/28 14:01
|
||||
*/
|
||||
public interface SmsClientFactory {
|
||||
/**
|
||||
* 获得短信 Client
|
||||
*
|
||||
* @param channelCode 渠道编码
|
||||
* @return 短信 Client
|
||||
*/
|
||||
SmsClient getSmsClient(String channelCode);
|
||||
|
||||
/**
|
||||
* 获得短信 Client
|
||||
*
|
||||
|
||||
-5
@@ -55,11 +55,6 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsClient getSmsClient(String channelCode) {
|
||||
return channelCodeClients.get(channelCode);
|
||||
}
|
||||
|
||||
private AbstractSmsClient createSmsClient(SmsProperties smsProperties) {
|
||||
String channel = smsProperties.getChannel();
|
||||
SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(channel);
|
||||
|
||||
-68
@@ -1,13 +1,6 @@
|
||||
package com.cf.imes.module.system.service.sms;
|
||||
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.SmsClient;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 短信渠道 Service 接口
|
||||
@@ -17,67 +10,6 @@ import java.util.List;
|
||||
*/
|
||||
public interface SmsChannelService {
|
||||
|
||||
/**
|
||||
* 创建短信渠道
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createSmsChannel(@Valid SmsChannelSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新短信渠道
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateSmsChannel(@Valid SmsChannelSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除短信渠道
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteSmsChannel(Long id);
|
||||
|
||||
/**
|
||||
* 获得短信渠道
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 短信渠道
|
||||
*/
|
||||
SmsChannelDO getSmsChannel(Long id);
|
||||
|
||||
/**
|
||||
* 获得所有短信渠道列表
|
||||
*
|
||||
* @return 短信渠道列表
|
||||
*/
|
||||
List<SmsChannelDO> getSmsChannelList();
|
||||
|
||||
/**
|
||||
* 获得短信渠道分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 短信渠道分页
|
||||
*/
|
||||
PageResult<SmsChannelDO> getSmsChannelPage(SmsChannelPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得短信客户端
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 短信客户端
|
||||
*/
|
||||
SmsClient getSmsClient(Long id);
|
||||
|
||||
/**
|
||||
* 获得短信客户端
|
||||
*
|
||||
* @param code 编码
|
||||
* @return 短信客户端
|
||||
*/
|
||||
SmsClient getSmsClient(String code);
|
||||
|
||||
/**
|
||||
* 根据配置获得短信客户端
|
||||
*
|
||||
|
||||
-129
@@ -1,28 +1,13 @@
|
||||
package com.cf.imes.module.system.service.sms;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.framework.common.util.object.BeanUtils;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.SmsClient;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.SmsClientFactory;
|
||||
import com.cf.imes.module.system.framework.sms.core.property.SmsProperties;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.cf.imes.module.system.dal.mysql.sms.SmsChannelMapper;
|
||||
import com.cf.imes.module.system.enums.ErrorCodeConstants;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import static com.cf.imes.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
|
||||
|
||||
/**
|
||||
* 短信渠道 Service 实现类
|
||||
@@ -36,123 +21,9 @@ public class SmsChannelServiceImpl implements SmsChannelService {
|
||||
@Resource
|
||||
private SmsClientFactory smsClientFactory;
|
||||
|
||||
@Resource
|
||||
private SmsChannelMapper smsChannelMapper;
|
||||
|
||||
@Resource
|
||||
private SmsTemplateService smsTemplateService;
|
||||
|
||||
@Resource
|
||||
private SmsProperties smsProperties;
|
||||
|
||||
/**
|
||||
* {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory
|
||||
*/
|
||||
@Getter
|
||||
private final LoadingCache<Long, SmsClient> idClientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
|
||||
new CacheLoader<>() {
|
||||
|
||||
@Override
|
||||
public SmsClient load(Long id) {
|
||||
return smsClientFactory.getSmsClient(smsProperties);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory
|
||||
*/
|
||||
@Getter
|
||||
private final LoadingCache<String, SmsClient> codeClientCache = buildAsyncReloadingCache(Duration.ofSeconds(60L),
|
||||
new CacheLoader<>() {
|
||||
|
||||
@Override
|
||||
public SmsClient load(String code) {
|
||||
return smsClientFactory.getSmsClient(smsProperties);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Override
|
||||
public Long createSmsChannel(SmsChannelSaveReqVO createReqVO) {
|
||||
SmsChannelDO channel = BeanUtils.toBean(createReqVO, SmsChannelDO.class);
|
||||
smsChannelMapper.insert(channel);
|
||||
return channel.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSmsChannel(SmsChannelSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
SmsChannelDO channel = validateSmsChannelExists(updateReqVO.getId());
|
||||
// 更新
|
||||
SmsChannelDO updateObj = BeanUtils.toBean(updateReqVO, SmsChannelDO.class);
|
||||
smsChannelMapper.updateById(updateObj);
|
||||
|
||||
// 清空缓存
|
||||
clearCache(updateReqVO.getId(), channel.getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSmsChannel(Long id) {
|
||||
// 校验存在
|
||||
SmsChannelDO channel = validateSmsChannelExists(id);
|
||||
// 校验是否有在使用该账号的模版
|
||||
if (smsTemplateService.getSmsTemplateCountByChannelId(id) > 0) {
|
||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN);
|
||||
}
|
||||
// 删除
|
||||
smsChannelMapper.deleteById(id);
|
||||
|
||||
// 清空缓存
|
||||
clearCache(id, channel.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空指定渠道编号的缓存
|
||||
*
|
||||
* @param id 渠道编号
|
||||
* @param code 渠道编码
|
||||
*/
|
||||
private void clearCache(Long id, String code) {
|
||||
idClientCache.invalidate(id);
|
||||
if (CharSequenceUtil.isNotEmpty(code)) {
|
||||
codeClientCache.invalidate(code);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsChannelDO validateSmsChannelExists(Long id) {
|
||||
SmsChannelDO channel = smsChannelMapper.selectById(id);
|
||||
if (channel == null) {
|
||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsChannelDO getSmsChannel(Long id) {
|
||||
return smsChannelMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SmsChannelDO> getSmsChannelList() {
|
||||
return smsChannelMapper.selectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SmsChannelDO> getSmsChannelPage(SmsChannelPageReqVO pageReqVO) {
|
||||
return smsChannelMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsClient getSmsClient(Long id) {
|
||||
return idClientCache.getUnchecked(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsClient getSmsClient(String code) {
|
||||
return smsClientFactory.getSmsClient(smsProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsClient getSmsClient() {
|
||||
return smsClientFactory.getSmsClient(smsProperties);
|
||||
|
||||
-8
@@ -3,7 +3,6 @@ package com.cf.imes.module.system.service.sms;
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginSmsCheckReqVO;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
@@ -31,13 +30,6 @@ public interface SmsCodeService {
|
||||
*/
|
||||
void useSmsCode(@Valid SmsCodeUseReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 检查验证码是否有效
|
||||
*
|
||||
* @param reqDTO 校验请求
|
||||
*/
|
||||
void validateSmsCode(@Valid SmsCodeValidateReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 检查验证码是否有效
|
||||
*
|
||||
|
||||
-32
@@ -1,6 +1,5 @@
|
||||
package com.cf.imes.module.system.service.sms;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
@@ -10,11 +9,8 @@ import com.cf.imes.framework.security.core.LoginUser;
|
||||
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginSmsCheckReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.cf.imes.module.system.dal.mysql.sms.SmsCodeMapper;
|
||||
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
|
||||
import com.cf.imes.module.system.enums.sms.SmsSceneEnum;
|
||||
import com.cf.imes.module.system.framework.sms.config.SmsCodeProperties;
|
||||
@@ -27,7 +23,6 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -45,9 +40,6 @@ public class SmsCodeServiceImpl implements SmsCodeService {
|
||||
@Resource
|
||||
private SmsCodeProperties smsCodeProperties;
|
||||
|
||||
@Resource
|
||||
private SmsCodeMapper smsCodeMapper;
|
||||
|
||||
@Resource
|
||||
private SmsSendService smsSendService;
|
||||
|
||||
@@ -215,28 +207,4 @@ public class SmsCodeServiceImpl implements SmsCodeService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateSmsCode(SmsCodeValidateReqDTO reqDTO) {
|
||||
validateSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene());
|
||||
}
|
||||
|
||||
private SmsCodeDO validateSmsCode0(String mobile, String code, Integer scene) {
|
||||
// 校验验证码
|
||||
SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, scene);
|
||||
// 若验证码不存在,抛出异常
|
||||
if (lastSmsCode == null) {
|
||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.SMS_CODE_NOT_FOUND);
|
||||
}
|
||||
// 超过时间
|
||||
if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis()
|
||||
>= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期
|
||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.SMS_CODE_EXPIRED);
|
||||
}
|
||||
// 判断验证码是否已被使用
|
||||
if (Boolean.TRUE.equals(lastSmsCode.getUsed())) {
|
||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.SMS_CODE_USED);
|
||||
}
|
||||
return lastSmsCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -32,8 +32,7 @@ public class SmsLogServiceImpl implements SmsLogService {
|
||||
SmsTemplateDO template, String templateContent, Map<String, Object> templateParams) {
|
||||
SmsLogDO.SmsLogDOBuilder logBuilder = SmsLogDO.builder();
|
||||
// 根据是否要发送,设置状态
|
||||
logBuilder.sendStatus(Objects.equals(isSend, true) ? SmsSendStatusEnum.INIT.getStatus()
|
||||
: SmsSendStatusEnum.IGNORE.getStatus());
|
||||
logBuilder.sendStatus(SmsSendStatusEnum.INIT.getStatus());
|
||||
// 设置手机相关字段
|
||||
logBuilder.mobile(mobile).userId(userId).userType(userType);
|
||||
// 设置模板相关字段
|
||||
|
||||
-29
@@ -11,35 +11,6 @@ import java.util.Map;
|
||||
* @author 晨丰科技
|
||||
*/
|
||||
public interface SmsSendService {
|
||||
|
||||
/**
|
||||
* 发送单条短信给管理后台的用户
|
||||
*
|
||||
* 在 mobile 为空时,使用 userId 加载对应管理员的手机号
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param userId 用户编号
|
||||
* @param templateCode 短信模板编号
|
||||
* @param templateParams 短信模板参数
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
Long sendSingleSmsToAdmin(String mobile, Long userId,
|
||||
String templateCode, Map<String, Object> templateParams);
|
||||
|
||||
/**
|
||||
* 发送单条短信给用户 APP 的用户
|
||||
*
|
||||
* 在 mobile 为空时,使用 userId 加载对应会员的手机号
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param userId 用户编号
|
||||
* @param templateCode 短信模板编号
|
||||
* @param templateParams 短信模板参数
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
Long sendSingleSmsToMember(String mobile, Long userId,
|
||||
String templateCode, Map<String, Object> templateParams);
|
||||
|
||||
/**
|
||||
* 发送单条短信给用户
|
||||
*
|
||||
|
||||
+15
-37
@@ -5,22 +5,20 @@ import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.cf.imes.framework.common.core.KeyValue;
|
||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||
import com.cf.imes.framework.common.enums.UserTypeEnum;
|
||||
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
|
||||
import com.cf.imes.module.system.framework.sms.config.SmsCodeProperties;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.SmsClient;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.cf.imes.module.system.mq.message.sms.SmsSendMessage;
|
||||
import com.cf.imes.module.system.mq.producer.sms.SmsProducer;
|
||||
import com.cf.imes.module.system.service.member.MemberService;
|
||||
import com.cf.imes.module.system.service.sms.handler.SmsSendAfterSendHandler;
|
||||
import com.cf.imes.module.system.service.user.AdminUserService;
|
||||
import com.cf.imes.module.system.enums.ErrorCodeConstants;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -37,10 +35,6 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
public class SmsSendServiceImpl implements SmsSendService {
|
||||
|
||||
@Resource
|
||||
private AdminUserService adminUserService;
|
||||
@Resource
|
||||
private MemberService memberService;
|
||||
@Resource
|
||||
private SmsChannelService smsChannelService;
|
||||
@Resource
|
||||
@@ -52,30 +46,10 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||
private SmsProducer smsProducer;
|
||||
|
||||
@Resource
|
||||
private List<SmsSendAfterSendHandler> smsSendAfterSendHandlers;
|
||||
private RedisTemplate redisTemplate;
|
||||
|
||||
@Override
|
||||
public Long sendSingleSmsToAdmin(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) {
|
||||
// 如果 mobile 为空,则加载用户编号对应的手机号
|
||||
if (StringUtils.isEmpty(mobile)) {
|
||||
AdminUserDO user = adminUserService.getUser(userId);
|
||||
if (user != null) {
|
||||
mobile = user.getMobile();
|
||||
}
|
||||
}
|
||||
// 执行发送
|
||||
return sendSingleSms(mobile, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) {
|
||||
// 如果 mobile 为空,则加载用户编号对应的手机号
|
||||
if (StringUtils.isEmpty(mobile)) {
|
||||
mobile = memberService.getMemberUserMobile(userId);
|
||||
}
|
||||
// 执行发送
|
||||
return sendSingleSms(mobile, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);
|
||||
}
|
||||
@Resource
|
||||
private SmsCodeProperties smsCodeProperties;
|
||||
|
||||
@Override
|
||||
public Long sendSingleSms(String mobile, Long userId, Integer userType,
|
||||
@@ -95,7 +69,9 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||
|
||||
// 发送 MQ 消息,异步执行发送短信
|
||||
if (isSend) {
|
||||
smsProducer.sendSmsSendMessage(sendLogId, mobile, template, newTemplateParams);
|
||||
SmsSendMessage message = new SmsSendMessage().setLogId(sendLogId).setMobile(mobile);
|
||||
message.setChannelId(template.getChannelId()).setApiTemplateId(template.getApiTemplateId()).setTemplateType(template.getType()).setTemplateParams(newTemplateParams);
|
||||
doSendSms(message);
|
||||
}
|
||||
return sendLogId;
|
||||
}
|
||||
@@ -153,9 +129,11 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||
message.getApiTemplateId(), templateParams);
|
||||
channelCode = sendResponse.getChannelCode();
|
||||
// 发送成功后操作
|
||||
for (SmsSendAfterSendHandler afterSendHandler : smsSendAfterSendHandlers) {
|
||||
if (afterSendHandler.checkTemplateType(message.getTemplateType())) {
|
||||
afterSendHandler.afterSend(sendResponse, templateParams);
|
||||
if (sendResponse.getSuccess()) {
|
||||
for (KeyValue<String, Object> keyValue : templateParams) {
|
||||
if ("code".equals(keyValue.getKey())) {
|
||||
redisTemplate.opsForValue().set(String.format(RedisKeyConstants.SMS_CAPTCHA_VERIFICATION, sendResponse.getMobile()), keyValue.getValue(), smsCodeProperties.getExpireTimes());
|
||||
}
|
||||
}
|
||||
}
|
||||
smsLogService.updateSmsSendResult(message.getLogId(), sendResponse.getSuccess(),
|
||||
@@ -171,7 +149,7 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||
@Override
|
||||
public void receiveSmsStatus(String channelCode, String text) throws Throwable {
|
||||
// 获得渠道对应的 SmsClient 客户端
|
||||
SmsClient smsClient = smsChannelService.getSmsClient(channelCode);
|
||||
SmsClient smsClient = smsChannelService.getSmsClient();
|
||||
Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode);
|
||||
// 解析内容
|
||||
List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text);
|
||||
|
||||
+1
-14
@@ -5,7 +5,6 @@ import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.framework.common.util.object.BeanUtils;
|
||||
@@ -14,7 +13,6 @@ import com.cf.imes.module.system.framework.sms.core.client.dto.SmsTemplateRespDT
|
||||
import com.cf.imes.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.cf.imes.module.system.dal.mysql.sms.SmsTemplateMapper;
|
||||
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
|
||||
@@ -122,18 +120,6 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
|
||||
return smsTemplateMapper.selectCountByChannelId(channelId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public SmsChannelDO validateSmsChannel(Long channelId) {
|
||||
SmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId);
|
||||
if (channelDO == null) {
|
||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS);
|
||||
}
|
||||
if (CommonStatusEnum.isDisable(channelDO.getStatus())) {
|
||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.SMS_CHANNEL_DISABLE);
|
||||
}
|
||||
return channelDO;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void validateSmsTemplateCodeDuplicate(Long id, String code) {
|
||||
SmsTemplateDO template = smsTemplateMapper.selectByCode(code);
|
||||
@@ -154,6 +140,7 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
|
||||
*
|
||||
* @param apiTemplateId API 模板编号
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void validateApiTemplate(String apiTemplateId) {
|
||||
// 获得短信模板
|
||||
SmsClient smsClient = smsChannelService.getSmsClient();
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package com.cf.imes.module.system.service.sms.handler;
|
||||
|
||||
import com.cf.imes.framework.common.core.KeyValue;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 短信发送后处理器接口
|
||||
*
|
||||
* @author Gqr
|
||||
* @since 2024/8/12 18:42
|
||||
*/
|
||||
public interface SmsSendAfterSendHandler {
|
||||
/**
|
||||
* 校验消息模板类型
|
||||
*
|
||||
* @param templateType
|
||||
* @return
|
||||
*/
|
||||
boolean checkTemplateType(Integer templateType);
|
||||
|
||||
|
||||
void afterSend(SmsSendRespDTO sendResponse, List<KeyValue<String, Object>> params);
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package com.cf.imes.module.system.service.sms.handler.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.cf.imes.framework.common.core.KeyValue;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
|
||||
import com.cf.imes.module.system.enums.sms.SmsTemplateTypeEnum;
|
||||
import com.cf.imes.module.system.framework.sms.config.SmsCodeProperties;
|
||||
import com.cf.imes.module.system.service.sms.handler.SmsSendAfterSendHandler;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Gqr
|
||||
* @since 2024/8/12 18:49
|
||||
*/
|
||||
@Service
|
||||
public class SmsSendAfterSendHandlerImpl implements SmsSendAfterSendHandler {
|
||||
|
||||
@Resource
|
||||
private RedisTemplate redisTemplate;
|
||||
|
||||
@Resource
|
||||
private SmsCodeProperties smsCodeProperties;
|
||||
|
||||
@Override
|
||||
public boolean checkTemplateType(Integer templateType) {
|
||||
SmsTemplateTypeEnum smsTemplateTypeEnum = SmsTemplateTypeEnum.valueOf(templateType);
|
||||
return ObjectUtil.isNotNull(smsTemplateTypeEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSend(SmsSendRespDTO sendResponse, List<KeyValue<String, Object>> params) {
|
||||
// 发送成功把验证码存入redis
|
||||
if (sendResponse.getSuccess()) {
|
||||
for (KeyValue<String, Object> keyValue : params) {
|
||||
if ("code".equals(keyValue.getKey())) {
|
||||
redisTemplate.opsForValue().set(String.format(RedisKeyConstants.SMS_CAPTCHA_VERIFICATION, sendResponse.getMobile()), keyValue.getValue(), smsCodeProperties.getExpireTimes());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,6 @@ logging:
|
||||
# 配置自己写的 MyBatis Mapper 打印日志
|
||||
com.cf.imes.module.system.dal.mysql: debug
|
||||
com.cf.imes.module.system.dal.mysql.sensitiveword.SensitiveWordMapper: INFO # 配置 SensitiveWordMapper 的日志级别为 info
|
||||
com.cf.imes.module.system.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info
|
||||
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO :先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示
|
||||
|
||||
--- #################### 微信公众号、小程序相关配置 ####################
|
||||
|
||||
-2
@@ -1,6 +1,5 @@
|
||||
package com.cf.imes.module.system.controller.admin.dept;
|
||||
|
||||
import com.cf.imes.framework.security.test.WithMockLoginUser;
|
||||
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.dept.DeptDO;
|
||||
@@ -59,7 +58,6 @@ public class DeptControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockLoginUser(isSuperAdmin = true)
|
||||
void testCreateDept() throws Exception {
|
||||
when(deptService.createDept(any())).thenReturn(123L);
|
||||
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.cf.imes.module.system.controller.admin.sms;
|
||||
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsLogDO;
|
||||
import com.cf.imes.module.system.service.sms.SmsLogService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Gqr
|
||||
* @since 2025/10/10 15:37
|
||||
*/
|
||||
@WebMvcTest(controllers = SmsLogController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
|
||||
public class SmsLogControllerTest {
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockitoBean
|
||||
private SmsLogService smsLogService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
/**
|
||||
* 测试分页查询接口
|
||||
*/
|
||||
@Test
|
||||
void testGetSmsLogPage_success() throws Exception {
|
||||
// mock service 层返回分页结果
|
||||
SmsLogDO logDO = new SmsLogDO();
|
||||
logDO.setId(1L);
|
||||
logDO.setMobile("13800000000");
|
||||
logDO.setSendStatus(1);
|
||||
PageResult<SmsLogDO> pageResult = new PageResult<>(Collections.singletonList(logDO), 1L);
|
||||
Mockito.when(smsLogService.getSmsLogPage(any())).thenReturn(pageResult);
|
||||
|
||||
// 构造查询参数
|
||||
SmsLogPageReqVO reqVO = new SmsLogPageReqVO();
|
||||
reqVO.setPageNo(1);
|
||||
reqVO.setPageSize(10);
|
||||
|
||||
// 执行请求
|
||||
mockMvc.perform(get("/system/sms-log/page")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.param("pageNo", "1")
|
||||
.param("pageSize", "10"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.list[0].mobile").value("13800000000"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试导出 Excel 接口
|
||||
*/
|
||||
@Test
|
||||
void testExportSmsLogExcel_success() throws Exception {
|
||||
// mock service 层返回数据
|
||||
SmsLogDO logDO = new SmsLogDO();
|
||||
logDO.setId(2L);
|
||||
logDO.setMobile("13900000000");
|
||||
PageResult<SmsLogDO> pageResult = new PageResult<>(List.of(logDO), 1L);
|
||||
Mockito.when(smsLogService.getSmsLogPage(any())).thenReturn(pageResult);
|
||||
|
||||
// 执行请求
|
||||
mockMvc.perform(get("/system/sms-log/export")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.param("pageNo", "1")
|
||||
.param("pageSize", "10"))
|
||||
.andExpect(status().isOk())
|
||||
// 校验响应头是下载 Excel 文件
|
||||
.andExpect(header().string("Content-Disposition", org.hamcrest.Matchers.containsString("attachment")));
|
||||
}
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
package com.cf.imes.module.system.controller.admin.sms;
|
||||
|
||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.template.SmsTemplateSendReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.cf.imes.module.system.enums.sms.SmsTemplateTypeEnum;
|
||||
import com.cf.imes.module.system.service.sms.SmsSendService;
|
||||
import com.cf.imes.module.system.service.sms.SmsTemplateService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Gqr
|
||||
* @since 2025/10/11 16:26
|
||||
*/
|
||||
@WebMvcTest(controllers = SmsTemplateController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
|
||||
public class SmsTemplateControllerTest {
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockitoBean
|
||||
private SmsTemplateService smsTemplateService;
|
||||
|
||||
@MockitoBean
|
||||
private SmsSendService smsSendService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
// ========== create ==========
|
||||
@Test
|
||||
void testCreateSmsTemplate() throws Exception {
|
||||
SmsTemplateSaveReqVO req = new SmsTemplateSaveReqVO();
|
||||
req.setCode("REGISTER_CODE");
|
||||
req.setName("注册验证码");
|
||||
req.setContent("您的验证码是{code}");
|
||||
req.setType(SmsTemplateTypeEnum.NOTICE.getType());
|
||||
req.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
req.setApiTemplateId("TEMPLATE_001");
|
||||
|
||||
when(smsTemplateService.createSmsTemplate(any())).thenReturn(1L);
|
||||
|
||||
mockMvc.perform(post("/system/sms-template/create")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(req)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data").value(1));
|
||||
|
||||
verify(smsTemplateService, times(1)).createSmsTemplate(any());
|
||||
}
|
||||
|
||||
// ========== update ==========
|
||||
@Test
|
||||
void testUpdateSmsTemplate() throws Exception {
|
||||
SmsTemplateSaveReqVO req = new SmsTemplateSaveReqVO();
|
||||
req.setId(100L);
|
||||
req.setCode("REGISTER_CODE");
|
||||
req.setName("注册验证码");
|
||||
req.setContent("您的验证码是{code}");
|
||||
req.setType(SmsTemplateTypeEnum.NOTICE.getType());
|
||||
req.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
req.setApiTemplateId("TEMPLATE_001");
|
||||
|
||||
doNothing().when(smsTemplateService).updateSmsTemplate(any());
|
||||
|
||||
mockMvc.perform(put("/system/sms-template/update")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(req)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data").value(true));
|
||||
|
||||
verify(smsTemplateService, times(1)).updateSmsTemplate(any());
|
||||
}
|
||||
|
||||
// ========== delete ==========
|
||||
@Test
|
||||
void testDeleteSmsTemplate() throws Exception {
|
||||
doNothing().when(smsTemplateService).deleteSmsTemplate(1L);
|
||||
|
||||
mockMvc.perform(delete("/system/sms-template/delete")
|
||||
.param("id", "1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data").value(true));
|
||||
|
||||
verify(smsTemplateService, times(1)).deleteSmsTemplate(1L);
|
||||
}
|
||||
|
||||
// ========== get ==========
|
||||
@Test
|
||||
void testGetSmsTemplate() throws Exception {
|
||||
SmsTemplateDO templateDO = new SmsTemplateDO();
|
||||
templateDO.setId(1L);
|
||||
templateDO.setCode("REGISTER_CODE");
|
||||
templateDO.setName("注册验证码");
|
||||
when(smsTemplateService.getSmsTemplate(1L)).thenReturn(templateDO);
|
||||
|
||||
mockMvc.perform(get("/system/sms-template/get")
|
||||
.param("id", "1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.code").value("REGISTER_CODE"))
|
||||
.andExpect(jsonPath("$.data.name").value("注册验证码"));
|
||||
|
||||
verify(smsTemplateService, times(1)).getSmsTemplate(1L);
|
||||
}
|
||||
|
||||
// ========== page ==========
|
||||
@Test
|
||||
void testGetSmsTemplatePage() throws Exception {
|
||||
SmsTemplateDO templateDO = new SmsTemplateDO();
|
||||
templateDO.setCode("REGISTER_CODE");
|
||||
PageResult<SmsTemplateDO> pageResult = new PageResult<>(List.of(templateDO), 1L);
|
||||
when(smsTemplateService.getSmsTemplatePage(any())).thenReturn(pageResult);
|
||||
|
||||
mockMvc.perform(get("/system/sms-template/page")
|
||||
.param("pageNo", "1")
|
||||
.param("pageSize", "10"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.list[0].code").value("REGISTER_CODE"));
|
||||
|
||||
verify(smsTemplateService, times(1)).getSmsTemplatePage(any());
|
||||
}
|
||||
|
||||
// ========== send-sms ==========
|
||||
@Test
|
||||
void testSendSms() throws Exception {
|
||||
SmsTemplateSendReqVO sendReqVO = new SmsTemplateSendReqVO();
|
||||
sendReqVO.setMobile("13800000000");
|
||||
sendReqVO.setTemplateCode("SMS_CODE");
|
||||
sendReqVO.setTemplateParams(Map.of("code", "123456"));
|
||||
|
||||
when(smsSendService.sendSingleSms(anyString(), isNull(), anyInt(), anyString(), anyMap()))
|
||||
.thenReturn(123L);
|
||||
|
||||
mockMvc.perform(post("/system/sms-template/send-sms")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(sendReqVO)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data").value(123));
|
||||
|
||||
verify(smsSendService, times(1)).sendSingleSms(
|
||||
eq("13800000000"),
|
||||
isNull(),
|
||||
anyInt(),
|
||||
eq("SMS_CODE"),
|
||||
anyMap()
|
||||
);
|
||||
}
|
||||
|
||||
// ========== export ==========
|
||||
@Test
|
||||
void testExportSmsTemplateExcel() throws Exception {
|
||||
SmsTemplateDO templateDO = new SmsTemplateDO();
|
||||
templateDO.setId(1L);
|
||||
templateDO.setCode("REGISTER_CODE");
|
||||
templateDO.setName("注册验证码");
|
||||
PageResult<SmsTemplateDO> pageResult = new PageResult<>(List.of(templateDO), 1L);
|
||||
when(smsTemplateService.getSmsTemplatePage(any())).thenReturn(pageResult);
|
||||
mockMvc.perform(get("/system/sms-template/export-excel")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.param("pageNo", "1")
|
||||
.param("pageSize", "10"))
|
||||
.andExpect(status().isOk())
|
||||
// 校验响应头是下载 Excel 文件
|
||||
.andExpect(header().string("Content-Disposition", org.hamcrest.Matchers.containsString("attachment")));
|
||||
}
|
||||
}
|
||||
+35
-38
@@ -1,7 +1,6 @@
|
||||
package com.cf.imes.module.system.service.organ;
|
||||
|
||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.framework.organ.config.OrganProperties;
|
||||
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
|
||||
@@ -14,27 +13,25 @@ import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
|
||||
import com.cf.imes.module.system.dal.dataobject.organ.TenantPackageDO;
|
||||
import com.cf.imes.module.system.dal.mysql.organ.OrganMapper;
|
||||
import com.cf.imes.module.system.enums.permission.RoleCodeEnum;
|
||||
import com.cf.imes.module.system.enums.permission.RoleTypeEnum;
|
||||
import com.cf.imes.module.system.service.permission.MenuService;
|
||||
import com.cf.imes.module.system.service.permission.PermissionService;
|
||||
import com.cf.imes.module.system.service.permission.RoleService;
|
||||
import com.cf.imes.module.system.service.organ.handler.OrganInfoHandler;
|
||||
import com.cf.imes.module.system.service.organ.handler.OrganMenuHandler;
|
||||
import com.cf.imes.module.system.service.user.AdminUserService;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import static com.cf.imes.framework.test.core.util.RandomUtils.*;
|
||||
import static com.cf.imes.framework.common.util.collection.SetUtils.asSet;
|
||||
import static com.cf.imes.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||
import static com.cf.imes.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||
import static com.cf.imes.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
@@ -60,27 +57,27 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
@Resource
|
||||
private OrganMapper tenantMapper;
|
||||
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private OrganProperties tenantProperties;
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private TenantPackageService tenantPackageService;
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private AdminUserService userService;
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private RoleService roleService;
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private MenuService menuService;
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private PermissionService permissionService;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
void setUp() {
|
||||
// 清理组织上下文
|
||||
OrganContextHolder.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testgetOrganIdList() {
|
||||
void testgetOrganIdList() {
|
||||
// mock 数据
|
||||
OrganizationDO tenant = randomPojo(OrganizationDO.class, o -> o.setId(1L));
|
||||
tenantMapper.insert(tenant);
|
||||
@@ -91,12 +88,12 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidTenant_notExists() {
|
||||
void testValidTenant_notExists() {
|
||||
assertServiceException(() -> tenantService.validOrgan(randomLongId()), ORGAN_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidTenant_disable() {
|
||||
void testValidTenant_disable() {
|
||||
// mock 数据
|
||||
OrganizationDO tenant = randomPojo(OrganizationDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
tenantMapper.insert(tenant);
|
||||
@@ -106,10 +103,10 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidTenant_expired() {
|
||||
void testValidTenant_expired() {
|
||||
// mock 数据
|
||||
OrganizationDO tenant = randomPojo(OrganizationDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||
.setExpireTime(buildTime(2020, 2, 2)));
|
||||
.setExpireTime(LocalDate.now().minusDays(1)));
|
||||
tenantMapper.insert(tenant);
|
||||
|
||||
// 调用,并断言业务异常
|
||||
@@ -117,10 +114,10 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidTenant_success() {
|
||||
void testValidTenant_success() {
|
||||
// mock 数据
|
||||
OrganizationDO tenant = randomPojo(OrganizationDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||
.setExpireTime(LocalDateTime.now().plusDays(1)));
|
||||
.setExpireTime(LocalDate.now().plusDays(1)));
|
||||
tenantMapper.insert(tenant);
|
||||
|
||||
// 调用,并断言业务异常
|
||||
@@ -128,7 +125,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTenant() {
|
||||
void testCreateTenant() {
|
||||
// mock 套餐 100L
|
||||
TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, o -> o.setId(100L));
|
||||
when(tenantPackageService.validTenantPackage(eq(100L))).thenReturn(tenantPackage);
|
||||
@@ -175,7 +172,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTenant_success() {
|
||||
void testUpdateTenant_success() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant = randomPojo(OrganizationDO.class, o -> o.setStatus(randomCommonStatus()));
|
||||
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
|
||||
@@ -210,7 +207,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTenant_notExists() {
|
||||
void testUpdateTenant_notExists() {
|
||||
// 准备参数
|
||||
OrganSaveReqVO reqVO = randomPojo(OrganSaveReqVO.class);
|
||||
|
||||
@@ -219,7 +216,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTenant_system() {
|
||||
void testUpdateTenant_system() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant = randomPojo(OrganizationDO.class, o -> o.setPackageId(OrganizationDO.PACKAGE_ID_SYSTEM));
|
||||
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
|
||||
@@ -233,7 +230,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteTenant_success() {
|
||||
void testDeleteTenant_success() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant = randomPojo(OrganizationDO.class,
|
||||
o -> o.setStatus(randomCommonStatus()));
|
||||
@@ -248,7 +245,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteTenant_notExists() {
|
||||
void testDeleteTenant_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
@@ -257,7 +254,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteTenant_system() {
|
||||
void testDeleteTenant_system() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant = randomPojo(OrganizationDO.class, o -> o.setPackageId(OrganizationDO.PACKAGE_ID_SYSTEM));
|
||||
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
|
||||
@@ -269,7 +266,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTenant() {
|
||||
void testGetTenant() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant = randomPojo(OrganizationDO.class);
|
||||
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
|
||||
@@ -283,7 +280,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTenantPage() {
|
||||
void testGetTenantPage() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant = randomPojo(OrganizationDO.class, o -> { // 等会查询到
|
||||
o.setName("晨丰科技");
|
||||
@@ -319,7 +316,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTenantByName() {
|
||||
void testGetTenantByName() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant = randomPojo(OrganizationDO.class, o -> o.setName("晨丰"));
|
||||
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
|
||||
@@ -331,7 +328,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTenantByWebsite() {
|
||||
void testGetTenantByWebsite() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant = randomPojo(OrganizationDO.class, o -> o.setWebsite("https://www.cf.com"));
|
||||
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
|
||||
@@ -343,7 +340,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTenantListByPackageId() {
|
||||
void testGetTenantListByPackageId() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant1 = randomPojo(OrganizationDO.class, o -> o.setPackageId(1L));
|
||||
tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据
|
||||
@@ -357,7 +354,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTenantCountByPackageId() {
|
||||
void testGetTenantCountByPackageId() {
|
||||
// mock 数据
|
||||
OrganizationDO dbTenant1 = randomPojo(OrganizationDO.class, o -> o.setPackageId(1L));
|
||||
tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据
|
||||
@@ -370,7 +367,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTenantInfo_disable() {
|
||||
void testHandleTenantInfo_disable() {
|
||||
// 准备参数
|
||||
OrganInfoHandler handler = mock(OrganInfoHandler.class);
|
||||
// mock 禁用
|
||||
@@ -383,7 +380,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTenantInfo_success() {
|
||||
void testHandleTenantInfo_success() {
|
||||
// 准备参数
|
||||
OrganInfoHandler handler = mock(OrganInfoHandler.class);
|
||||
// mock 未禁用
|
||||
@@ -403,7 +400,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTenantMenu_disable() {
|
||||
void testHandleTenantMenu_disable() {
|
||||
// 准备参数
|
||||
OrganMenuHandler handler = mock(OrganMenuHandler.class);
|
||||
// mock 禁用
|
||||
@@ -416,7 +413,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test // 系统组织的情况
|
||||
public void testHandleTenantMenu_system() {
|
||||
void testHandleTenantMenu_system() {
|
||||
// 准备参数
|
||||
OrganMenuHandler handler = mock(OrganMenuHandler.class);
|
||||
// mock 未禁用
|
||||
@@ -436,7 +433,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test // 普通组织的情况
|
||||
public void testHandleTenantMenu_normal() {
|
||||
void testHandleTenantMenu_normal() {
|
||||
// 准备参数
|
||||
OrganMenuHandler handler = mock(OrganMenuHandler.class);
|
||||
// mock 未禁用
|
||||
|
||||
+18
-206
@@ -1,229 +1,41 @@
|
||||
package com.cf.imes.module.system.service.sms;
|
||||
|
||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.framework.common.util.object.BeanUtils;
|
||||
import com.cf.imes.module.system.framework.sms.config.ChenfengSmsAutoConfiguration;
|
||||
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.SmsClient;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.SmsClientFactory;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.impl.aliyun.AliyunSmsClient;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient;
|
||||
import com.cf.imes.module.system.framework.sms.core.enums.SmsChannelEnum;
|
||||
import com.cf.imes.module.system.framework.sms.core.property.SmsChannelProperties;
|
||||
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.cf.imes.module.system.dal.mysql.sms.SmsChannelMapper;
|
||||
import com.cf.imes.module.system.framework.sms.core.property.SmsProperties;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static com.cf.imes.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||
import static com.cf.imes.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||
import static com.cf.imes.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static com.cf.imes.framework.test.core.util.RandomUtils.*;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
@Import(SmsChannelServiceImpl.class)
|
||||
|
||||
@Import({SmsChannelServiceImpl.class, ChenfengSmsAutoConfiguration.class})
|
||||
public class SmsChannelServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private SmsChannelServiceImpl smsChannelService;
|
||||
|
||||
@Resource
|
||||
private SmsChannelMapper smsChannelMapper;
|
||||
private SmsProperties smsProperties; // 真实 Bean
|
||||
|
||||
@MockBean
|
||||
private SmsClientFactory smsClientFactory;
|
||||
@MockBean
|
||||
private SmsTemplateService smsTemplateService;
|
||||
@Resource
|
||||
private SmsClientFactory smsClientFactory; // 真实 Bean
|
||||
|
||||
@Test
|
||||
public void testCreateSmsChannel_success() {
|
||||
// 准备参数
|
||||
SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class, o -> o.setStatus(randomCommonStatus()))
|
||||
.setId(null); // 防止 id 被赋值
|
||||
void testGetSmsClient_aliyun() {
|
||||
smsProperties.setChannel(SmsChannelEnum.ALIYUN.getCode());
|
||||
SmsClient aliyunClient = smsChannelService.getSmsClient();
|
||||
assertThat(aliyunClient).isInstanceOf(AliyunSmsClient.class);
|
||||
|
||||
// 调用
|
||||
Long smsChannelId = smsChannelService.createSmsChannel(reqVO);
|
||||
// 断言
|
||||
assertNotNull(smsChannelId);
|
||||
// 校验记录的属性是否正确
|
||||
SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId);
|
||||
assertPojoEquals(reqVO, smsChannel, "id");
|
||||
// 断言 cache
|
||||
assertNull(smsChannelService.getIdClientCache().getIfPresent(smsChannel.getId()));
|
||||
assertNull(smsChannelService.getCodeClientCache().getIfPresent(smsChannel.getCode()));
|
||||
smsProperties.setChannel(SmsChannelEnum.DEBUG_DING_TALK.getCode());
|
||||
SmsClient dingDingClient = smsChannelService.getSmsClient();
|
||||
assertThat(dingDingClient).isInstanceOf(DebugDingTalkSmsClient.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSmsChannel_success() {
|
||||
// mock 数据
|
||||
SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class);
|
||||
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class, o -> {
|
||||
o.setId(dbSmsChannel.getId()); // 设置更新的 ID
|
||||
o.setStatus(randomCommonStatus());
|
||||
o.setCallbackUrl(randomString());
|
||||
});
|
||||
|
||||
// 调用
|
||||
smsChannelService.updateSmsChannel(reqVO);
|
||||
// 校验是否更新正确
|
||||
SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(reqVO, smsChannel);
|
||||
// 断言 cache
|
||||
assertNull(smsChannelService.getIdClientCache().getIfPresent(smsChannel.getId()));
|
||||
assertNull(smsChannelService.getCodeClientCache().getIfPresent(smsChannel.getCode()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSmsChannel_notExists() {
|
||||
// 准备参数
|
||||
SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> smsChannelService.updateSmsChannel(reqVO), SMS_CHANNEL_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteSmsChannel_success() {
|
||||
// mock 数据
|
||||
SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class);
|
||||
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbSmsChannel.getId();
|
||||
|
||||
// 调用
|
||||
smsChannelService.deleteSmsChannel(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(smsChannelMapper.selectById(id));
|
||||
// 断言 cache
|
||||
assertNull(smsChannelService.getIdClientCache().getIfPresent(dbSmsChannel.getId()));
|
||||
assertNull(smsChannelService.getCodeClientCache().getIfPresent(dbSmsChannel.getCode()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteSmsChannel_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteSmsChannel_hasChildren() {
|
||||
// mock 数据
|
||||
SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class);
|
||||
smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbSmsChannel.getId();
|
||||
// mock 方法
|
||||
when(smsTemplateService.getSmsTemplateCountByChannelId(eq(id))).thenReturn(10L);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_HAS_CHILDREN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsChannel() {
|
||||
// mock 数据
|
||||
SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class);
|
||||
smsChannelMapper.insert(dbSmsChannel); // @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbSmsChannel.getId();
|
||||
|
||||
// 调用,并断言
|
||||
assertPojoEquals(dbSmsChannel, smsChannelService.getSmsChannel(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsChannelList() {
|
||||
// mock 数据
|
||||
SmsChannelDO dbSmsChannel01 = randomPojo(SmsChannelDO.class);
|
||||
smsChannelMapper.insert(dbSmsChannel01);
|
||||
SmsChannelDO dbSmsChannel02 = randomPojo(SmsChannelDO.class);
|
||||
smsChannelMapper.insert(dbSmsChannel02);
|
||||
// 准备参数
|
||||
|
||||
// 调用
|
||||
List<SmsChannelDO> list = smsChannelService.getSmsChannelList();
|
||||
// 断言
|
||||
assertEquals(2, list.size());
|
||||
assertPojoEquals(dbSmsChannel01, list.get(0));
|
||||
assertPojoEquals(dbSmsChannel02, list.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsChannelPage() {
|
||||
// mock 数据
|
||||
SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到
|
||||
o.setSignature("晨丰科技");
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setCreateTime(buildTime(2020, 12, 12));
|
||||
});
|
||||
smsChannelMapper.insert(dbSmsChannel);
|
||||
// 测试 signature 不匹配
|
||||
smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码")));
|
||||
// 测试 status 不匹配
|
||||
smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
|
||||
// 测试 createTime 不匹配
|
||||
smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11))));
|
||||
// 准备参数
|
||||
SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO();
|
||||
reqVO.setSignature("晨丰");
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24));
|
||||
|
||||
// 调用
|
||||
PageResult<SmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbSmsChannel, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsClient_id() {
|
||||
// mock 数据
|
||||
SmsChannelDO channel = randomPojo(SmsChannelDO.class);
|
||||
smsChannelMapper.insert(channel);
|
||||
// mock 参数
|
||||
Long id = channel.getId();
|
||||
// mock 方法
|
||||
SmsClient mockClient = mock(SmsClient.class);
|
||||
when(smsClientFactory.getSmsClient(eq(SmsChannelEnum.ALIYUN.getCode()))).thenReturn(mockClient);
|
||||
|
||||
// 调用
|
||||
SmsClient client = smsChannelService.getSmsClient(id);
|
||||
// 断言
|
||||
assertSame(client, mockClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsClient_code() {
|
||||
// mock 数据
|
||||
SmsChannelDO channel = randomPojo(SmsChannelDO.class);
|
||||
smsChannelMapper.insert(channel);
|
||||
// mock 参数
|
||||
String code = channel.getCode();
|
||||
// mock 方法
|
||||
SmsClient mockClient = mock(SmsClient.class);
|
||||
when(smsClientFactory.getSmsClient(eq(code))).thenReturn(mockClient);
|
||||
|
||||
// 调用
|
||||
SmsClient client = smsChannelService.getSmsClient(code);
|
||||
// 断言
|
||||
assertSame(client, mockClient);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+291
-147
@@ -1,209 +1,353 @@
|
||||
package com.cf.imes.module.system.service.sms;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.cf.imes.framework.mybatis.core.enums.SqlConstants;
|
||||
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
|
||||
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import com.cf.imes.framework.security.test.WithMockLoginUser;
|
||||
import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
||||
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import com.cf.imes.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import com.cf.imes.module.system.dal.mysql.sms.SmsCodeMapper;
|
||||
import com.cf.imes.module.system.controller.admin.auth.vo.AuthLoginSmsCheckReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.cf.imes.module.system.enums.sms.SmsSceneEnum;
|
||||
import com.cf.imes.module.system.framework.sms.config.SmsCodeConfiguration;
|
||||
import com.cf.imes.module.system.framework.sms.config.SmsCodeProperties;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.cf.imes.module.system.service.user.AdminUserService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.data.redis.core.ZSetOperations;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.randomEle;
|
||||
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.AUTH_MOBILE_NOT_EXISTS;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.AUTH_MOBILE_NO_CHANGE;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.SMS_CODE_IS_VALID;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.SMS_CODE_NOT_CORRECT;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.SMS_CODE_NOT_FOUND;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.SMS_CODE_SEND_TOO_FAST;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.USER_MOBILE_EXISTS;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyDouble;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Import(SmsCodeServiceImpl.class)
|
||||
public class SmsCodeServiceImplTest extends BaseDbUnitTest {
|
||||
@Import({SmsCodeServiceImpl.class, SmsCodeConfiguration.class})
|
||||
public class SmsCodeServiceImplTest extends BaseDbAndRedisUnitTest {
|
||||
|
||||
@Resource
|
||||
private SmsCodeServiceImpl smsCodeService;
|
||||
|
||||
@Resource
|
||||
private SmsCodeMapper smsCodeMapper;
|
||||
@MockitoBean
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private ZSetOperations<String, Object> zSetOperations;
|
||||
|
||||
@MockitoBean
|
||||
private ValueOperations<String, Object> valueOperations;
|
||||
|
||||
@MockitoBean
|
||||
private AdminUserService userService;
|
||||
|
||||
@Resource
|
||||
private SmsCodeProperties smsCodeProperties;
|
||||
@MockBean
|
||||
|
||||
@MockitoBean
|
||||
private SmsSendService smsSendService;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(smsCodeProperties.getExpireTimes()).thenReturn(Duration.ofMinutes(5));
|
||||
when(smsCodeProperties.getSendFrequency()).thenReturn(Duration.ofMinutes(1));
|
||||
when(smsCodeProperties.getSendMaximumQuantityPerDay()).thenReturn(10);
|
||||
when(smsCodeProperties.getBeginCode()).thenReturn(9999);
|
||||
when(smsCodeProperties.getEndCode()).thenReturn(9999);
|
||||
void setup() {
|
||||
when(redisTemplate.opsForZSet()).thenReturn(zSetOperations);
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendSmsCode_success() {
|
||||
// 准备参数
|
||||
SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> {
|
||||
o.setMobile("15601691300");
|
||||
o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene());
|
||||
});
|
||||
// mock 方法
|
||||
SqlConstants.init(DbType.MYSQL);
|
||||
@WithMockLoginUser(userId = 1L)
|
||||
void testSendSmsCode_userUpdateMobile_noChange() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_UPDATE_MOBILE.getScene());
|
||||
|
||||
AdminUserDO user = new AdminUserDO();
|
||||
user.setId(1L);
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(user);
|
||||
|
||||
assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), AUTH_MOBILE_NO_CHANGE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendSmsCode_notLogin() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_UPDATE_MOBILE.getScene());
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(null);
|
||||
|
||||
assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), GlobalErrorCodeConstants.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockLoginUser
|
||||
void testSendSmsCode_userUpdateMobile_exists() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_UPDATE_MOBILE.getScene());
|
||||
|
||||
AdminUserDO user = new AdminUserDO();
|
||||
user.setId(2L);
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(user);
|
||||
|
||||
assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), USER_MOBILE_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockLoginUser
|
||||
void testSendSmsCode_nonUpdateMobile_userNotExist() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_RESET_PASSWORD.getScene());
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(null);
|
||||
|
||||
assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), AUTH_MOBILE_NOT_EXISTS);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockLoginUser
|
||||
void testSendSmsCode_userUpdateMobile_success() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_UPDATE_MOBILE.getScene());
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(null);
|
||||
|
||||
// 调用
|
||||
smsCodeService.sendSmsCode(reqDTO);
|
||||
// 断言 code 验证码
|
||||
SmsCodeDO smsCodeDO = smsCodeMapper.selectOne(null);
|
||||
assertPojoEquals(reqDTO, smsCodeDO);
|
||||
assertEquals("9999", smsCodeDO.getCode());
|
||||
assertEquals(1, smsCodeDO.getTodayIndex());
|
||||
assertFalse(smsCodeDO.getUsed());
|
||||
// 断言调用
|
||||
verify(smsSendService).sendSingleSms(eq(reqDTO.getMobile()), isNull(), isNull(),
|
||||
eq("user-sms-login"), eq(MapUtil.of("code", "9999")));
|
||||
|
||||
Mockito.verify(smsSendService).sendSingleSms(eq("12345678901"), isNull(), isNull(), anyString(), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendSmsCode_tooFast() {
|
||||
// mock 数据
|
||||
SmsCodeDO smsCodeDO = randomPojo(SmsCodeDO.class,
|
||||
o -> o.setMobile("15601691300").setTodayIndex(1));
|
||||
smsCodeMapper.insert(smsCodeDO);
|
||||
// 准备参数
|
||||
SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> {
|
||||
o.setMobile("15601691300");
|
||||
o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene());
|
||||
});
|
||||
// mock 方法
|
||||
SqlConstants.init(DbType.MYSQL);
|
||||
@WithMockLoginUser
|
||||
void testSendSmsCode_success() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_RESET_PASSWORD.getScene());
|
||||
reqDTO.setCreateIp("127.0.0.1");
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO),
|
||||
SMS_CODE_SEND_TOO_FAST);
|
||||
AdminUserDO user = new AdminUserDO();
|
||||
user.setId(1L);
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(user);
|
||||
|
||||
when(redisTemplate.hasKey(anyString())).thenReturn(false);
|
||||
when(zSetOperations.zCard(anyString())).thenReturn(0L);
|
||||
when(zSetOperations.add(anyString(), any(), anyDouble())).thenReturn(true);
|
||||
when(redisTemplate.expire(anyString(), anyLong(), any(TimeUnit.class))).thenReturn(true);
|
||||
|
||||
when(smsSendService.sendSingleSms(
|
||||
Mockito.eq("12345678901"),
|
||||
Mockito.isNull(),
|
||||
Mockito.isNull(),
|
||||
Mockito.anyString(),
|
||||
Mockito.anyMap()
|
||||
)).thenReturn(123L); // 返回一个模拟的日志ID
|
||||
|
||||
smsCodeService.sendSmsCode(reqDTO);
|
||||
|
||||
Mockito.verify(smsSendService).sendSingleSms(eq("12345678901"), isNull(), isNull(), anyString(), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendSmsCode_exceedDay() {
|
||||
// mock 数据
|
||||
SmsCodeDO smsCodeDO = randomPojo(SmsCodeDO.class,
|
||||
o -> o.setMobile("15601691300").setTodayIndex(10).setCreateTime(LocalDateTime.now()));
|
||||
smsCodeMapper.insert(smsCodeDO);
|
||||
// 准备参数
|
||||
SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> {
|
||||
o.setMobile("15601691300");
|
||||
o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene());
|
||||
});
|
||||
// mock 方法
|
||||
SqlConstants.init(DbType.MYSQL);
|
||||
when(smsCodeProperties.getSendFrequency()).thenReturn(Duration.ofMillis(0));
|
||||
@WithMockLoginUser
|
||||
void testSendSmsCode_moblibe_sendToFast() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_RESET_PASSWORD.getScene());
|
||||
reqDTO.setCreateIp("127.0.0.1");
|
||||
|
||||
AdminUserDO user = new AdminUserDO();
|
||||
user.setId(1L);
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(user);
|
||||
|
||||
// 模拟redis校验发送次数为6次
|
||||
when(zSetOperations.zCard(anyString())).thenReturn(6L);
|
||||
|
||||
assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), SMS_CODE_SEND_TOO_FAST);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO),
|
||||
SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUseSmsCode_success() {
|
||||
// 准备参数
|
||||
SmsCodeUseReqDTO reqDTO = randomPojo(SmsCodeUseReqDTO.class, o -> {
|
||||
o.setMobile("15601691300");
|
||||
o.setScene(randomEle(SmsSceneEnum.values()).getScene());
|
||||
});
|
||||
// mock 数据
|
||||
SqlConstants.init(DbType.MYSQL);
|
||||
smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> {
|
||||
o.setMobile(reqDTO.getMobile()).setScene(reqDTO.getScene())
|
||||
.setCode(reqDTO.getCode()).setUsed(false);
|
||||
}));
|
||||
@WithMockLoginUser
|
||||
void testSendSmsCode_ip_sendToFast() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_RESET_PASSWORD.getScene());
|
||||
reqDTO.setCreateIp("127.0.0.1");
|
||||
|
||||
AdminUserDO user = new AdminUserDO();
|
||||
user.setId(1L);
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(user);
|
||||
|
||||
// 模拟redis校验发送次数为6次
|
||||
when(zSetOperations.zCard(anyString())).thenReturn(6L);
|
||||
|
||||
SmsCodeServiceImpl spyService = Mockito.spy(smsCodeService);
|
||||
|
||||
// 首次手机号校验发送次数返回true
|
||||
Mockito.doReturn(true)
|
||||
.doReturn(false)
|
||||
.when(spyService)
|
||||
.slidingWindowRateLimit(Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt());
|
||||
|
||||
assertServiceException(() -> spyService.sendSmsCode(reqDTO), SMS_CODE_SEND_TOO_FAST);
|
||||
|
||||
// 调用
|
||||
smsCodeService.useSmsCode(reqDTO);
|
||||
// 断言
|
||||
SmsCodeDO smsCodeDO = smsCodeMapper.selectOne(null);
|
||||
assertTrue(smsCodeDO.getUsed());
|
||||
assertNotNull(smsCodeDO.getUsedTime());
|
||||
assertEquals(reqDTO.getUsedIp(), smsCodeDO.getUsedIp());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateSmsCode_success() {
|
||||
// 准备参数
|
||||
SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> {
|
||||
o.setMobile("15601691300");
|
||||
o.setScene(randomEle(SmsSceneEnum.values()).getScene());
|
||||
});
|
||||
// mock 数据
|
||||
SqlConstants.init(DbType.MYSQL);
|
||||
smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile())
|
||||
.setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(false)));
|
||||
void testSendSmsCode_codeExists() {
|
||||
SmsCodeSendReqDTO reqDTO = new SmsCodeSendReqDTO();
|
||||
reqDTO.setMobile("12345678901");
|
||||
reqDTO.setScene(SmsSceneEnum.USER_RESET_PASSWORD.getScene());
|
||||
|
||||
// 调用
|
||||
smsCodeService.validateSmsCode(reqDTO);
|
||||
AdminUserDO user = new AdminUserDO();
|
||||
user.setId(1L);
|
||||
|
||||
when(userService.getUserByUsernameAndOrganId(anyString(), any()))
|
||||
.thenReturn(user);
|
||||
|
||||
// 模拟验证码已存在
|
||||
when(redisTemplate.hasKey(anyString())).thenReturn(true);
|
||||
|
||||
assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), SMS_CODE_IS_VALID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateSmsCode_notFound() {
|
||||
// 准备参数
|
||||
SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> {
|
||||
o.setMobile("15601691300");
|
||||
o.setScene(randomEle(SmsSceneEnum.values()).getScene());
|
||||
});
|
||||
// mock 数据
|
||||
SqlConstants.init(DbType.MYSQL);
|
||||
void testUseSmsCode_userNotExists() {
|
||||
SmsCodeUseReqDTO req = SmsCodeUseReqDTO.builder()
|
||||
.mobile("13800000000")
|
||||
.build();
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO),
|
||||
SMS_CODE_NOT_FOUND);
|
||||
when(userService.getUserUniqueByUserName(req.getMobile())).thenReturn(null);
|
||||
|
||||
assertServiceException(() -> smsCodeService.useSmsCode(req), AUTH_MOBILE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateSmsCode_expired() {
|
||||
// 准备参数
|
||||
SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> {
|
||||
o.setMobile("15601691300");
|
||||
o.setScene(randomEle(SmsSceneEnum.values()).getScene());
|
||||
});
|
||||
// mock 数据
|
||||
SqlConstants.init(DbType.MYSQL);
|
||||
smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile())
|
||||
.setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(false)
|
||||
.setCreateTime(LocalDateTime.now().minusMinutes(6))));
|
||||
void testUseSmsCode_codeNotFound() {
|
||||
SmsCodeUseReqDTO req = SmsCodeUseReqDTO.builder()
|
||||
.mobile("13800000000")
|
||||
.scene(SmsSceneEnum.USER_RESET_PASSWORD.getScene())
|
||||
.code("123456")
|
||||
.build();
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO),
|
||||
SMS_CODE_EXPIRED);
|
||||
when(userService.getUserUniqueByUserName(req.getMobile())).thenReturn(new AdminUserDO());
|
||||
when(valueOperations.get(Mockito.anyString())).thenReturn(null);
|
||||
|
||||
assertServiceException(() -> smsCodeService.useSmsCode(req), SMS_CODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateSmsCode_used() {
|
||||
// 准备参数
|
||||
SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> {
|
||||
o.setMobile("15601691300");
|
||||
o.setScene(randomEle(SmsSceneEnum.values()).getScene());
|
||||
});
|
||||
// mock 数据
|
||||
SqlConstants.init(DbType.MYSQL);
|
||||
smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile())
|
||||
.setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(true)
|
||||
.setCreateTime(LocalDateTime.now())));
|
||||
void testUseSmsCode_codeIncorrect_shouldThrow() {
|
||||
SmsCodeUseReqDTO req = SmsCodeUseReqDTO.builder()
|
||||
.mobile("13800000000")
|
||||
.scene(SmsSceneEnum.USER_RESET_PASSWORD.getScene())
|
||||
.code("654321")
|
||||
.build();
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO),
|
||||
SMS_CODE_USED);
|
||||
when(userService.getUserUniqueByUserName(req.getMobile())).thenReturn(new AdminUserDO());
|
||||
when(valueOperations.get(Mockito.anyString())).thenReturn("123456");
|
||||
|
||||
assertServiceException(() -> smsCodeService.useSmsCode(req), SMS_CODE_NOT_CORRECT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUseSmsCode_success() {
|
||||
SmsCodeUseReqDTO req = SmsCodeUseReqDTO.builder()
|
||||
.mobile("13800000000")
|
||||
.scene(SmsSceneEnum.USER_UPDATE_MOBILE.getScene())
|
||||
.code("123456")
|
||||
.build();
|
||||
|
||||
when(userService.getUserUniqueByUserName(req.getMobile())).thenReturn(null);
|
||||
when(valueOperations.get(Mockito.anyString())).thenReturn("123456");
|
||||
|
||||
smsCodeService.useSmsCode(req);
|
||||
|
||||
Mockito.verify(redisTemplate).delete(Mockito.anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateSmsCode_userNotExists_shouldThrowAuthMobileNotExists() {
|
||||
AuthLoginSmsCheckReqVO reqVO = new AuthLoginSmsCheckReqVO();
|
||||
reqVO.setMobile("13800000000");
|
||||
|
||||
when(userService.getUserUniqueByUserName("13800000000")).thenReturn(null);
|
||||
|
||||
assertServiceException(() -> smsCodeService.validateSmsCode(reqVO), AUTH_MOBILE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateSmsCode_smsCodeNotFound_shouldThrowSmsCodeNotFound() {
|
||||
AuthLoginSmsCheckReqVO reqVO = new AuthLoginSmsCheckReqVO();
|
||||
reqVO.setMobile("13800000000");
|
||||
|
||||
when(userService.getUserUniqueByUserName("13800000000")).thenReturn(new AdminUserDO());
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
when(valueOperations.get(anyString())).thenReturn(null);
|
||||
|
||||
assertServiceException(() -> smsCodeService.validateSmsCode(reqVO), SMS_CODE_NOT_FOUND);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateSmsCode_smsCodeNotCorrect_shouldThrowSmsCodeNotCorrect() {
|
||||
AuthLoginSmsCheckReqVO reqVO = new AuthLoginSmsCheckReqVO();
|
||||
reqVO.setMobile("13800000000");
|
||||
reqVO.setSmsCaptchaVerification("123456");
|
||||
|
||||
when(userService.getUserUniqueByUserName("13800000000")).thenReturn(new AdminUserDO());
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
when(valueOperations.get(anyString())).thenReturn("999999");
|
||||
|
||||
assertServiceException(() -> smsCodeService.validateSmsCode(reqVO), SMS_CODE_NOT_CORRECT);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateSmsCode_smsCodeCorrect_shouldPass() {
|
||||
AuthLoginSmsCheckReqVO reqVO = new AuthLoginSmsCheckReqVO();
|
||||
reqVO.setMobile("13800000000");
|
||||
reqVO.setSmsCaptchaVerification("123456");
|
||||
|
||||
when(userService.getUserUniqueByUserName("13800000000")).thenReturn(new AdminUserDO());
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
when(valueOperations.get(anyString())).thenReturn("123456");
|
||||
|
||||
assertDoesNotThrow(() -> smsCodeService.validateSmsCode(reqVO));
|
||||
}
|
||||
}
|
||||
|
||||
+25
-18
@@ -4,7 +4,7 @@ import cn.hutool.core.map.MapUtil;
|
||||
import com.cf.imes.framework.common.enums.UserTypeEnum;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.framework.common.util.collection.ArrayUtils;
|
||||
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
|
||||
import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsLogDO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
@@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
@@ -31,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
@Import(SmsLogServiceImpl.class)
|
||||
public class SmsLogServiceImplTest extends BaseDbUnitTest {
|
||||
public class SmsLogServiceImplTest extends BaseDbAndRedisUnitTest {
|
||||
|
||||
@Resource
|
||||
private SmsLogServiceImpl smsLogService;
|
||||
@@ -40,7 +41,7 @@ public class SmsLogServiceImplTest extends BaseDbUnitTest {
|
||||
private SmsLogMapper smsLogMapper;
|
||||
|
||||
@Test
|
||||
public void testGetSmsLogPage() {
|
||||
void testGetSmsLogPage() {
|
||||
// mock 数据
|
||||
SmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到
|
||||
o.setChannelId(1L);
|
||||
@@ -85,7 +86,7 @@ public class SmsLogServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSmsLog() {
|
||||
void testCreateSmsLog() {
|
||||
// 准备参数
|
||||
String mobile = randomString();
|
||||
Long userId = randomLongId();
|
||||
@@ -102,30 +103,26 @@ public class SmsLogServiceImplTest extends BaseDbUnitTest {
|
||||
templateDO, templateContent, templateParams);
|
||||
// 断言
|
||||
SmsLogDO logDO = smsLogMapper.selectById(logId);
|
||||
assertEquals(isSend ? SmsSendStatusEnum.INIT.getStatus() : SmsSendStatusEnum.IGNORE.getStatus(),
|
||||
logDO.getSendStatus());
|
||||
assertEquals(SmsSendStatusEnum.INIT.getStatus(), logDO.getSendStatus());
|
||||
assertEquals(mobile, logDO.getMobile());
|
||||
assertEquals(userType, logDO.getUserType());
|
||||
assertEquals(userId, logDO.getUserId());
|
||||
assertEquals(templateDO.getId(), logDO.getTemplateId());
|
||||
assertEquals(templateDO.getCode(), logDO.getTemplateCode());
|
||||
assertEquals(templateDO.getType(), logDO.getTemplateType());
|
||||
assertEquals(templateDO.getChannelId(), logDO.getChannelId());
|
||||
assertEquals(templateDO.getChannelCode(), logDO.getChannelCode());
|
||||
assertEquals(templateContent, logDO.getTemplateContent());
|
||||
assertEquals(templateParams, logDO.getTemplateParams());
|
||||
assertEquals(SmsReceiveStatusEnum.INIT.getStatus(), logDO.getReceiveStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSmsSendResult() {
|
||||
void testUpdateSmsSendResult() {
|
||||
// mock 数据
|
||||
SmsLogDO dbSmsLog = randomSmsLogDO(
|
||||
o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus()));
|
||||
smsLogMapper.insert(dbSmsLog);
|
||||
// 准备参数
|
||||
Long id = dbSmsLog.getId();
|
||||
Boolean success = randomBoolean();
|
||||
String apiSendCode = randomString();
|
||||
String apiSendMsg = randomString();
|
||||
String apiRequestId = randomString();
|
||||
@@ -133,41 +130,51 @@ public class SmsLogServiceImplTest extends BaseDbUnitTest {
|
||||
String channelCode = randomString();
|
||||
|
||||
// 调用
|
||||
smsLogService.updateSmsSendResult(id, success,
|
||||
smsLogService.updateSmsSendResult(id, true,
|
||||
apiSendCode, apiSendMsg, apiRequestId, apiSerialNo, channelCode);
|
||||
// 断言
|
||||
dbSmsLog = smsLogMapper.selectById(id);
|
||||
assertEquals(success ? SmsSendStatusEnum.SUCCESS.getStatus() : SmsSendStatusEnum.FAILURE.getStatus(),
|
||||
dbSmsLog.getSendStatus());
|
||||
assertEquals(SmsSendStatusEnum.SUCCESS.getStatus(), dbSmsLog.getSendStatus());
|
||||
assertNotNull(dbSmsLog.getSendTime());
|
||||
assertEquals(apiSendCode, dbSmsLog.getApiSendCode());
|
||||
assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg());
|
||||
assertEquals(apiRequestId, dbSmsLog.getApiRequestId());
|
||||
assertEquals(apiSerialNo, dbSmsLog.getApiSerialNo());
|
||||
|
||||
// 调用
|
||||
smsLogService.updateSmsSendResult(id, false,
|
||||
apiSendCode, apiSendMsg, apiRequestId, apiSerialNo, channelCode);
|
||||
// 断言
|
||||
dbSmsLog = smsLogMapper.selectById(id);
|
||||
assertEquals(SmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSmsReceiveResult() {
|
||||
void testUpdateSmsReceiveResult() {
|
||||
// mock 数据
|
||||
SmsLogDO dbSmsLog = randomSmsLogDO(
|
||||
o -> o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus()));
|
||||
smsLogMapper.insert(dbSmsLog);
|
||||
// 准备参数
|
||||
Long id = dbSmsLog.getId();
|
||||
Boolean success = randomBoolean();
|
||||
LocalDateTime receiveTime = randomLocalDateTime();
|
||||
String apiReceiveCode = randomString();
|
||||
String apiReceiveMsg = randomString();
|
||||
|
||||
// 调用
|
||||
smsLogService.updateSmsReceiveResult(id, success, receiveTime, apiReceiveCode, apiReceiveMsg);
|
||||
smsLogService.updateSmsReceiveResult(id, true, receiveTime, apiReceiveCode, apiReceiveMsg);
|
||||
// 断言
|
||||
dbSmsLog = smsLogMapper.selectById(id);
|
||||
assertEquals(success ? SmsReceiveStatusEnum.SUCCESS.getStatus()
|
||||
: SmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus());
|
||||
assertEquals(SmsReceiveStatusEnum.SUCCESS.getStatus(), dbSmsLog.getReceiveStatus());
|
||||
assertEquals(receiveTime, dbSmsLog.getReceiveTime());
|
||||
assertEquals(apiReceiveCode, dbSmsLog.getApiReceiveCode());
|
||||
assertEquals(apiReceiveMsg, dbSmsLog.getApiReceiveMsg());
|
||||
|
||||
// 调用
|
||||
smsLogService.updateSmsReceiveResult(id, false, receiveTime, apiReceiveCode, apiReceiveMsg);
|
||||
// 断言
|
||||
dbSmsLog = smsLogMapper.selectById(id);
|
||||
assertEquals(SmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus());
|
||||
}
|
||||
|
||||
// ========== 随机对象 ==========
|
||||
|
||||
+97
-111
@@ -4,23 +4,26 @@ import cn.hutool.core.map.MapUtil;
|
||||
import com.cf.imes.framework.common.core.KeyValue;
|
||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||
import com.cf.imes.framework.common.enums.UserTypeEnum;
|
||||
import com.cf.imes.framework.test.core.ut.BaseRedisUnitTest;
|
||||
import com.cf.imes.module.system.framework.sms.config.SmsCodeConfiguration;
|
||||
import com.cf.imes.module.system.framework.sms.config.SmsCodeProperties;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.SmsClient;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.cf.imes.module.system.mq.message.sms.SmsSendMessage;
|
||||
import com.cf.imes.module.system.mq.producer.sms.SmsProducer;
|
||||
import com.cf.imes.module.system.service.member.MemberService;
|
||||
import com.cf.imes.module.system.service.user.AdminUserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.assertj.core.util.Lists;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -29,110 +32,46 @@ import static cn.hutool.core.util.RandomUtil.randomEle;
|
||||
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static com.cf.imes.framework.test.core.util.RandomUtils.*;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
@Import({SmsSendServiceImpl.class, SmsCodeConfiguration.class})
|
||||
public class SmsSendServiceImplTest extends BaseRedisUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
@Resource
|
||||
private SmsSendServiceImpl smsSendService;
|
||||
|
||||
@Mock
|
||||
private AdminUserService adminUserService;
|
||||
@Mock
|
||||
private MemberService memberService;
|
||||
@Mock
|
||||
@MockitoBean
|
||||
private SmsChannelService smsChannelService;
|
||||
@Mock
|
||||
@MockitoBean
|
||||
private SmsTemplateService smsTemplateService;
|
||||
@Mock
|
||||
@MockitoBean
|
||||
private SmsLogService smsLogService;
|
||||
@Mock
|
||||
@MockitoBean
|
||||
private SmsProducer smsProducer;
|
||||
|
||||
@Test
|
||||
public void testSendSingleSmsToAdmin() {
|
||||
// 准备参数
|
||||
Long userId = randomLongId();
|
||||
String templateCode = randomString();
|
||||
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
|
||||
.put("op", "login").build();
|
||||
// mock adminUserService 的方法
|
||||
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300"));
|
||||
when(adminUserService.getUser(eq(userId))).thenReturn(user);
|
||||
@Resource
|
||||
private SmsCodeProperties smsCodeProperties;
|
||||
|
||||
// mock SmsTemplateService 的方法
|
||||
SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> {
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setContent("验证码为{code}, 操作为{op}");
|
||||
o.setParams(Lists.newArrayList("code", "op"));
|
||||
});
|
||||
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
|
||||
String content = randomString();
|
||||
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
|
||||
.thenReturn(content);
|
||||
// mock SmsChannelService 的方法
|
||||
SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel);
|
||||
// mock SmsLogService 的方法
|
||||
Long smsLogId = randomLongId();
|
||||
when(smsLogService.createSmsLog(eq(user.getMobile()), eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(Boolean.TRUE), eq(template),
|
||||
eq(content), eq(templateParams))).thenReturn(smsLogId);
|
||||
@MockitoBean
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
// 调用
|
||||
Long resultSmsLogId = smsSendService.sendSingleSmsToAdmin(null, userId, templateCode, templateParams);
|
||||
// 断言
|
||||
assertEquals(smsLogId, resultSmsLogId);
|
||||
// 断言调用
|
||||
verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(user.getMobile()), eq(template),
|
||||
eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login"))));
|
||||
}
|
||||
@MockitoBean
|
||||
private ValueOperations<String, Object> valueOperations;
|
||||
|
||||
@Test
|
||||
public void testSendSingleSmsToUser() {
|
||||
// 准备参数
|
||||
Long userId = randomLongId();
|
||||
String templateCode = randomString();
|
||||
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
|
||||
.put("op", "login").build();
|
||||
// mock memberService 的方法
|
||||
String mobile = "15601691300";
|
||||
when(memberService.getMemberUserMobile(eq(userId))).thenReturn(mobile);
|
||||
|
||||
// mock SmsTemplateService 的方法
|
||||
SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> {
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setContent("验证码为{code}, 操作为{op}");
|
||||
o.setParams(Lists.newArrayList("code", "op"));
|
||||
});
|
||||
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
|
||||
String content = randomString();
|
||||
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
|
||||
.thenReturn(content);
|
||||
// mock SmsChannelService 的方法
|
||||
SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel);
|
||||
// mock SmsLogService 的方法
|
||||
Long smsLogId = randomLongId();
|
||||
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(Boolean.TRUE), eq(template),
|
||||
eq(content), eq(templateParams))).thenReturn(smsLogId);
|
||||
|
||||
// 调用
|
||||
Long resultSmsLogId = smsSendService.sendSingleSmsToMember(null, userId, templateCode, templateParams);
|
||||
// 断言
|
||||
assertEquals(smsLogId, resultSmsLogId);
|
||||
// 断言调用
|
||||
verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), eq(template),
|
||||
eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login"))));
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功,当短信模板开启时
|
||||
*/
|
||||
@Test
|
||||
public void testSendSingleSms_successWhenSmsTemplateEnable() {
|
||||
void testSendSingleSms_successWhenSmsTemplateEnable() {
|
||||
// 准备参数
|
||||
String mobile = randomString();
|
||||
Long userId = randomLongId();
|
||||
@@ -150,9 +89,6 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
String content = randomString();
|
||||
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
|
||||
.thenReturn(content);
|
||||
// mock SmsChannelService 的方法
|
||||
SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel);
|
||||
// mock SmsLogService 的方法
|
||||
Long smsLogId = randomLongId();
|
||||
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.TRUE), eq(template),
|
||||
@@ -171,7 +107,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
* 发送成功,当短信模板关闭时
|
||||
*/
|
||||
@Test
|
||||
public void testSendSingleSms_successWhenSmsTemplateDisable() {
|
||||
void testSendSingleSms_successWhenSmsTemplateDisable() {
|
||||
// 准备参数
|
||||
String mobile = randomString();
|
||||
Long userId = randomLongId();
|
||||
@@ -189,9 +125,6 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
String content = randomString();
|
||||
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
|
||||
.thenReturn(content);
|
||||
// mock SmsChannelService 的方法
|
||||
SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel);
|
||||
// mock SmsLogService 的方法
|
||||
Long smsLogId = randomLongId();
|
||||
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.FALSE), eq(template),
|
||||
@@ -206,7 +139,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckSmsTemplateValid_notExists() {
|
||||
void testCheckSmsTemplateValid_notExists() {
|
||||
// 准备参数
|
||||
String templateCode = randomString();
|
||||
// mock 方法
|
||||
@@ -217,7 +150,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildTemplateParams_paramMiss() {
|
||||
void testBuildTemplateParams_paramMiss() {
|
||||
// 准备参数
|
||||
SmsTemplateDO template = randomPojo(SmsTemplateDO.class,
|
||||
o -> o.setParams(Lists.newArrayList("code")));
|
||||
@@ -230,7 +163,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckMobile_notExists() {
|
||||
void testCheckMobile_notExists() {
|
||||
// 准备参数
|
||||
// mock 方法
|
||||
|
||||
@@ -240,7 +173,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendBatchNotify() {
|
||||
void testSendBatchNotify() {
|
||||
// 准备参数
|
||||
// mock 方法
|
||||
|
||||
@@ -255,38 +188,91 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDoSendSms() throws Throwable {
|
||||
void testDoSendSms() throws Throwable {
|
||||
// 准备参数
|
||||
SmsSendMessage message = randomPojo(SmsSendMessage.class);
|
||||
message.setMobile("15601691300");
|
||||
message.setTemplateParams(List.of(new KeyValue<>("code", "123456")));
|
||||
// mock SmsClientFactory 的方法
|
||||
SmsClient smsClient = spy(SmsClient.class);
|
||||
when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
|
||||
SmsClient smsClient = mock(SmsClient.class);
|
||||
when(smsChannelService.getSmsClient()).thenReturn(smsClient);
|
||||
SmsSendRespDTO sendResp = randomPojo(SmsSendRespDTO.class, dto -> dto.setSuccess(true));
|
||||
|
||||
// mock SmsClient 的方法
|
||||
SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class);
|
||||
when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()),
|
||||
eq(message.getTemplateParams()))).thenReturn(sendResult);
|
||||
eq(message.getTemplateParams()))).thenReturn(sendResp);
|
||||
|
||||
// 调用
|
||||
smsSendService.doSendSms(message);
|
||||
|
||||
// 断言
|
||||
verify(smsLogService).updateSmsSendResult(eq(message.getLogId()),
|
||||
eq(sendResult.getSuccess()), eq(sendResult.getApiCode()),
|
||||
eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo()), eq(sendResult.getChannelCode()));
|
||||
verify(valueOperations).set(
|
||||
contains("sms_captcha_verification"),
|
||||
eq("123456"),
|
||||
eq(Duration.ofMinutes(5))
|
||||
);
|
||||
verify(smsLogService).updateSmsSendResult(
|
||||
eq(message.getLogId()), eq(true),
|
||||
eq(sendResp.getApiCode()), eq(sendResp.getApiMsg()),
|
||||
eq(sendResp.getApiRequestId()), eq(sendResp.getSerialNo()), eq(sendResp.getChannelCode())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveSmsStatus() throws Throwable {
|
||||
void testDoSendSms_fail() throws Throwable {
|
||||
SmsSendMessage message = randomPojo(SmsSendMessage.class);
|
||||
message.setTemplateParams(List.of(new KeyValue<>("code", "999999")));
|
||||
// mock SmsClientFactory 的方法
|
||||
SmsClient smsClient = spy(SmsClient.class);
|
||||
when(smsChannelService.getSmsClient()).thenReturn(smsClient);
|
||||
SmsSendRespDTO sendResp = randomPojo(SmsSendRespDTO.class, dto -> dto.setSuccess(false));
|
||||
when(smsClient.sendSms(anyLong(), anyString(), anyString(), anyList())).thenReturn(sendResp);
|
||||
|
||||
smsSendService.doSendSms(message);
|
||||
|
||||
// 不应写 redis
|
||||
verify(redisTemplate, never()).opsForValue();
|
||||
verify(smsLogService).updateSmsSendResult(
|
||||
eq(message.getLogId()), eq(false),
|
||||
eq(sendResp.getApiCode()), eq(sendResp.getApiMsg()),
|
||||
eq(sendResp.getApiRequestId()), eq(sendResp.getSerialNo()), eq(sendResp.getChannelCode())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoSendSms_throwExceptionDuringSend() throws Throwable {
|
||||
SmsSendMessage message = randomPojo(SmsSendMessage.class);
|
||||
// mock SmsClientFactory 的方法
|
||||
SmsClient smsClient = spy(SmsClient.class);
|
||||
when(smsChannelService.getSmsClient()).thenReturn(smsClient);
|
||||
when(smsClient.sendSms(anyLong(), anyString(), anyString(), anyList()))
|
||||
.thenThrow(new RuntimeException("发送异常"));
|
||||
|
||||
smsSendService.doSendSms(message);
|
||||
|
||||
verify(smsLogService).updateSmsSendResult(
|
||||
eq(message.getLogId()), eq(false),
|
||||
eq("EXCEPTION"), contains("发送异常"),
|
||||
isNull(), isNull(), isNull()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
void testReceiveSmsStatus() throws Throwable {
|
||||
// 准备参数
|
||||
String channelCode = randomString();
|
||||
String text = randomString();
|
||||
// mock SmsClientFactory 的方法
|
||||
SmsClient smsClient = spy(SmsClient.class);
|
||||
when(smsChannelService.getSmsClient(eq(channelCode))).thenReturn(smsClient);
|
||||
when(smsChannelService.getSmsClient()).thenReturn(smsClient);
|
||||
// mock SmsClient 的方法
|
||||
List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
|
||||
when(smsClient.parseSmsReceiveStatus(text)).thenReturn(receiveResults);
|
||||
|
||||
// 调用
|
||||
assertThrows(Throwable.class, () -> smsSendService.receiveSmsStatus(channelCode, text));
|
||||
assertDoesNotThrow(() -> smsSendService.receiveSmsStatus(channelCode, text));
|
||||
// 断言
|
||||
receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()),
|
||||
eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode())));
|
||||
|
||||
+72
-68
@@ -2,25 +2,28 @@ package com.cf.imes.module.system.service.sms;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.cf.imes.framework.common.enums.CommonStatusEnum;
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
import com.cf.imes.framework.common.util.collection.ArrayUtils;
|
||||
import com.cf.imes.framework.common.util.object.ObjectUtils;
|
||||
import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.template.SmsTemplateRespVO;
|
||||
import com.cf.imes.module.system.enums.ErrorCodeConstants;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.SmsClient;
|
||||
import com.cf.imes.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
|
||||
import com.cf.imes.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
|
||||
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
|
||||
import com.cf.imes.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.cf.imes.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.cf.imes.module.system.dal.mysql.sms.SmsTemplateMapper;
|
||||
import com.cf.imes.module.system.enums.sms.SmsTemplateTypeEnum;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
@@ -34,10 +37,12 @@ import static com.cf.imes.framework.test.core.util.RandomUtils.*;
|
||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Import(SmsTemplateServiceImpl.class)
|
||||
public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
public class SmsTemplateServiceImplTest extends BaseDbAndRedisUnitTest {
|
||||
|
||||
@Resource
|
||||
private SmsTemplateServiceImpl smsTemplateService;
|
||||
@@ -45,13 +50,14 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
@Resource
|
||||
private SmsTemplateMapper smsTemplateMapper;
|
||||
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private SmsChannelService smsChannelService;
|
||||
@MockBean
|
||||
|
||||
@MockitoBean
|
||||
private SmsClient smsClient;
|
||||
|
||||
@Test
|
||||
public void testFormatSmsTemplateContent() {
|
||||
void testFormatSmsTemplateContent() {
|
||||
// 准备参数
|
||||
String content = "正在进行登录操作{operation},您的验证码是{code}";
|
||||
Map<String, Object> params = MapUtil.<String, Object>builder("operation", "登录")
|
||||
@@ -64,7 +70,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTemplateContentParams() {
|
||||
void testParseTemplateContentParams() {
|
||||
// 准备参数
|
||||
String content = "正在进行登录操作{operation},您的验证码是{code}";
|
||||
// mock 方法
|
||||
@@ -77,7 +83,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testCreateSmsTemplate_success() throws Throwable {
|
||||
void testCreateSmsTemplate_success() throws Throwable {
|
||||
// 准备参数
|
||||
SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class, o -> {
|
||||
o.setContent("正在进行登录操作{operation},您的验证码是{code}");
|
||||
@@ -101,7 +107,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testUpdateSmsTemplate_success() throws Throwable {
|
||||
void testUpdateSmsTemplate_success() throws Throwable {
|
||||
// mock 数据
|
||||
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
|
||||
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
|
||||
@@ -126,7 +132,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSmsTemplate_notExists() {
|
||||
void testUpdateSmsTemplate_notExists() {
|
||||
// 准备参数
|
||||
SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class);
|
||||
|
||||
@@ -135,7 +141,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteSmsTemplate_success() {
|
||||
void testDeleteSmsTemplate_success() {
|
||||
// mock 数据
|
||||
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
|
||||
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
|
||||
@@ -149,7 +155,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteSmsTemplate_notExists() {
|
||||
void testDeleteSmsTemplate_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
@@ -158,7 +164,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsTemplate() {
|
||||
void testGetSmsTemplate() {
|
||||
// mock 数据
|
||||
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
|
||||
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
|
||||
@@ -172,7 +178,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsTemplateByCodeFromCache() {
|
||||
void testGetSmsTemplateByCodeFromCache() {
|
||||
// mock 数据
|
||||
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
|
||||
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
|
||||
@@ -186,7 +192,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsTemplatePage() {
|
||||
void testGetSmsTemplatePage() {
|
||||
// mock 数据
|
||||
SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到
|
||||
o.setType(SmsTemplateTypeEnum.PROMOTION.getType());
|
||||
@@ -194,7 +200,6 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
o.setCode("tudou");
|
||||
o.setContent("晨丰科技");
|
||||
o.setApiTemplateId("yunai");
|
||||
o.setChannelId(1L);
|
||||
o.setCreateTime(buildTime(2021, 11, 11));
|
||||
});
|
||||
smsTemplateMapper.insert(dbSmsTemplate);
|
||||
@@ -204,12 +209,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
|
||||
// 测试 code 不匹配
|
||||
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma")));
|
||||
// 测试 content 不匹配
|
||||
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码")));
|
||||
// 测试 apiTemplateId 不匹配
|
||||
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai")));
|
||||
// 测试 channelId 不匹配
|
||||
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L)));
|
||||
// 测试 createTime 不匹配
|
||||
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12))));
|
||||
// 准备参数
|
||||
@@ -229,7 +230,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsTemplateCountByChannelId() {
|
||||
void testGetSmsTemplateCountByChannelId() {
|
||||
// mock 数据
|
||||
SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> o.setChannelId(1L));
|
||||
smsTemplateMapper.insert(dbSmsTemplate);
|
||||
@@ -245,56 +246,13 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateSmsChannel_success() {
|
||||
// 准备参数
|
||||
Long channelId = randomLongId();
|
||||
// mock 方法
|
||||
SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> {
|
||||
o.setId(channelId);
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态
|
||||
});
|
||||
when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO);
|
||||
|
||||
// 调用
|
||||
SmsChannelDO returnChannelDO = smsTemplateService.validateSmsChannel(channelId);
|
||||
// 断言
|
||||
assertPojoEquals(returnChannelDO, channelDO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateSmsChannel_notExists() {
|
||||
// 准备参数
|
||||
Long channelId = randomLongId();
|
||||
|
||||
// 调用,校验异常
|
||||
assertServiceException(() -> smsTemplateService.validateSmsChannel(channelId),
|
||||
SMS_CHANNEL_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateSmsChannel_disable() {
|
||||
// 准备参数
|
||||
Long channelId = randomLongId();
|
||||
// mock 方法
|
||||
SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> {
|
||||
o.setId(channelId);
|
||||
o.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 保证 status 禁用,触发失败
|
||||
});
|
||||
when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO);
|
||||
|
||||
// 调用,校验异常
|
||||
assertServiceException(() -> smsTemplateService.validateSmsChannel(channelId),
|
||||
SMS_CHANNEL_DISABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDictDataValueUnique_success() {
|
||||
void testValidateDictDataValueUnique_success() {
|
||||
// 调用,成功
|
||||
smsTemplateService.validateSmsTemplateCodeDuplicate(randomLongId(), randomString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateSmsTemplateCodeDuplicate_valueDuplicateForCreate() {
|
||||
void testValidateSmsTemplateCodeDuplicate_valueDuplicateForCreate() {
|
||||
// 准备参数
|
||||
String code = randomString();
|
||||
// mock 数据
|
||||
@@ -306,7 +264,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDictDataValueUnique_valueDuplicateForUpdate() {
|
||||
void testValidateDictDataValueUnique_valueDuplicateForUpdate() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
String code = randomString();
|
||||
@@ -329,4 +287,50 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
|
||||
return randomPojo(SmsTemplateDO.class, ArrayUtils.append(consumer, consumers));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateApiTemplate_allBranches() throws Throwable {
|
||||
String apiTemplateId = "tpl-123";
|
||||
|
||||
// 1. smsClient 为 null
|
||||
doReturn(null).when(smsChannelService).getSmsClient();
|
||||
ServiceException ex1 = assertThrows(ServiceException.class,
|
||||
() -> smsTemplateService.validateApiTemplate(apiTemplateId));
|
||||
assertEquals(ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS.getCode(), ex1.getCode());
|
||||
|
||||
// 2. smsClient 不为 null,但 getSmsTemplate 抛异常
|
||||
doReturn(smsClient).when(smsChannelService).getSmsClient();
|
||||
doThrow(new RuntimeException("api error")).when(smsClient).getSmsTemplate(apiTemplateId);
|
||||
ServiceException ex2 = assertThrows(ServiceException.class,
|
||||
() -> smsTemplateService.validateApiTemplate(apiTemplateId));
|
||||
assertEquals(ErrorCodeConstants.SMS_TEMPLATE_API_ERROR.getCode(), ex2.getCode());
|
||||
|
||||
// 3. 返回 null 模板
|
||||
doReturn(null).when(smsClient).getSmsTemplate(apiTemplateId);
|
||||
ServiceException ex3 = assertThrows(ServiceException.class,
|
||||
() -> smsTemplateService.validateApiTemplate(apiTemplateId));
|
||||
assertEquals(ErrorCodeConstants.SMS_TEMPLATE_API_NOT_FOUND.getCode(), ex3.getCode());
|
||||
|
||||
// 4. 审核中
|
||||
SmsTemplateRespDTO checkingTpl = new SmsTemplateRespDTO();
|
||||
checkingTpl.setAuditStatus(SmsTemplateAuditStatusEnum.CHECKING.getStatus());
|
||||
doReturn(checkingTpl).when(smsClient).getSmsTemplate(apiTemplateId);
|
||||
ServiceException ex4 = assertThrows(ServiceException.class,
|
||||
() -> smsTemplateService.validateApiTemplate(apiTemplateId));
|
||||
assertEquals(ErrorCodeConstants.SMS_TEMPLATE_API_AUDIT_CHECKING.getCode(), ex4.getCode());
|
||||
|
||||
// 5. 审核失败
|
||||
SmsTemplateRespDTO failTpl = new SmsTemplateRespDTO();
|
||||
failTpl.setAuditStatus(SmsTemplateAuditStatusEnum.FAIL.getStatus());
|
||||
failTpl.setAuditReason("reason");
|
||||
doReturn(failTpl).when(smsClient).getSmsTemplate(apiTemplateId);
|
||||
ServiceException ex5 = assertThrows(ServiceException.class,
|
||||
() -> smsTemplateService.validateApiTemplate(apiTemplateId));
|
||||
assertEquals(ErrorCodeConstants.SMS_TEMPLATE_API_AUDIT_FAIL.getCode(), ex5.getCode());
|
||||
|
||||
// 6. 审核成功,Assert.equals 分支
|
||||
SmsTemplateRespDTO successTpl = new SmsTemplateRespDTO();
|
||||
successTpl.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus());
|
||||
doReturn(successTpl).when(smsClient).getSmsTemplate(apiTemplateId);
|
||||
assertDoesNotThrow(() -> smsTemplateService.validateApiTemplate(apiTemplateId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,8 +278,8 @@ CREATE TABLE IF NOT EXISTS "system_sms_template" (
|
||||
"params" varchar(255) NOT NULL,
|
||||
"remark" varchar(255) DEFAULT NULL,
|
||||
"api_template_id" varchar(63) NOT NULL,
|
||||
"channel_id" bigint NOT NULL,
|
||||
"channel_code" varchar(63) NOT NULL,
|
||||
"channel_id" bigint NULL,
|
||||
"channel_code" varchar(63) NULL,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
@@ -290,8 +290,8 @@ CREATE TABLE IF NOT EXISTS "system_sms_template" (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "system_sms_log" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"channel_id" bigint NOT NULL,
|
||||
"channel_code" varchar(63) NOT NULL,
|
||||
"channel_id" bigint NULL,
|
||||
"channel_code" varchar(63) NULL,
|
||||
"template_id" bigint NOT NULL,
|
||||
"template_code" varchar(63) NOT NULL,
|
||||
"template_type" tinyint NOT NULL,
|
||||
|
||||
Reference in New Issue
Block a user