diff --git a/core/src/main/java/com/dite/znpt/service/EmailService.java b/core/src/main/java/com/dite/znpt/service/EmailService.java new file mode 100644 index 0000000..e13ba73 --- /dev/null +++ b/core/src/main/java/com/dite/znpt/service/EmailService.java @@ -0,0 +1,30 @@ +package com.dite.znpt.service; + +import org.springframework.stereotype.Service; + +@Service +public interface EmailService { + /** + * 发送邮箱验证码 + * @param email + * @param code + * @return + */ + public boolean sendVerificationCode(String email, String code); + + /** + * 生成验证码 + * @param email + * @return + */ + public String generateCode(String email); + + /** + * 验证邮箱验证码 + * @param email + * @param code + * @return + */ + public boolean verifyCode(String email, String code); + +} diff --git a/core/src/main/java/com/dite/znpt/service/UserPostService.java b/core/src/main/java/com/dite/znpt/service/UserPostService.java index bc14fd1..c2a86ab 100644 --- a/core/src/main/java/com/dite/znpt/service/UserPostService.java +++ b/core/src/main/java/com/dite/znpt/service/UserPostService.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.dite.znpt.domain.entity.PostEntity; import com.dite.znpt.domain.entity.UserPostEntity; import com.dite.znpt.domain.vo.PostResp; +import com.dite.znpt.domain.vo.UserResp; import java.util.List; @@ -16,4 +17,5 @@ public interface UserPostService extends IService { List getPostsByUserId(String userId); void bindUserPost(String userId, List postIds); void bindPostUser(String postId, List userIds); + List getUsersByPostId(String postId); } diff --git a/core/src/main/java/com/dite/znpt/service/impl/EmailServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/EmailServiceImpl.java new file mode 100644 index 0000000..ea93d8d --- /dev/null +++ b/core/src/main/java/com/dite/znpt/service/impl/EmailServiceImpl.java @@ -0,0 +1,66 @@ +package com.dite.znpt.service.impl; + +import com.dite.znpt.service.EmailService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; +import org.springframework.web.server.ResponseStatusException; + +import java.util.Random; + +@Slf4j +@Service +public class EmailServiceImpl implements EmailService { + + @Autowired + private JavaMailSender mailSender; + + @Autowired + private InMemoryVerificationCodeStore codeStore; + + @Value("${email.verification.from}") + private String from; + @Value("${email.verification.subject}") + private String subject; + @Value("${email.verification.template}") + private String template; + + @Override + public boolean sendVerificationCode(String email, String code) { + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(from); + message.setSubject(subject); + message.setTo(email); + message.setText(String.format(template,code)); + try{ + mailSender.send(message); + return true; + }catch (Exception e){ + log.error("发送邮件失败:",e); + return false; + } + } + + @Override + public String generateCode(String email) { + if (!codeStore.canSend(email)) { + throw new ResponseStatusException( + HttpStatus.TOO_MANY_REQUESTS, // 429 状态码 + "验证码发送过于频繁,请稍后再试" + ); + } + + String code = String.format("%04d", new Random().nextInt(9999)); + codeStore.save(email, code); + return code; + } + + @Override + public boolean verifyCode(String email, String code) { + return codeStore.validate(email, code); + } +} diff --git a/core/src/main/java/com/dite/znpt/service/impl/InMemoryVerificationCodeStore.java b/core/src/main/java/com/dite/znpt/service/impl/InMemoryVerificationCodeStore.java new file mode 100644 index 0000000..5b909b3 --- /dev/null +++ b/core/src/main/java/com/dite/znpt/service/impl/InMemoryVerificationCodeStore.java @@ -0,0 +1,74 @@ +package com.dite.znpt.service.impl; + +import lombok.Data; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class InMemoryVerificationCodeStore { + + // 存储邮箱验证码的map + private final Map emailCodeMap = new ConcurrentHashMap<>(); + + public void save(String email, String code){ + // 设置五分钟过期 + emailCodeMap.put(email,new codeInfo(code, LocalDateTime.now().plusMinutes(5))); + } + + /** + * 验证验证码 + * @param email + * @param code + * @return + */ + public boolean validate(String email, String code){ + codeInfo codeInfo = emailCodeMap.get(email); + if(codeInfo == null){ + return false; + } + + boolean isValid = false; + if(code.equals(codeInfo.getCode()) && LocalDateTime.now().isBefore(codeInfo.getExpireTime())){ + isValid = true; + } + if(isValid){ + emailCodeMap.remove(email); + return true; + } + return false; + } + + /** + * 判断邮箱是否可发送验证码 + * @param email + * @return + */ + public boolean canSend(String email){ + codeInfo codeInfo = emailCodeMap.get(email); + if(codeInfo != null && LocalDateTime.now().isBefore(codeInfo.getExpireTime())){ + return false; + } + return true; + } + + @Data + public static class codeInfo { + // 验证码 + private String code; + + // 过期时间 + private LocalDateTime expireTime; + + // 上一次发送时间 + private LocalDateTime lastSendTime; + + public codeInfo(String code, LocalDateTime expireTime){ + this.code = code; + this.expireTime = expireTime; + this.lastSendTime = LocalDateTime.now(); + } + } +} diff --git a/core/src/main/java/com/dite/znpt/service/impl/UserPostServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/UserPostServiceImpl.java index e505d14..151e9e6 100644 --- a/core/src/main/java/com/dite/znpt/service/impl/UserPostServiceImpl.java +++ b/core/src/main/java/com/dite/znpt/service/impl/UserPostServiceImpl.java @@ -9,7 +9,9 @@ import com.dite.znpt.converts.Converts; import com.dite.znpt.domain.entity.PostEntity; import com.dite.znpt.domain.entity.UserEntity; import com.dite.znpt.domain.entity.UserPostEntity; +import com.dite.znpt.domain.entity.UserRoleEntity; import com.dite.znpt.domain.vo.PostResp; +import com.dite.znpt.domain.vo.UserResp; import com.dite.znpt.exception.ServiceException; import com.dite.znpt.mapper.UserPostMapper; import com.dite.znpt.service.PostService; @@ -88,4 +90,14 @@ public class UserPostServiceImpl extends ServiceImpl getUsersByPostId(String postId) { + List userIds = this.list(Wrappers.lambdaQuery(UserPostEntity.class).eq(UserPostEntity::getPostId, postId)).stream().map(UserPostEntity::getUserId).toList(); + if (CollUtil.isEmpty(userIds)) { + return new ArrayList<>(); + } + List users = userService.listByIds(userIds).stream().filter(user -> Constants.DEL_FLAG_0.equals(user.getDelFlag()) && Constants.STATUS_0.equals(user.getStatus())).toList(); + return Converts.INSTANCE.toUserResp(users); + } } diff --git a/pom.xml b/pom.xml index fcdc462..4716368 100644 --- a/pom.xml +++ b/pom.xml @@ -12,12 +12,46 @@ core - sip + web flowable pom + + + + org.springframework.boot + spring-boot-starter-mail + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + com.aliyun + aliyun-java-sdk-core + 4.6.3 + + + + + com.aliyun + aliyun-java-sdk-dysmsapi + 2.1.0 + + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + diff --git a/web/pom.xml b/web/pom.xml index c6d6423..4dd81fc 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -20,11 +20,11 @@ - - com.dite.znpt - sip - 1.0.0-SNAPSHOT - + + + + + com.dite.znpt core diff --git a/web/src/main/java/com/dite/znpt/web/controller/EmailController.java b/web/src/main/java/com/dite/znpt/web/controller/EmailController.java new file mode 100644 index 0000000..41dacba --- /dev/null +++ b/web/src/main/java/com/dite/znpt/web/controller/EmailController.java @@ -0,0 +1,46 @@ +package com.dite.znpt.web.controller; + +import com.dite.znpt.domain.Result; +import com.dite.znpt.domain.entity.UserEntity; +import com.dite.znpt.service.EmailService; +import com.dite.znpt.service.UserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@Api(tags = "邮件") +@RestController("/email") +public class EmailController { + @Resource + private EmailService emailService; + + @Resource + private UserService userService; + + @ApiOperation(value = "发送邮箱验证码", httpMethod = "POST") + @PostMapping("/send") + public Result send(@RequestParam("email") String email) { + boolean send = emailService.sendVerificationCode(email, emailService.generateCode(email)); + if (send) { + return Result.ok("发送成功"); + } + return Result.error("发送失败"); + } + + @ApiOperation(value = "验证邮箱验证码", httpMethod = "POST") + @PostMapping("/verify") + public Result verify(@RequestParam("userId") String userId, @RequestParam("email") String email, @RequestParam("code") String code) { + boolean verify = emailService.verifyCode(email, code); + if (verify) { + UserEntity userEntity = userService.getById(userId); + userEntity.setEmail(email); + userService.updateById(userEntity); + return Result.ok("验证成功"); + } + return Result.error("验证失败"); + } +} diff --git a/web/src/main/java/com/dite/znpt/web/controller/PostController.java b/web/src/main/java/com/dite/znpt/web/controller/PostController.java index 7662b1f..bd57fb0 100644 --- a/web/src/main/java/com/dite/znpt/web/controller/PostController.java +++ b/web/src/main/java/com/dite/znpt/web/controller/PostController.java @@ -6,6 +6,7 @@ import com.dite.znpt.domain.Result; import com.dite.znpt.domain.entity.UserPostEntity; import com.dite.znpt.domain.vo.PostReq; import com.dite.znpt.domain.vo.PostResp; +import com.dite.znpt.domain.vo.UserResp; import com.dite.znpt.service.PostService; import com.dite.znpt.service.UserPostService; import com.dite.znpt.util.ValidationGroup; @@ -72,4 +73,9 @@ public class PostController { return Result.ok(); } + @ApiOperation(value="查询属于该岗位的用户", httpMethod = "GET") + @GetMapping("/{postId}/user") + public Result> listUser(@PathVariable String postId){ + return Result.ok(userPostService.getUsersByPostId(postId)); + } } diff --git a/web/src/main/resources/application-dev.yml b/web/src/main/resources/application-dev.yml index 1d8c3aa..f9bda52 100644 --- a/web/src/main/resources/application-dev.yml +++ b/web/src/main/resources/application-dev.yml @@ -5,6 +5,21 @@ server: # 数据源配置 spring: + mail: + host: smtp.qq.com + port: 587 + # 网易邮箱配置 + # host: smtp.163.com + # port: 465 + username: 2838879363@qq.com + password: vtccznivtjrndfci + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver @@ -82,6 +97,12 @@ spring: # domain: # prefix: /minio/ +email: + verification: + from: 2838879363@qq.com + subject: "注册验证码" + template: "您的验证码为:%s,有效期为5分钟。请不要将验证码泄露给他人。" + ############## Sa-Token 配置 (文档: https://sa-token.cc) ############## sa-token: # token 名称(同时也是 cookie 名称)