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;
+ }
}