From d6a60de5834194c3068ab368c0c354ddce79e771 Mon Sep 17 00:00:00 2001 From: gaoqr <13665037151@163.com> Date: Tue, 14 Oct 2025 09:03:58 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E7=AE=A1=E7=90=86=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/test/WithMockLoginUser.java | 2 + ...thMockLoginUserSecurityContextFactory.java | 2 + .../module/system/api/sms/SmsCodeApi.java | 6 - .../module/system/api/sms/SmsSendApi.java | 28 -- .../module/system/api/sms/SmsCodeApiImpl.java | 7 - .../module/system/api/sms/SmsSendApiImpl.java | 32 -- .../admin/sms/SmsChannelController.java | 82 ---- .../admin/sms/SmsTemplateController.java | 3 +- .../sms/vo/channel/SmsChannelPageReqVO.java | 30 -- .../sms/vo/channel/SmsChannelRespVO.java | 45 -- .../sms/vo/channel/SmsChannelSaveReqVO.java | 42 -- .../vo/channel/SmsChannelSimpleRespVO.java | 19 - .../sms/vo/template/SmsTemplateSaveReqVO.java | 12 +- .../dal/dataobject/sms/SmsChannelDO.java | 62 --- .../system/dal/dataobject/sms/SmsCodeDO.java | 65 --- .../system/dal/dataobject/sms/SmsLogDO.java | 2 - .../dal/dataobject/sms/SmsTemplateDO.java | 2 - .../dal/mysql/sms/SmsChannelMapper.java | 25 - .../system/dal/mysql/sms/SmsCodeMapper.java | 28 -- .../sms/core/client/SmsClientFactory.java | 8 - .../client/impl/SmsClientFactoryImpl.java | 5 - .../system/service/sms/SmsChannelService.java | 68 --- .../service/sms/SmsChannelServiceImpl.java | 129 ------ .../system/service/sms/SmsCodeService.java | 8 - .../service/sms/SmsCodeServiceImpl.java | 32 -- .../system/service/sms/SmsLogServiceImpl.java | 3 +- .../system/service/sms/SmsSendService.java | 29 -- .../service/sms/SmsSendServiceImpl.java | 52 +-- .../service/sms/SmsTemplateServiceImpl.java | 15 +- .../sms/handler/SmsSendAfterSendHandler.java | 25 - .../impl/SmsSendAfterSendHandlerImpl.java | 46 -- .../src/main/resources/application-local.yaml | 1 - .../admin/dept/DeptControllerTest.java | 2 - .../admin/sms/SmsLogControllerTest.java | 88 ++++ .../admin/sms/SmsTemplateControllerTest.java | 196 ++++++++ .../service/organ/TenantServiceImplTest.java | 73 ++- .../service/sms/SmsChannelServiceTest.java | 224 +-------- .../service/sms/SmsCodeServiceImplTest.java | 438 ++++++++++++------ .../service/sms/SmsLogServiceImplTest.java | 43 +- .../service/sms/SmsSendServiceImplTest.java | 208 ++++----- .../sms/SmsTemplateServiceImplTest.java | 140 +++--- .../src/test/resources/sql/create_tables.sql | 8 +- 42 files changed, 855 insertions(+), 1480 deletions(-) delete mode 100644 cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/sms/SmsSendApi.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/sms/SmsSendApiImpl.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsChannelController.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsChannelDO.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsCodeDO.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/sms/SmsChannelMapper.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/sms/SmsCodeMapper.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/SmsSendAfterSendHandler.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/impl/SmsSendAfterSendHandlerImpl.java create mode 100644 cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/sms/SmsLogControllerTest.java create mode 100644 cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/sms/SmsTemplateControllerTest.java diff --git a/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUser.java b/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUser.java index ec6e7071a..cee21e892 100644 --- a/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUser.java +++ b/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUser.java @@ -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; diff --git a/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUserSecurityContextFactory.java b/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUserSecurityContextFactory.java index 6de655537..e11cc95df 100644 --- a/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUserSecurityContextFactory.java +++ b/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUserSecurityContextFactory.java @@ -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()); diff --git a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/sms/SmsCodeApi.java b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/sms/SmsCodeApi.java index 69080a48b..9bae416c8 100644 --- a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/sms/SmsCodeApi.java +++ b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/sms/SmsCodeApi.java @@ -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 useSmsCode(@Valid @RequestBody SmsCodeUseReqDTO reqDTO); - @GetMapping(PREFIX + "/validate") - @Operation(summary = "检查验证码是否有效") - CommonResult validateSmsCode(@Valid @RequestBody SmsCodeValidateReqDTO reqDTO); - } diff --git a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/sms/SmsSendApi.java b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/sms/SmsSendApi.java deleted file mode 100644 index aa6fc465f..000000000 --- a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/sms/SmsSendApi.java +++ /dev/null @@ -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 sendSingleSmsToAdmin(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO); - - @PostMapping(PREFIX + "/send-single-member") - @Operation(summary = "发送单条短信给 Member 用户", description = "在 mobile 为空时,使用 userId 加载对应 Member 的手机号") - CommonResult sendSingleSmsToMember(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO); - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/sms/SmsCodeApiImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/sms/SmsCodeApiImpl.java index 04f193371..754d7dcb6 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/sms/SmsCodeApiImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/sms/SmsCodeApiImpl.java @@ -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 validateSmsCode(SmsCodeValidateReqDTO reqDTO) { - smsCodeService.validateSmsCode(reqDTO); - return success(true); - } - } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/sms/SmsSendApiImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/sms/SmsSendApiImpl.java deleted file mode 100644 index 4fa2435b2..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/sms/SmsSendApiImpl.java +++ /dev/null @@ -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 sendSingleSmsToAdmin(SmsSendSingleToUserReqDTO reqDTO) { - return success(smsSendService.sendSingleSmsToAdmin(reqDTO.getMobile(), reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams())); - } - - @Override - public CommonResult sendSingleSmsToMember(SmsSendSingleToUserReqDTO reqDTO) { - return success(smsSendService.sendSingleSmsToMember(reqDTO.getMobile(), reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams())); - } - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsChannelController.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsChannelController.java deleted file mode 100644 index 6fe85cda0..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsChannelController.java +++ /dev/null @@ -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 createSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO createReqVO) { - return success(smsChannelService.createSmsChannel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新短信渠道") - @PreAuthorize("@ss.hasPermission('system:sms-channel:update')") - public CommonResult 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 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 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> getSmsChannelPage(@Valid SmsChannelPageReqVO pageVO) { - PageResult pageResult = smsChannelService.getSmsChannelPage(pageVO); - return success(BeanUtils.toBean(pageResult, SmsChannelRespVO.class)); - } - - @GetMapping({"/list-all-simple", "/simple-list"}) - @Operation(summary = "获得短信渠道精简列表", description = "包含被禁用的短信渠道") - public CommonResult> getSimpleSmsChannelList() { - List list = smsChannelService.getSmsChannelList(); - list.sort(Comparator.comparing(SmsChannelDO::getId)); - return success(BeanUtils.toBean(list, SmsChannelSimpleRespVO.class)); - } - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsTemplateController.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsTemplateController.java index 79d83e91c..2672fb4c7 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsTemplateController.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsTemplateController.java @@ -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 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())); } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java deleted file mode 100644 index 203de0d4a..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java +++ /dev/null @@ -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; - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java deleted file mode 100644 index 1520dc278..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java +++ /dev/null @@ -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; - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java deleted file mode 100644 index 99fb245ae..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java +++ /dev/null @@ -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; - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java deleted file mode 100644 index b83186c02..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java +++ /dev/null @@ -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; - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java index e62c4e925..69e709c5e 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java @@ -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; } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsChannelDO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsChannelDO.java deleted file mode 100644 index 42d60d572..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsChannelDO.java +++ /dev/null @@ -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; - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsCodeDO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsCodeDO.java deleted file mode 100644 index eedb99a45..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsCodeDO.java +++ /dev/null @@ -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; - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsLogDO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsLogDO.java index e324431c9..a91c54c67 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsLogDO.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsLogDO.java @@ -41,13 +41,11 @@ public class SmsLogDO extends BaseDO { /** * 短信渠道编号 * - * 关联 {@link SmsChannelDO#getId()} */ private Long channelId; /** * 短信渠道编码 * - * 冗余 {@link SmsChannelDO#getCode()} */ private String channelCode; diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsTemplateDO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsTemplateDO.java index 7b976a6dc..6c6496a39 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsTemplateDO.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/sms/SmsTemplateDO.java @@ -80,13 +80,11 @@ public class SmsTemplateDO extends BaseDO { /** * 短信渠道编号 * - * 关联 {@link SmsChannelDO#getId()} */ private Long channelId; /** * 短信渠道编码 * - * 冗余 {@link SmsChannelDO#getCode()} */ private String channelCode; diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/sms/SmsChannelMapper.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/sms/SmsChannelMapper.java deleted file mode 100644 index fac71d6c9..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/sms/SmsChannelMapper.java +++ /dev/null @@ -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 { - - default PageResult selectPage(SmsChannelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .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); - } - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/sms/SmsCodeMapper.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/sms/SmsCodeMapper.java deleted file mode 100644 index 325717931..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/sms/SmsCodeMapper.java +++ /dev/null @@ -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 { - - /** - * 获得手机号的最后一个手机验证码 - * - * @param mobile 手机号 - * @param scene 发送场景,选填 - * @param code 验证码 选填 - * @return 手机验证码 - */ - default SmsCodeDO selectLastByMobile(String mobile, String code, Integer scene) { - return selectOne(new QueryWrapperX() - .eq("mobile", mobile) - .eqIfPresent("scene", scene) - .eqIfPresent("code", code) - .orderByDesc("id") - .limitN(1)); - } - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/SmsClientFactory.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/SmsClientFactory.java index be6090587..8dea8c2b5 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/SmsClientFactory.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/SmsClientFactory.java @@ -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 * diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java index c6568535f..726e281a3 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java @@ -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); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsChannelService.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsChannelService.java index 73aaa3e65..c115b5cce 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsChannelService.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsChannelService.java @@ -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 getSmsChannelList(); - - /** - * 获得短信渠道分页 - * - * @param pageReqVO 分页查询 - * @return 短信渠道分页 - */ - PageResult getSmsChannelPage(SmsChannelPageReqVO pageReqVO); - - /** - * 获得短信客户端 - * - * @param id 编号 - * @return 短信客户端 - */ - SmsClient getSmsClient(Long id); - - /** - * 获得短信客户端 - * - * @param code 编码 - * @return 短信客户端 - */ - SmsClient getSmsClient(String code); - /** * 根据配置获得短信客户端 * diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsChannelServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsChannelServiceImpl.java index dd56984cc..e9cb89c74 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsChannelServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsChannelServiceImpl.java @@ -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 idClientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L), - new CacheLoader<>() { - - @Override - public SmsClient load(Long id) { - return smsClientFactory.getSmsClient(smsProperties); - } - - }); - - /** - * {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory - */ - @Getter - private final LoadingCache 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 getSmsChannelList() { - return smsChannelMapper.selectList(); - } - - @Override - public PageResult 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); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsCodeService.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsCodeService.java index 2faafe563..c8f41c3f5 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsCodeService.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsCodeService.java @@ -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); - /** * 检查验证码是否有效 * diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsCodeServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsCodeServiceImpl.java index a7911b1ef..3e33037c8 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsCodeServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsCodeServiceImpl.java @@ -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; - } - } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsLogServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsLogServiceImpl.java index c05f06923..8b8556d2f 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsLogServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsLogServiceImpl.java @@ -32,8 +32,7 @@ public class SmsLogServiceImpl implements SmsLogService { SmsTemplateDO template, String templateContent, Map 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); // 设置模板相关字段 diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendService.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendService.java index a4006d24c..e883dde30 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendService.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendService.java @@ -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 templateParams); - - /** - * 发送单条短信给用户 APP 的用户 - * - * 在 mobile 为空时,使用 userId 加载对应会员的手机号 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param templateCode 短信模板编号 - * @param templateParams 短信模板参数 - * @return 发送日志编号 - */ - Long sendSingleSmsToMember(String mobile, Long userId, - String templateCode, Map templateParams); - /** * 发送单条短信给用户 * diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendServiceImpl.java index ec334d486..26ba3d8e3 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendServiceImpl.java @@ -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 smsSendAfterSendHandlers; + private RedisTemplate redisTemplate; - @Override - public Long sendSingleSmsToAdmin(String mobile, Long userId, String templateCode, Map 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 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 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 receiveResults = smsClient.parseSmsReceiveStatus(text); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsTemplateServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsTemplateServiceImpl.java index 362c22956..1f5610349 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsTemplateServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsTemplateServiceImpl.java @@ -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(); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/SmsSendAfterSendHandler.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/SmsSendAfterSendHandler.java deleted file mode 100644 index 04b9cccbf..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/SmsSendAfterSendHandler.java +++ /dev/null @@ -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> params); -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/impl/SmsSendAfterSendHandlerImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/impl/SmsSendAfterSendHandlerImpl.java deleted file mode 100644 index 9bb8b4573..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/impl/SmsSendAfterSendHandlerImpl.java +++ /dev/null @@ -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> params) { - // 发送成功把验证码存入redis - if (sendResponse.getSuccess()) { - for (KeyValue keyValue : params) { - if ("code".equals(keyValue.getKey())) { - redisTemplate.opsForValue().set(String.format(RedisKeyConstants.SMS_CAPTCHA_VERIFICATION, sendResponse.getMobile()), keyValue.getValue(), smsCodeProperties.getExpireTimes()); - } - } - } - } -} diff --git a/cf-module-system/cf-module-system-biz/src/main/resources/application-local.yaml b/cf-module-system/cf-module-system-biz/src/main/resources/application-local.yaml index c582a850e..b9a4076d7 100644 --- a/cf-module-system/cf-module-system-biz/src/main/resources/application-local.yaml +++ b/cf-module-system/cf-module-system-biz/src/main/resources/application-local.yaml @@ -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 提示 --- #################### 微信公众号、小程序相关配置 #################### diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/dept/DeptControllerTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/dept/DeptControllerTest.java index b48f383d4..21ac74a4a 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/dept/DeptControllerTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/dept/DeptControllerTest.java @@ -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); diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/sms/SmsLogControllerTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/sms/SmsLogControllerTest.java new file mode 100644 index 000000000..6d6e0f9d7 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/sms/SmsLogControllerTest.java @@ -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 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 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"))); + } +} diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/sms/SmsTemplateControllerTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/sms/SmsTemplateControllerTest.java new file mode 100644 index 000000000..36a4a97de --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/sms/SmsTemplateControllerTest.java @@ -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 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 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"))); + } +} diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/organ/TenantServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/organ/TenantServiceImplTest.java index 0382b04f2..ece84e6d4 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/organ/TenantServiceImplTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/organ/TenantServiceImplTest.java @@ -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 未禁用 diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsChannelServiceTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsChannelServiceTest.java index 17809abc2..88c08767c 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsChannelServiceTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsChannelServiceTest.java @@ -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 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 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); - } - } diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsCodeServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsCodeServiceImplTest.java index 1fe21bf52..26106357f 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsCodeServiceImplTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsCodeServiceImplTest.java @@ -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 redisTemplate; - @MockBean + @MockitoBean + private ZSetOperations zSetOperations; + + @MockitoBean + private ValueOperations 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)); + } } diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsLogServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsLogServiceImplTest.java index 1bb0286fc..640964f36 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsLogServiceImplTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsLogServiceImplTest.java @@ -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()); } // ========== 随机对象 ========== diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsSendServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsSendServiceImplTest.java index ed2094052..5481f8558 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsSendServiceImplTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsSendServiceImplTest.java @@ -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 templateParams = MapUtil.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 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 valueOperations; - @Test - public void testSendSingleSmsToUser() { - // 准备参数 - Long userId = randomLongId(); - String templateCode = randomString(); - Map templateParams = MapUtil.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 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()))); diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsTemplateServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsTemplateServiceImplTest.java index ec3c619fd..3f1d829f4 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsTemplateServiceImplTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsTemplateServiceImplTest.java @@ -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 params = MapUtil.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)); + } } diff --git a/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql b/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql index 1c7530163..90d639330 100644 --- a/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql +++ b/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql @@ -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,