From 98b0218f465116eecb5be930706fde7ef5b61bdf Mon Sep 17 00:00:00 2001 From: lihongjie0209 Date: Fri, 5 Jan 2024 16:03:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9F=AD=E4=BF=A1=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 + .../coal/session/LoginController.java | 9 + .../cn/lihongjie/coal/session/LoginDto.java | 7 + .../coal/session/SessionService.java | 325 ++++++++++++------ .../coal/sms/controller/SmsController.java | 54 +++ .../lihongjie/coal/sms/dto/CreateSmsDto.java | 8 + .../cn/lihongjie/coal/sms/dto/SmsDto.java | 8 + .../lihongjie/coal/sms/dto/UpdateSmsDto.java | 8 + .../lihongjie/coal/sms/entity/SmsEntity.java | 42 +++ .../lihongjie/coal/sms/mapper/SmsMapper.java | 17 + .../coal/sms/repository/SmsRepository.java | 9 + .../coal/sms/service/SmsService.java | 165 +++++++++ .../controller/SmsTemplateController.java | 54 +++ .../smsTemplate/dto/CreateSmsTemplateDto.java | 8 + .../coal/smsTemplate/dto/SmsTemplateDto.java | 8 + .../smsTemplate/dto/UpdateSmsTemplateDto.java | 8 + .../smsTemplate/entity/SmsTemplateEntity.java | 24 ++ .../smsTemplate/mapper/SmsTemplateMapper.java | 19 + .../repository/SmsTemplateRepository.java | 9 + .../service/SmsTemplateService.java | 70 ++++ .../coal/user/service/UserService.java | 21 ++ 21 files changed, 768 insertions(+), 111 deletions(-) create mode 100644 src/main/java/cn/lihongjie/coal/sms/controller/SmsController.java create mode 100644 src/main/java/cn/lihongjie/coal/sms/dto/CreateSmsDto.java create mode 100644 src/main/java/cn/lihongjie/coal/sms/dto/SmsDto.java create mode 100644 src/main/java/cn/lihongjie/coal/sms/dto/UpdateSmsDto.java create mode 100644 src/main/java/cn/lihongjie/coal/sms/entity/SmsEntity.java create mode 100644 src/main/java/cn/lihongjie/coal/sms/mapper/SmsMapper.java create mode 100644 src/main/java/cn/lihongjie/coal/sms/repository/SmsRepository.java create mode 100644 src/main/java/cn/lihongjie/coal/sms/service/SmsService.java create mode 100644 src/main/java/cn/lihongjie/coal/smsTemplate/controller/SmsTemplateController.java create mode 100644 src/main/java/cn/lihongjie/coal/smsTemplate/dto/CreateSmsTemplateDto.java create mode 100644 src/main/java/cn/lihongjie/coal/smsTemplate/dto/SmsTemplateDto.java create mode 100644 src/main/java/cn/lihongjie/coal/smsTemplate/dto/UpdateSmsTemplateDto.java create mode 100644 src/main/java/cn/lihongjie/coal/smsTemplate/entity/SmsTemplateEntity.java create mode 100644 src/main/java/cn/lihongjie/coal/smsTemplate/mapper/SmsTemplateMapper.java create mode 100644 src/main/java/cn/lihongjie/coal/smsTemplate/repository/SmsTemplateRepository.java create mode 100644 src/main/java/cn/lihongjie/coal/smsTemplate/service/SmsTemplateService.java diff --git a/pom.xml b/pom.xml index 574bf741..f8394c01 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,12 @@ spring-boot-starter-websocket + + com.aliyun + dysmsapi20170525 + 2.0.24 + + com.nulab-inc zxcvbn diff --git a/src/main/java/cn/lihongjie/coal/session/LoginController.java b/src/main/java/cn/lihongjie/coal/session/LoginController.java index dd729779..0c150a9b 100644 --- a/src/main/java/cn/lihongjie/coal/session/LoginController.java +++ b/src/main/java/cn/lihongjie/coal/session/LoginController.java @@ -3,6 +3,7 @@ package cn.lihongjie.coal.session; import cn.lihongjie.coal.annotation.Anonymous; import cn.lihongjie.coal.annotation.SysLog; import cn.lihongjie.coal.base.controller.BaseController; +import cn.lihongjie.coal.base.dto.IdRequest; import cn.lihongjie.coal.common.Ctx; import cn.lihongjie.coal.user.dto.UserDto; import cn.lihongjie.coal.user.service.UserService; @@ -36,6 +37,14 @@ public class LoginController extends BaseController { return this.service.genCaptcha(); } + + @PostMapping("/loginSmsCode") + @Anonymous + public CaptchaDto loginSmsCode(@RequestBody IdRequest request) { + return this.service.loginSmsCode(request); + } + + @PostMapping("/logout") @SysLog(action = "退出") public Object logout() { diff --git a/src/main/java/cn/lihongjie/coal/session/LoginDto.java b/src/main/java/cn/lihongjie/coal/session/LoginDto.java index f5c8c513..b93205cb 100644 --- a/src/main/java/cn/lihongjie/coal/session/LoginDto.java +++ b/src/main/java/cn/lihongjie/coal/session/LoginDto.java @@ -5,7 +5,14 @@ import lombok.Data; @Data public class LoginDto { + /** + * 0 用户名密码登录 + * 1 短信验证码登录 + */ + private String loginType = "0"; + private String username; + private String phone; private String password; private String captchaId; private String captcha; diff --git a/src/main/java/cn/lihongjie/coal/session/SessionService.java b/src/main/java/cn/lihongjie/coal/session/SessionService.java index cb58684f..bf572abb 100644 --- a/src/main/java/cn/lihongjie/coal/session/SessionService.java +++ b/src/main/java/cn/lihongjie/coal/session/SessionService.java @@ -13,15 +13,23 @@ import cn.lihongjie.coal.loginUserHis.service.LoginUserHisService; import cn.lihongjie.coal.organization.entity.OrganizationEntity; import cn.lihongjie.coal.organization.service.OrganizationService; import cn.lihongjie.coal.ratelimit.RateLimiterService; +import cn.lihongjie.coal.sms.service.SmsService; +import cn.lihongjie.coal.smsTemplate.entity.SmsTemplateEntity; +import cn.lihongjie.coal.smsTemplate.service.SmsTemplateService; import cn.lihongjie.coal.sysconfig.service.SysConfigService; import cn.lihongjie.coal.user.dto.UserDto; import cn.lihongjie.coal.user.entity.UserEntity; import cn.lihongjie.coal.user.service.UserService; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.wf.captcha.SpecCaptcha; import com.wf.captcha.base.Captcha; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import jakarta.servlet.http.HttpServletRequest; import lombok.Data; @@ -31,6 +39,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -43,7 +52,9 @@ import org.springframework.web.context.request.ServletRequestAttributes; import java.time.LocalDateTime; import java.util.Collection; +import java.util.List; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @Service @@ -97,113 +108,7 @@ public class SessionService { @Autowired RateLimiterService rateLimiterService; - @SneakyThrows - public void login(LoginDto dto) { - HttpServletRequest request = - ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) - .getRequest(); - UserEntity user = null; - - try { - - if (sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_CAPTCHA)) { - - String captchaId = dto.getCaptchaId(); - if (captchaId == null) { - throw new BizException("验证码错误, 请刷新验证码重试"); - } - - String expectCaptcha = stringRedisTemplate.opsForValue().get(captchaId); - - if (expectCaptcha == null) { - throw new BizException("验证码已失效"); - } - - if (!StringUtils.equals(expectCaptcha, dto.getCaptcha())) { - stringRedisTemplate.opsForValue().getAndDelete(captchaId); - throw new BizException("验证码错误, 请刷新验证码重试"); - } - } - - user = - userService - .findByUsername(dto.getUsername()) - .orElseThrow(() -> new BizException("用户名或者密码错误")); - - if (user.isDisabled()) { - throw new BizException("用户被禁用"); - } - - OrganizationEntity organization = organizationService.get(user.getOrganizationId()); - if (organization.isDisabled()) { - throw new BizException("用户所在机构被禁用"); - } - - if (organization.getExpireTime()!=null && organization.getExpireTime().isBefore(LocalDateTime.now())) { - throw new BizException("用户所在机构已过期"); - } - - if (!userService.isValidPassword(dto.getPassword(), user.getPassword())) { - throw new BizException("用户名或者密码错误"); - } - - LoginUserEntity entity = new LoginUserEntity(); - entity.setUser(user); - entity.setCaptcha(dto.getCaptcha()); - entity.setIp(RequestUtils.getIp(request)); - entity.setUa(RequestUtils.getUa(request)); - try { - - entity.setLocation(ipQueryService.query(entity.getIp())); - } catch (Exception e) { - log.warn("查询ip地址失败 {}", entity.getIp(), e); - } - loginUserService.newLogin(entity); - - LoginUserDto loginUserDto = loginUserService.getFromCache(entity); - - String sessionId = entity.getId(); - - SecurityContext context = SecurityContextHolder.createEmptyContext(); - - context.setAuthentication( - new MyAuthentication(loginUserDto, loginUserDto.getUser(), sessionId)); - - SecurityContextHolder.setContext(context); - LoginUserHisEntity his = new LoginUserHisEntity(); - his.setIp(RequestUtils.getIp(request)); - his.setUa(RequestUtils.getUa(request)); - his.setCaptcha(dto.getCaptcha()); - his.setUserName(dto.getUsername()); - his.setLoginStatus("1"); - his.setLocation(ipQueryService.query(his.getIp())); - his.setLoginTime(LocalDateTime.now()); - his.setUser(user); - his.setSessionId(sessionId); - - - - - - loginUserHisService.save(his); - rateLimiterService.initRL(sessionId, user.getId()); - - } catch (Exception e) { - - LoginUserHisEntity his = new LoginUserHisEntity(); - his.setIp(RequestUtils.getIp(request)); - his.setUa(RequestUtils.getUa(request)); - his.setCaptcha(dto.getCaptcha()); - his.setUserName(dto.getUsername()); - his.setLoginStatus("1"); - his.setLocation(ipQueryService.query(his.getIp())); - his.setLoginTime(LocalDateTime.now()); - his.setUser(user); - his.setRemarks(e.getMessage()); - loginUserHisService.save(his); - throw e; - } - } + @Autowired SmsService smsService; @SneakyThrows public void rebuildSession(String sessionId) { @@ -262,7 +167,6 @@ public class SessionService { public void userLogout() { - loginUserService.logout( new IdRequest( ((SessionService.MyAuthentication) @@ -273,9 +177,6 @@ public class SessionService { ((SessionService.MyAuthentication) SecurityContextHolder.getContext().getAuthentication()) .getSessionId()); - - - } public void anonymousSession() { @@ -289,6 +190,208 @@ public class SessionService { SecurityContextHolder.setContext(context); } + @Autowired SmsTemplateService smsTemplateService; + + @SneakyThrows + public void login(LoginDto dto) { + HttpServletRequest request = + ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); + UserEntity user = null; + + try { + + if ((StringUtils.equalsIgnoreCase(dto.getLoginType(), "0") + && sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_CAPTCHA))) { + + String captchaId = dto.getCaptchaId(); + if (captchaId == null) { + throw new BizException("验证码错误, 请刷新验证码重试"); + } + + String expectCaptcha = stringRedisTemplate.opsForValue().get(captchaId); + + if (expectCaptcha == null) { + throw new BizException("验证码已失效"); + } + + if (!StringUtils.equals(expectCaptcha, dto.getCaptcha())) { + stringRedisTemplate.opsForValue().getAndDelete(captchaId); + throw new BizException("验证码错误, 请刷新验证码重试"); + } + } + + if (StringUtils.equalsIgnoreCase(dto.getLoginType(), "1")) { + if (StringUtils.isEmpty(dto.getCaptcha()) || StringUtils.isEmpty(dto.getPhone())) { + throw new BizException("短信验证码或者手机号不能为空"); + } + user = userService.findUniqUserByPhone(dto.getPhone()); + + String captchaId = dto.getCaptchaId(); + if (captchaId == null) { + throw new BizException("验证码错误, 请重新发送"); + } + + String expectCaptcha = stringRedisTemplate.opsForValue().get(captchaId); + + if (expectCaptcha == null) { + throw new BizException("验证码已失效"); + } + + if (!StringUtils.equals(expectCaptcha, dto.getCaptcha())) { + stringRedisTemplate.opsForValue().getAndDelete(captchaId); + throw new BizException("验证码错误, 请重新发送"); + } + + } else { + + user = + userService + .findByUsername(dto.getUsername()) + .orElseThrow(() -> new BizException("用户名或者密码错误")); + } + + if (user.isDisabled()) { + throw new BizException("用户被禁用"); + } + + OrganizationEntity organization = organizationService.get(user.getOrganizationId()); + if (organization.isDisabled()) { + throw new BizException("用户所在机构被禁用"); + } + + if (organization.getExpireTime() != null + && organization.getExpireTime().isBefore(LocalDateTime.now())) { + throw new BizException("用户所在机构已过期"); + } + + if (!userService.isValidPassword(dto.getPassword(), user.getPassword())) { + throw new BizException("用户名或者密码错误"); + } + + LoginUserEntity entity = new LoginUserEntity(); + entity.setUser(user); + entity.setCaptcha(dto.getCaptcha()); + entity.setIp(RequestUtils.getIp(request)); + entity.setUa(RequestUtils.getUa(request)); + try { + + entity.setLocation(ipQueryService.query(entity.getIp())); + } catch (Exception e) { + log.warn("查询ip地址失败 {}", entity.getIp(), e); + } + loginUserService.newLogin(entity); + + LoginUserDto loginUserDto = loginUserService.getFromCache(entity); + + String sessionId = entity.getId(); + + SecurityContext context = SecurityContextHolder.createEmptyContext(); + + context.setAuthentication( + new MyAuthentication(loginUserDto, loginUserDto.getUser(), sessionId)); + + SecurityContextHolder.setContext(context); + LoginUserHisEntity his = new LoginUserHisEntity(); + his.setIp(RequestUtils.getIp(request)); + his.setUa(RequestUtils.getUa(request)); + his.setCaptcha(dto.getCaptcha()); + his.setUserName(dto.getUsername()); + his.setLoginStatus("1"); + his.setLocation(ipQueryService.query(his.getIp())); + his.setLoginTime(LocalDateTime.now()); + his.setUser(user); + his.setSessionId(sessionId); + + loginUserHisService.save(his); + rateLimiterService.initRL(sessionId, user.getId()); + + } catch (Exception e) { + + LoginUserHisEntity his = new LoginUserHisEntity(); + his.setIp(RequestUtils.getIp(request)); + his.setUa(RequestUtils.getUa(request)); + his.setCaptcha(dto.getCaptcha()); + his.setUserName(dto.getUsername()); + his.setLoginStatus("1"); + his.setLocation(ipQueryService.query(his.getIp())); + his.setLoginTime(LocalDateTime.now()); + his.setUser(user); + his.setRemarks(e.getMessage()); + loginUserHisService.save(his); + throw e; + } + } + + @SneakyThrows + public CaptchaDto loginSmsCode(IdRequest request) { + + UserEntity user = userService.findUniqUserByPhone(request.getId()); + + String organizationId = user.getOrganizationId(); + + String id = UUID.randomUUID().toString(); + + String code = ThreadLocalRandom.current().nextInt(1, 9999) + ""; + + stringRedisTemplate.opsForValue().set(id, code, 5, TimeUnit.MINUTES); + + ObjectNode objectNode = objectMapper.createObjectNode(); + objectNode.put("code", code); + + Long orgTemplateCount = + smsTemplateService.count( + new Specification() { + @Override + public Predicate toPredicate( + Root root, + CriteriaQuery query, + CriteriaBuilder criteriaBuilder) { + + return criteriaBuilder.and( + criteriaBuilder.equal(root.get("code"), "login"), + criteriaBuilder.equal( + root.get("organizationId"), organizationId)); + } + }); + + if (orgTemplateCount > 0) { + + smsService.sendSmsMessage( + user.getPhone(), + objectMapper.writeValueAsString(objectNode), + "login", + organizationId); + } else { + + List all = + smsTemplateService.findAll( + new Specification() { + @Override + public Predicate toPredicate( + Root root, + CriteriaQuery query, + CriteriaBuilder criteriaBuilder) { + + return criteriaBuilder.and( + criteriaBuilder.equal( + root.get("code"), "login_global")); + } + }); + + if (all.isEmpty()) { + throw new BizException("没有找到对应的短信模板 login_global"); + } + + smsService.sendSmsMessage( + user.getPhone(), + objectMapper.writeValueAsString(objectNode), + all.get(0), + organizationId); + } + + return new CaptchaDto(id, null); + } @Data public static class MyAuthentication implements Authentication { diff --git a/src/main/java/cn/lihongjie/coal/sms/controller/SmsController.java b/src/main/java/cn/lihongjie/coal/sms/controller/SmsController.java new file mode 100644 index 00000000..0ae17268 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/sms/controller/SmsController.java @@ -0,0 +1,54 @@ +package cn.lihongjie.coal.sms.controller; + +import cn.lihongjie.coal.annotation.OrgScope; +import cn.lihongjie.coal.annotation.SysLog; +import cn.lihongjie.coal.base.dto.CommonQuery; +import cn.lihongjie.coal.base.dto.IdRequest; +import cn.lihongjie.coal.sms.dto.CreateSmsDto; +import cn.lihongjie.coal.sms.dto.SmsDto; +import cn.lihongjie.coal.sms.dto.UpdateSmsDto; +import cn.lihongjie.coal.sms.service.SmsService; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/sms") +@SysLog(module = "短信通知") +@Slf4j +@OrgScope +public class SmsController { + @Autowired private SmsService service; + + @PostMapping("/create") + public SmsDto create(@RequestBody CreateSmsDto request) { + return this.service.create(request); + } + + @PostMapping("/update") + public SmsDto update(@RequestBody UpdateSmsDto request) { + return this.service.update(request); + } + + @PostMapping("/delete") + public Object delete(@RequestBody IdRequest request) { + this.service.delete(request); + return true; + } + + @PostMapping("/getById") + public SmsDto getById(@RequestBody IdRequest request) { + return this.service.getById(request.getId()); + } + + @PostMapping("/list") + public Page list(@RequestBody CommonQuery request) { + return this.service.list(request); + } +} diff --git a/src/main/java/cn/lihongjie/coal/sms/dto/CreateSmsDto.java b/src/main/java/cn/lihongjie/coal/sms/dto/CreateSmsDto.java new file mode 100644 index 00000000..9f2b1771 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/sms/dto/CreateSmsDto.java @@ -0,0 +1,8 @@ +package cn.lihongjie.coal.sms.dto; + +import cn.lihongjie.coal.base.dto.OrgCommonDto; + +import lombok.Data; + +@Data +public class CreateSmsDto extends OrgCommonDto {} diff --git a/src/main/java/cn/lihongjie/coal/sms/dto/SmsDto.java b/src/main/java/cn/lihongjie/coal/sms/dto/SmsDto.java new file mode 100644 index 00000000..16a1a61f --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/sms/dto/SmsDto.java @@ -0,0 +1,8 @@ +package cn.lihongjie.coal.sms.dto; + +import cn.lihongjie.coal.base.dto.OrgCommonDto; + +import lombok.Data; + +@Data +public class SmsDto extends OrgCommonDto {} diff --git a/src/main/java/cn/lihongjie/coal/sms/dto/UpdateSmsDto.java b/src/main/java/cn/lihongjie/coal/sms/dto/UpdateSmsDto.java new file mode 100644 index 00000000..a73e251d --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/sms/dto/UpdateSmsDto.java @@ -0,0 +1,8 @@ +package cn.lihongjie.coal.sms.dto; + +import cn.lihongjie.coal.base.dto.OrgCommonDto; + +import lombok.Data; + +@Data +public class UpdateSmsDto extends OrgCommonDto {} diff --git a/src/main/java/cn/lihongjie/coal/sms/entity/SmsEntity.java b/src/main/java/cn/lihongjie/coal/sms/entity/SmsEntity.java new file mode 100644 index 00000000..ccca58a3 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/sms/entity/SmsEntity.java @@ -0,0 +1,42 @@ +package cn.lihongjie.coal.sms.entity; + +import cn.lihongjie.coal.base.entity.OrgCommonEntity; + +import jakarta.persistence.Entity; + +import lombok.Data; + +import org.hibernate.annotations.Comment; +import org.hibernate.annotations.Formula; + +@Data +@Entity +public class SmsEntity extends OrgCommonEntity { + + private String accessKeyId; + + private String endpoint; + + private String signName; + + private String templateCode; + + private String phoneNumbers; + + private String templateParam; + + private String response; + + @Comment("发送状态") + private String execStatus; + + @Formula( + "(select i.name\n" + + "from t_dictionary d,\n" + + " t_dictionary_item i\n" + + "where d.id = i.dictionary_id\n" + + " and d.code = 'common.execStatus'\n" + + " and i.code = exec_status)") + private String execStatusName; + +} diff --git a/src/main/java/cn/lihongjie/coal/sms/mapper/SmsMapper.java b/src/main/java/cn/lihongjie/coal/sms/mapper/SmsMapper.java new file mode 100644 index 00000000..823f0aef --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/sms/mapper/SmsMapper.java @@ -0,0 +1,17 @@ +package cn.lihongjie.coal.sms.mapper; + +import cn.lihongjie.coal.base.mapper.BaseMapper; +import cn.lihongjie.coal.base.mapper.CommonMapper; +import cn.lihongjie.coal.sms.dto.CreateSmsDto; +import cn.lihongjie.coal.sms.dto.SmsDto; +import cn.lihongjie.coal.sms.dto.UpdateSmsDto; +import cn.lihongjie.coal.sms.entity.SmsEntity; + +import org.mapstruct.Mapper; +import org.mapstruct.control.DeepClone; + +@Mapper( + componentModel = org.mapstruct.MappingConstants.ComponentModel.SPRING, + uses = {CommonMapper.class}, + mappingControl = DeepClone.class) +public interface SmsMapper extends BaseMapper {} diff --git a/src/main/java/cn/lihongjie/coal/sms/repository/SmsRepository.java b/src/main/java/cn/lihongjie/coal/sms/repository/SmsRepository.java new file mode 100644 index 00000000..bc6a6502 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/sms/repository/SmsRepository.java @@ -0,0 +1,9 @@ +package cn.lihongjie.coal.sms.repository; + +import cn.lihongjie.coal.base.dao.BaseRepository; +import cn.lihongjie.coal.sms.entity.SmsEntity; + +import org.springframework.stereotype.Repository; + +@Repository +public interface SmsRepository extends BaseRepository {} diff --git a/src/main/java/cn/lihongjie/coal/sms/service/SmsService.java b/src/main/java/cn/lihongjie/coal/sms/service/SmsService.java new file mode 100644 index 00000000..10ff32a3 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/sms/service/SmsService.java @@ -0,0 +1,165 @@ +package cn.lihongjie.coal.sms.service; + +import cn.lihongjie.coal.base.dto.CommonQuery; +import cn.lihongjie.coal.base.dto.IdRequest; +import cn.lihongjie.coal.base.service.BaseService; +import cn.lihongjie.coal.common.Ctx; +import cn.lihongjie.coal.exception.BizException; +import cn.lihongjie.coal.sms.dto.CreateSmsDto; +import cn.lihongjie.coal.sms.dto.SmsDto; +import cn.lihongjie.coal.sms.dto.UpdateSmsDto; +import cn.lihongjie.coal.sms.entity.SmsEntity; +import cn.lihongjie.coal.sms.mapper.SmsMapper; +import cn.lihongjie.coal.sms.repository.SmsRepository; +import cn.lihongjie.coal.smsTemplate.entity.SmsTemplateEntity; +import cn.lihongjie.coal.smsTemplate.service.SmsTemplateService; + +import com.aliyun.dysmsapi20170525.Client; +import com.aliyun.dysmsapi20170525.models.SendSmsResponse; +import com.aliyun.teaopenapi.models.Config; + +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Slf4j +@Transactional +public class SmsService extends BaseService { + @Autowired private SmsRepository repository; + + @Autowired private SmsMapper mapper; + + @Autowired private ConversionService conversionService; + + + @Autowired + private SmsTemplateService smsTemplateService; + + + @SneakyThrows + public void sendSmsMessage(String phone, String templateParam, String sysTemplateCode) { + sendSmsMessage(phone, templateParam, sysTemplateCode, Ctx.currentUser().getOrganizationId()); + } + + @SneakyThrows + public void sendSmsMessage(String phone, String templateParam, String sysTemplateCode, String organizationId) { + + if (sysTemplateCode == null){ + throw new BizException("短信模板不能为空"); + } + + if (organizationId == null){ + throw new BizException("组织机构不能为空"); + } + + + List all = smsTemplateService.findAll(new Specification() { + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) { + return criteriaBuilder.and( + criteriaBuilder.equal(root.get("code"), sysTemplateCode), + criteriaBuilder.equal(root.get("organizationId"), organizationId) + ); + } + }); + + if (all.isEmpty()){ + throw new BizException("没有找到对应的短信模板 " + sysTemplateCode); + } + + sendSmsMessage(phone, templateParam, all.get(0), organizationId); + } + + @SneakyThrows + public void sendSmsMessage(String phone, String templateParam, SmsTemplateEntity template, String organizationId) { + + SmsEntity sms = new SmsEntity(); + sms.setPhoneNumbers(phone); + sms.setOrganizationId(organizationId); + sms.setTemplateParam(templateParam); + sms.setAccessKeyId(template.getAccessKeyId()); + sms.setEndpoint(template.getEndpoint()); + sms.setSignName(template.getSignName()); + sms.setTemplateCode(template.getTemplateCode()); + + + Config config = new Config(); + config.accessKeyId = sms.getAccessKeyId(); + config.accessKeySecret = template.getAccessKeySecret(); + config.endpoint = sms.getEndpoint(); + + com.aliyun.dysmsapi20170525.Client client = new Client(config); + com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = + new com.aliyun.dysmsapi20170525.models.SendSmsRequest() + .setSignName(sms.getSignName()) + .setTemplateCode(sms.getTemplateCode()) + .setPhoneNumbers(sms.getPhoneNumbers()) + .setTemplateParam(sms.getTemplateParam()); + com.aliyun.teautil.models.RuntimeOptions runtime = + new com.aliyun.teautil.models.RuntimeOptions(); + try { + // 复制代码运行请自行打印 API 的返回值 + SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime); + sms.setResponse(sendSmsResponse.getBody().toString()); + } catch (Exception _error) { + sms.setResponse(_error.getMessage()); + sms.setExecStatus("1"); + } + + this.repository.save(sms); + } + + public SmsDto create(CreateSmsDto request) { + SmsEntity entity = mapper.toEntity(request); + + this.repository.save(entity); + return getById(entity.getId()); + } + + public SmsDto update(UpdateSmsDto request) { + SmsEntity entity = this.repository.get(request.getId()); + this.mapper.updateEntity(entity, request); + + this.repository.save(entity); + + return getById(entity.getId()); + } + + public void delete(IdRequest request) { + this.repository.deleteAllById(request.getIds()); + } + + public SmsDto getById(String id) { + SmsEntity entity = repository.get(id); + + return mapper.toDto(entity); + } + + public Page list(CommonQuery query) { + Page page = + repository.findAll( + query.specification(conversionService), + PageRequest.of( + query.getPageNo(), + query.getPageSize(), + Sort.by(query.getOrders()))); + + return page.map(this.mapper::toDto); + } +} diff --git a/src/main/java/cn/lihongjie/coal/smsTemplate/controller/SmsTemplateController.java b/src/main/java/cn/lihongjie/coal/smsTemplate/controller/SmsTemplateController.java new file mode 100644 index 00000000..2f7a2af2 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/smsTemplate/controller/SmsTemplateController.java @@ -0,0 +1,54 @@ +package cn.lihongjie.coal.smsTemplate.controller; + +import cn.lihongjie.coal.annotation.OrgScope; +import cn.lihongjie.coal.annotation.SysLog; +import cn.lihongjie.coal.base.dto.CommonQuery; +import cn.lihongjie.coal.base.dto.IdRequest; +import cn.lihongjie.coal.smsTemplate.dto.CreateSmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.dto.SmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.dto.UpdateSmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.service.SmsTemplateService; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/smsTemplate") +@SysLog(module = "短信模板") +@Slf4j +@OrgScope +public class SmsTemplateController { + @Autowired private SmsTemplateService service; + + @PostMapping("/create") + public SmsTemplateDto create(@RequestBody CreateSmsTemplateDto request) { + return this.service.create(request); + } + + @PostMapping("/update") + public SmsTemplateDto update(@RequestBody UpdateSmsTemplateDto request) { + return this.service.update(request); + } + + @PostMapping("/delete") + public Object delete(@RequestBody IdRequest request) { + this.service.delete(request); + return true; + } + + @PostMapping("/getById") + public SmsTemplateDto getById(@RequestBody IdRequest request) { + return this.service.getById(request.getId()); + } + + @PostMapping("/list") + public Page list(@RequestBody CommonQuery request) { + return this.service.list(request); + } +} diff --git a/src/main/java/cn/lihongjie/coal/smsTemplate/dto/CreateSmsTemplateDto.java b/src/main/java/cn/lihongjie/coal/smsTemplate/dto/CreateSmsTemplateDto.java new file mode 100644 index 00000000..867c690b --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/smsTemplate/dto/CreateSmsTemplateDto.java @@ -0,0 +1,8 @@ +package cn.lihongjie.coal.smsTemplate.dto; + +import cn.lihongjie.coal.base.dto.OrgCommonDto; + +import lombok.Data; + +@Data +public class CreateSmsTemplateDto extends OrgCommonDto {} diff --git a/src/main/java/cn/lihongjie/coal/smsTemplate/dto/SmsTemplateDto.java b/src/main/java/cn/lihongjie/coal/smsTemplate/dto/SmsTemplateDto.java new file mode 100644 index 00000000..45cbb2bb --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/smsTemplate/dto/SmsTemplateDto.java @@ -0,0 +1,8 @@ +package cn.lihongjie.coal.smsTemplate.dto; + +import cn.lihongjie.coal.base.dto.OrgCommonDto; + +import lombok.Data; + +@Data +public class SmsTemplateDto extends OrgCommonDto {} diff --git a/src/main/java/cn/lihongjie/coal/smsTemplate/dto/UpdateSmsTemplateDto.java b/src/main/java/cn/lihongjie/coal/smsTemplate/dto/UpdateSmsTemplateDto.java new file mode 100644 index 00000000..7fe24e6f --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/smsTemplate/dto/UpdateSmsTemplateDto.java @@ -0,0 +1,8 @@ +package cn.lihongjie.coal.smsTemplate.dto; + +import cn.lihongjie.coal.base.dto.OrgCommonDto; + +import lombok.Data; + +@Data +public class UpdateSmsTemplateDto extends OrgCommonDto {} diff --git a/src/main/java/cn/lihongjie/coal/smsTemplate/entity/SmsTemplateEntity.java b/src/main/java/cn/lihongjie/coal/smsTemplate/entity/SmsTemplateEntity.java new file mode 100644 index 00000000..07c3b53e --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/smsTemplate/entity/SmsTemplateEntity.java @@ -0,0 +1,24 @@ +package cn.lihongjie.coal.smsTemplate.entity; + +import cn.lihongjie.coal.base.entity.OrgCommonEntity; + +import jakarta.persistence.Entity; + +import lombok.Data; + +@Data +@Entity +public class SmsTemplateEntity extends OrgCommonEntity { + + + private String accessKeyId; + + private String accessKeySecret; + + private String endpoint; + + private String signName; + + private String templateCode; + +} diff --git a/src/main/java/cn/lihongjie/coal/smsTemplate/mapper/SmsTemplateMapper.java b/src/main/java/cn/lihongjie/coal/smsTemplate/mapper/SmsTemplateMapper.java new file mode 100644 index 00000000..1aa164d2 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/smsTemplate/mapper/SmsTemplateMapper.java @@ -0,0 +1,19 @@ +package cn.lihongjie.coal.smsTemplate.mapper; + +import cn.lihongjie.coal.base.mapper.BaseMapper; +import cn.lihongjie.coal.base.mapper.CommonMapper; +import cn.lihongjie.coal.smsTemplate.dto.CreateSmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.dto.SmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.dto.UpdateSmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.entity.SmsTemplateEntity; + +import org.mapstruct.Mapper; +import org.mapstruct.control.DeepClone; + +@Mapper( + componentModel = org.mapstruct.MappingConstants.ComponentModel.SPRING, + uses = {CommonMapper.class}, + mappingControl = DeepClone.class) +public interface SmsTemplateMapper + extends BaseMapper< + SmsTemplateEntity, SmsTemplateDto, CreateSmsTemplateDto, UpdateSmsTemplateDto> {} diff --git a/src/main/java/cn/lihongjie/coal/smsTemplate/repository/SmsTemplateRepository.java b/src/main/java/cn/lihongjie/coal/smsTemplate/repository/SmsTemplateRepository.java new file mode 100644 index 00000000..a3a709af --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/smsTemplate/repository/SmsTemplateRepository.java @@ -0,0 +1,9 @@ +package cn.lihongjie.coal.smsTemplate.repository; + +import cn.lihongjie.coal.base.dao.BaseRepository; +import cn.lihongjie.coal.smsTemplate.entity.SmsTemplateEntity; + +import org.springframework.stereotype.Repository; + +@Repository +public interface SmsTemplateRepository extends BaseRepository {} diff --git a/src/main/java/cn/lihongjie/coal/smsTemplate/service/SmsTemplateService.java b/src/main/java/cn/lihongjie/coal/smsTemplate/service/SmsTemplateService.java new file mode 100644 index 00000000..f139080f --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/smsTemplate/service/SmsTemplateService.java @@ -0,0 +1,70 @@ +package cn.lihongjie.coal.smsTemplate.service; + +import cn.lihongjie.coal.base.dto.CommonQuery; +import cn.lihongjie.coal.base.dto.IdRequest; +import cn.lihongjie.coal.base.service.BaseService; +import cn.lihongjie.coal.smsTemplate.dto.CreateSmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.dto.SmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.dto.UpdateSmsTemplateDto; +import cn.lihongjie.coal.smsTemplate.entity.SmsTemplateEntity; +import cn.lihongjie.coal.smsTemplate.mapper.SmsTemplateMapper; +import cn.lihongjie.coal.smsTemplate.repository.SmsTemplateRepository; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Slf4j +@Transactional +public class SmsTemplateService extends BaseService { + @Autowired private SmsTemplateRepository repository; + + @Autowired private SmsTemplateMapper mapper; + + @Autowired private ConversionService conversionService; + + public SmsTemplateDto create(CreateSmsTemplateDto request) { + SmsTemplateEntity entity = mapper.toEntity(request); + + this.repository.save(entity); + return getById(entity.getId()); + } + + public SmsTemplateDto update(UpdateSmsTemplateDto request) { + SmsTemplateEntity entity = this.repository.get(request.getId()); + this.mapper.updateEntity(entity, request); + + this.repository.save(entity); + + return getById(entity.getId()); + } + + public void delete(IdRequest request) { + this.repository.deleteAllById(request.getIds()); + } + + public SmsTemplateDto getById(String id) { + SmsTemplateEntity entity = repository.get(id); + + return mapper.toDto(entity); + } + + public Page list(CommonQuery query) { + Page page = + repository.findAll( + query.specification(conversionService), + PageRequest.of( + query.getPageNo(), + query.getPageSize(), + Sort.by(query.getOrders()))); + + return page.map(this.mapper::toDto); + } +} diff --git a/src/main/java/cn/lihongjie/coal/user/service/UserService.java b/src/main/java/cn/lihongjie/coal/user/service/UserService.java index e57b9052..9a8ac818 100644 --- a/src/main/java/cn/lihongjie/coal/user/service/UserService.java +++ b/src/main/java/cn/lihongjie/coal/user/service/UserService.java @@ -38,6 +38,7 @@ import jakarta.persistence.criteria.Root; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -369,4 +370,24 @@ public class UserService extends BaseService { log.error("清除用户权限缓存失败", e); } } + + public UserEntity findUniqUserByPhone(final String phone) { + List users = findAll(new Specification() { + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) { + return criteriaBuilder.equal(root.get("phone"), phone); + } + }); + if (CollectionUtils.isEmpty(users)){ + throw new BizException("手机号不存在"); + } + + + if (users.size() > 1){ + throw new BizException("手机号重复"); + } + + UserEntity user = users.get(0); + return user; + } }