邮箱模块:完善 account 账号的增删改查功能

pull/2/head
YunaiV 2023-01-25 17:48:09 +08:00
parent c9c5a818ec
commit 0262fa5c15
28 changed files with 555 additions and 272 deletions

View File

@ -1,4 +1,34 @@
package cn.iocoder.yudao.module.system.api.mail;
import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
import javax.validation.Valid;
/**
* API
*
* @author
*/
public interface MailSendApi {
/**
* Admin
*
* mail 使 userId Admin
*
* @param reqDTO
* @return
*/
Long sendSingleSmsToAdmin(@Valid MailSendSingleToUserReqDTO reqDTO);
/**
* Member
*
* mail 使 userId Member
*
* @param reqDTO
* @return
*/
Long sendSingleSmsToMember(@Valid MailSendSingleToUserReqDTO reqDTO);
}

View File

@ -1,38 +0,0 @@
package cn.iocoder.yudao.module.system.api.mail.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
@ApiModel("管理后台 - 邮件发送 Req VO")
@Data
public class MailSendReqDTO { // TODO @wangjingqi1, 不用空格2应该只要传递 templateCode、参数就好title、from、content、附件应该都是参数里的
@ApiModelProperty(value = "用户编码",required = true)
@NotNull(message = "用户编码不能为空")
private String userId;
@ApiModelProperty(value = "用户类型",required = true)
@NotNull(message = "用户类型不能为空")
private String userType;
@ApiModelProperty(value = "邮箱模版id",example = "1024")
@NotNull(message = "邮箱模版编码不能为空")
private Integer templateCode;
@ApiModelProperty(value = "邮箱参数")
@NotNull(message = "模版参数不能为空")
private Map<String,Object> templateParams;
@ApiModelProperty(value = "收件人",required = true,example = "yudaoyuanma@123.com")
@NotNull(message = "收件人不能为空")
private List<String> tos;
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.system.api.mail.dto;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import java.util.Map;
/**
* Request DTO
*
* @author wangjingqi
*/
@Data
public class MailSendSingleToUserReqDTO {
/**
*
*/
@NotNull(message = "用户编号不能为空")
private String userId;
/**
*
*/
@Email
private String mail;
/**
*
*/
@NotNull(message = "邮件模板编号不能为空")
private String templateCode;
/**
*
*/
@NotNull(message = "邮件模板参数不能为空")
private Map<String, Object> templateParams;
}

View File

@ -141,16 +141,13 @@ public interface ErrorCodeConstants {
ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1002022000, "code 不存在");
ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1002022001, "code 已过期");
// TODO @芋艿:需要重新搞下 mail 错误码
// ========== 邮箱账号 1002020000 ==========
ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1002020000, "邮箱账号不存在");
ErrorCode MAIL_ACCOUNT_EXISTS = new ErrorCode(1002020001, "邮箱账号存在");
// ========== 邮箱账号 1002023000 ==========
ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1002023000, "邮箱账号不存在");
ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002023001, "无法删除,该邮箱账号还有邮件模板");
// ========== 邮箱模版 1002021000 ==========
ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002021000, "邮箱模版不存在");
ErrorCode MAIL_TEMPLATE_EXISTS = new ErrorCode(1002021001, "邮箱模版存在");
ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002021002, "存在关联邮箱模版");
ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1002021003, "模板参数({})缺失");
}

View File

@ -1,22 +0,0 @@
package cn.iocoder.yudao.module.system.enums.mail;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*
* @author wangjingyi
*/
@Getter
@AllArgsConstructor
public enum MailLogUserTypeEnum {
COMMON (10),
VIP (20);
/**
*
*/
private final int userType;
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.system.api.mail;
import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@ -10,5 +11,16 @@ import org.springframework.validation.annotation.Validated;
*/
@Service
@Validated
public class MailSendApiImpl implements MailSendApi{
public class MailSendApiImpl implements MailSendApi {
@Override
public Long sendSingleSmsToAdmin(MailSendSingleToUserReqDTO reqDTO) {
return null;
}
@Override
public Long sendSingleSmsToMember(MailSendSingleToUserReqDTO reqDTO) {
return null;
}
}

View File

@ -3,11 +3,7 @@ package cn.iocoder.yudao.module.system.controller.admin.mail;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountBaseVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.send.MailReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.*;
import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
@ -36,14 +32,14 @@ public class MailAccountController {
@ApiOperation("创建邮箱账号")
@PreAuthorize("@ss.hasPermission('system:mail-account:create')")
public CommonResult<Long> createMailAccount(@Valid @RequestBody MailAccountCreateReqVO createReqVO) {
return success(mailAccountService.create(createReqVO));
return success(mailAccountService.createMailAccount(createReqVO));
}
@PutMapping("/update")
@ApiOperation("修改邮箱账号")
@PreAuthorize("@ss.hasPermission('system:mail-account:update')")
public CommonResult<Boolean> updateMailAccount(@Valid @RequestBody MailAccountUpdateReqVO updateReqVO) {
mailAccountService.update(updateReqVO);
mailAccountService.updateMailAccount(updateReqVO);
return success(true);
}
@ -52,18 +48,15 @@ public class MailAccountController {
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:mail-account:delete')")
public CommonResult<Boolean> deleteMailAccount(@RequestParam Long id) {
mailAccountService.delete(id);
mailAccountService.deleteMailAccount(id);
return success(true);
}
// TODO @wangjingyigetMailAccount 和 getMailAccountPage 这两个接口,定义一个对应的 Resp 类哈,参考别的模块。主要不要返回 password 字段。
// 一个可以的做法,是 MailAccountBaseVO 不返回 password然后 MailAccountCreateReqVO、MailAccountUpdateReqVO 添加这个字段 DONE
@GetMapping("/get")
@ApiOperation("获得邮箱账号")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:mail-account:get')")
public CommonResult<MailAccountBaseVO> getMailAccount(@RequestParam("id") Long id) {
public CommonResult<MailAccountRespVO> getMailAccount(@RequestParam("id") Long id) {
MailAccountDO mailAccountDO = mailAccountService.getMailAccount(id);
return success(MailAccountConvert.INSTANCE.convert(mailAccountDO));
}
@ -76,14 +69,10 @@ public class MailAccountController {
return success(MailAccountConvert.INSTANCE.convertPage(pageResult));
}
// TODO @wangjingyigetSimpleMailAccountList 单独定义一个类只返回精简的信息idfrom 即可。像密码之类都是敏感信息,不应该返回 DONE
@GetMapping("/list-all-simple")
@ApiOperation(value = "获得邮箱账号精简列表")
public CommonResult<List<MailAccountBaseVO>> getSimpleMailAccountList() {
public CommonResult<List<MailAccountSimpleRespVO>> getSimpleMailAccountList() {
List<MailAccountDO> list = mailAccountService.getMailAccountList();
// 排序后,返回给前端
list.sort(Comparator.comparing(MailAccountDO::getId));
return success(MailAccountConvert.INSTANCE.convertList02(list));
}

View File

@ -1,33 +1,41 @@
package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 邮箱账号基类 Base VO")
/**
* Base VO VO 使
* VO Swagger
*/
@Data
public class MailAccountBaseVO {
@ApiModelProperty(value = "邮箱",required = true,example = "yudaoyuanma@123.com")
@NotNull(message = "邮箱必填")
@Email(message = "必须是Email格式")
private String fromAddress;
@ApiModelProperty(value = "邮箱", required = true, example = "yudaoyuanma@123.com")
@NotNull(message = "邮箱不能为空")
@Email(message = "必须是 Email 格式")
private String mail;
@ApiModelProperty(value = "用户名",required = true,example = "yudao")
@NotNull(message = "用户名必填")
@ApiModelProperty(value = "用户名", required = true, example = "yudao")
@NotNull(message = "用户名不能为空")
private String username;
@ApiModelProperty(value = "网站",required = true,example = "www.iocoder.cn")
@NotNull(message = "网站必填")
@ApiModelProperty(value = "密码", required = true, example = "123456")
@NotNull(message = "密码必填")
private String password;
@ApiModelProperty(value = "网站", required = true, example = "www.iocoder.cn")
@NotNull(message = "网站不能为空")
private String host;
@ApiModelProperty(value = "端口",required = true,example = "80")
@ApiModelProperty(value = "端口", required = true, example = "80")
@NotNull(message = "端口不能为空")
private Integer port;
@ApiModelProperty(value = "是否开启ssl",required = true,example = "2")
@NotNull(message = "是否开启ssl必填")
@ApiModelProperty(value = "是否开启 ssl", required = true, example = "true")
@NotNull(message = "是否开启 ssl 必填")
private Boolean sslEnable;
}

View File

@ -14,7 +14,4 @@ import javax.validation.constraints.NotNull;
@ToString(callSuper = true)
public class MailAccountCreateReqVO extends MailAccountBaseVO {
@ApiModelProperty(value = "密码",required = true,example = "123456")
@NotNull(message = "密码必填")
private String password;
}

View File

@ -13,21 +13,10 @@ import lombok.ToString;
@ToString(callSuper = true)
public class MailAccountPageReqVO extends PageParam {
@ApiModelProperty(value = "邮箱" , required = true , example = "yudaoyuanma@123.com")
private String fromAddress;
@ApiModelProperty(value = "邮箱", required = true, example = "yudaoyuanma@123.com")
private String mail;
@ApiModelProperty(value = "用户名" , required = true , example = "yudao")
private String username;
@ApiModelProperty(value = "密码" , required = true , example = "123456")
private String password;
@ApiModelProperty(value = "网站" , required = true , example = "www.iocoder.cn")
private String host;
@ApiModelProperty(value = "端口" , required = true , example = "80")
private String port;
@ApiModelProperty(value = "是否开启ssl" , required = true , example = "2")
private Boolean sslEnable;
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@ApiModel("管理后台 - 邮箱账号 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MailAccountRespVO extends MailAccountBaseVO {
@ApiModelProperty(value = "编号", required = true, example = "1024")
@NotNull(message = "编号不能为空")
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel("管理后台 - 邮箱账号的精简 Response VO")
@Data
public class MailAccountSimpleRespVO {
@ApiModelProperty(value = "邮箱比那好", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "邮箱", required = true, example = "768541388@qq.com")
private String mail;
}

View File

@ -18,7 +18,4 @@ public class MailAccountUpdateReqVO extends MailAccountBaseVO {
@NotNull(message = "编号不能为空")
private Long id;
@ApiModelProperty(value = "密码",required = true,example = "123456")
@NotNull(message = "密码必填")
private String password;
}

View File

@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.system.convert.mail;
import cn.hutool.extra.mail.MailAccount;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountBaseVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountRespVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSimpleRespVO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
import org.mapstruct.Mapper;
@ -14,30 +16,33 @@ import java.util.Map;
@Mapper
public interface MailAccountConvert {
MailAccountConvert INSTANCE = Mappers.getMapper(MailAccountConvert.class);
MailAccountDO convert (MailAccountBaseVO mailAccountBaseVO);
MailAccountDO convert(MailAccountBaseVO bean);
MailAccountBaseVO convert (MailAccountDO mailAccountDO);
MailAccountRespVO convert(MailAccountDO bean);
PageResult<MailAccountBaseVO> convertPage(PageResult<MailAccountDO> pageResult);
PageResult<MailAccountBaseVO> convertPage(PageResult<MailAccountDO> pageResult);
List<MailAccountBaseVO> convertList02(List<MailAccountDO> list);
List<MailAccountSimpleRespVO> convertList02(List<MailAccountDO> list);
default MailAccount convertAccount(MailSendMessage mailAccountDO){
// TODO 芋艿:改下
default MailAccount convertAccount(MailSendMessage bean) {
return new MailAccount()
.setHost(mailAccountDO.getHost())
.setPort(mailAccountDO.getPort())
.setHost(bean.getHost())
.setPort(bean.getPort())
.setAuth(true)
.setFrom(mailAccountDO.getFromAddress())
.setUser(mailAccountDO.getUsername())
.setPass(mailAccountDO.getPassword())
.setSslEnable(mailAccountDO.getSslEnable());
.setFrom(bean.getFromAddress())
.setUser(bean.getUsername())
.setPass(bean.getPassword())
.setSslEnable(bean.getSslEnable());
}
// TODO 芋艿:改下
default Map<String, String> convertToMap(MailAccountDO mailAccountDO , String content) {
Map<String , String> map = new HashMap<>();
map.put("from_address" , mailAccountDO.getFromAddress());
map.put("from_address" , mailAccountDO.getMail());
map.put("username" , mailAccountDO.getUsername());
map.put("content" , content);
return map;

View File

@ -1,14 +1,14 @@
package cn.iocoder.yudao.module.system.dal.dataobject.mail;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
*
* DO
*
*
*
* @author wangjingyi
* @since 2022-03-21
@ -25,7 +25,8 @@ public class MailAccountDO extends BaseDO {
/**
*
*/
private String fromAddress;
private String mail;
/**
*
*/

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.system.dal.dataobject.mail;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.system.enums.mail.MailLogUserTypeEnum;
import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@ -13,7 +13,7 @@ import java.util.Date;
import java.util.Map;
/**
*
* DO
*
*
* @author wangjingyi
@ -29,7 +29,7 @@ import java.util.Map;
public class MailLogDO extends BaseDO implements Serializable {
/**
*
*
*/
private Long id;
@ -37,62 +37,74 @@ public class MailLogDO extends BaseDO implements Serializable {
*
*/
private Long userId;
/**
*
*
* {@link MailLogUserTypeEnum#getUserType}
* {@link UserTypeEnum}
*/
private Integer userType;
/**
*
*/
private String toMail;
// TODO @wangjingyiaccountId
/**
*
*
* {@link MailAccountDO#getId()}
*/
private Long accountId;
// TODO @wangjingyi如果是冗余字段记得 @ 下DONE
/**
*
*
*
* {@link MailAccountDO}
* {@link MailAccountDO#getMail()}
*/
private String fromAddress;
private String fromMail;
// ========= 模板相关字段 =========
/**
*
*
*
* {@link MailTemplateDO#getId()}
*/
private Long templateId;
/**
*
*
* {@link MailTemplateDO#getCode()}
*/
private String templateCode;
/**
*
*
* {@link MailTemplateDO#getContent()}
*/
private String templateContent;
/**
*
*
* {@link MailTemplateDO#getParams()}
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String,Object> templateParams;
/**
*
*/
private Date sendTime;
//=========接收相关字段=========
// ========= 发送相关字段 =========
/**
*
*
* {@link MailSendStatusEnum}
*/
private Integer sendStatus;
/**
*
*/
private Date sendTime;
// ========= 接收相关字段 =========
/**
*
*/
private String sendResult;
/**
* ID
*/

View File

@ -11,7 +11,7 @@ import lombok.EqualsAndHashCode;
import java.util.List;
/**
*
* DO
*
* @author wangjingyi
* @since 2022-03-21
@ -34,9 +34,12 @@ public class MailTemplateDO extends BaseDO {
*/
private String code;
/**
*
*
*
* {@link MailAccountDO#getId()}
*/
private Long accountId;
/**
*
*/

View File

@ -2,37 +2,19 @@ package cn.iocoder.yudao.module.system.dal.mysql.mail;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.Date;
@Mapper
public interface MailAccountMapper extends BaseMapperX<MailAccountDO> {
default PageResult<MailAccountDO> selectPage(MailAccountPageReqVO pageReqVO) {
return selectPage(pageReqVO, new QueryWrapperX<MailAccountDO>()
.likeIfPresent("from_address" , pageReqVO.getFromAddress())
.likeIfPresent("host" , pageReqVO.getHost())
.likeIfPresent("username" , pageReqVO.getUsername())
.eqIfPresent("password" , pageReqVO.getPassword())
.eqIfPresent("port" , pageReqVO.getPort())
);
}
default MailAccountDO selectByUserName(String userName){
return selectOne(new QueryWrapperX<MailAccountDO>()
.eqIfPresent("username" , userName));
}
default MailAccountDO selectByUserNameAndId(String userName,Long id){
return selectOne(new QueryWrapperX<MailAccountDO>()
.eqIfPresent("username" , userName)
.neIfPresent("id" , id));
return selectPage(pageReqVO, new LambdaQueryWrapperX<MailAccountDO>()
.likeIfPresent(MailAccountDO::getMail, pageReqVO.getMail())
.likeIfPresent(MailAccountDO::getUsername , pageReqVO.getUsername()));
}
default MailAccountDO selectOneByFrom(String from){
@ -40,6 +22,4 @@ public interface MailAccountMapper extends BaseMapperX<MailAccountDO> {
.eqIfPresent("from_address" , from));
}
@Select("SELECT COUNT(*) FROM system_mail_account WHERE update_time > #{maxUpdateTime}")
Long selectCountByUpdateTimeGt(Date maxUpdateTime);
}

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@ -36,5 +37,10 @@ public interface MailTemplateMapper extends BaseMapperX<MailTemplateDO> {
default MailTemplateDO selectOneByAccountId(Long accountId){
return selectOne(new QueryWrapperX<MailTemplateDO>()
.eqIfPresent("account_id" , accountId));
};
}
default Long selectCountByAccountId(Long accountId) {
return selectCount(MailTemplateDO::getAccountId, accountId);
}
}

View File

@ -55,7 +55,7 @@ public class MailProducer {
public void sendMailSendMessage(Long sendLogId,MailAccountDO mailAccountDO, MailTemplateDO mailTemplateDO, String content,List<KeyValue<String, Object>> templateParams,String to) {
MailSendMessage message = new MailSendMessage();
message.setContent(content)
.setFromAddress(mailAccountDO.getFromAddress())
.setFromAddress(mailAccountDO.getMail())
.setHost(mailAccountDO.getHost())
.setPort(mailAccountDO.getPort())
.setPassword(mailAccountDO.getPassword())

View File

@ -28,21 +28,21 @@ public interface MailAccountService {
* @param createReqVO
* @return
*/
Long create(@Valid MailAccountCreateReqVO createReqVO);
Long createMailAccount(@Valid MailAccountCreateReqVO createReqVO);
/**
*
*
* @param updateReqVO
*/
void update(@Valid MailAccountUpdateReqVO updateReqVO);
void updateMailAccount(@Valid MailAccountUpdateReqVO updateReqVO);
/**
*
*
* @param id
*/
void delete(Long id);
void deleteMailAccount(Long id);
/**
*

View File

@ -85,4 +85,12 @@ public interface MailTemplateService {
*/
String formatMailTemplateContent(String content, Map<String, String> params);
/**
*
*
* @param accountId
* @return
*/
long countByAccountId(Long accountId);
}

View File

@ -1,32 +1,28 @@
package cn.iocoder.yudao.module.system.service.mail.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS;
/**
* Service
@ -43,7 +39,7 @@ public class MailAccountServiceImpl implements MailAccountService {
private MailAccountMapper mailAccountMapper;
@Resource
private MailTemplateMapper mailTemplateMapper;
private MailTemplateService mailTemplateService;
@Resource
private MailProducer mailProducer;
@ -56,75 +52,61 @@ public class MailAccountServiceImpl implements MailAccountService {
*/
private volatile Map<Long, MailAccountDO> mailAccountCache;
/**
*
*/
private volatile Date maxUpdateTime;
@Override
@PostConstruct
public void initLocalCache() {
List<MailAccountDO> mailAccountDOList = this.loadMailAccountIfUpdate(maxUpdateTime);
if (CollUtil.isEmpty(mailAccountDOList)) {
return;
}
// 第一步:查询数据
List<MailAccountDO> accounts = mailAccountMapper.selectList();
log.info("[initLocalCache][缓存邮箱账号,数量:{}]", accounts.size());
// 写入缓存
mailAccountCache = CollectionUtils.convertMap(mailAccountDOList, MailAccountDO::getId);
maxUpdateTime = CollectionUtils.getMaxValue(mailAccountDOList, MailAccountDO::getUpdateTime);
log.info("[initLocalCache][初始化 MailAccount 数量为 {}]", mailAccountDOList.size());
}
private List<MailAccountDO> loadMailAccountIfUpdate(Date maxUpdateTime) {
//第一步 判断是否需要更新
if(null == maxUpdateTime){ // 如果更新时间为空,说明 DB 一定有新数据
log.info("[loadMailAccountIfUpdate][首次加载全量账号信息]");
}else{ // 判断数据库中是否有更新的账号信息
if (mailAccountMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
return null;
}
log.info("[loadMailAccountIfUpdate][增量加载全量账号信息]");
}
return mailAccountMapper.selectList();
// 第二步:构建缓存
mailAccountCache = convertMap(accounts, MailAccountDO::getId);
}
@Override
public Long create(MailAccountCreateReqVO createReqVO) {
// username 要校验唯一
this.validateMailAccountOnlyByUserName(createReqVO.getUsername());
MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(createReqVO);
mailAccountMapper.insert(mailAccountDO);
public Long createMailAccount(MailAccountCreateReqVO createReqVO) {
// 插入
MailAccountDO account = MailAccountConvert.INSTANCE.convert(createReqVO);
mailAccountMapper.insert(account);
// 更新
// 发送刷新消息
mailProducer.sendMailAccountRefreshMessage();
return mailAccountDO.getId();
return account.getId();
}
@Override
public void update(MailAccountUpdateReqVO updateReqVO) {
// username 要校验唯一 TODO @wangjingyi校验唯一的时候需要排除掉自己 DONE
this.validateMailAccountOnlyByUserNameAndId(updateReqVO.getUsername(),updateReqVO.getId());
MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(updateReqVO);
public void updateMailAccount(MailAccountUpdateReqVO updateReqVO) {
// 校验是否存在
validateMailAccountExists(mailAccountDO.getId());
validateMailAccountExists(updateReqVO.getId());
// 更新
MailAccountDO updateObj = MailAccountConvert.INSTANCE.convert(updateReqVO);
mailAccountMapper.updateById(updateObj);
// 发送刷新消息
mailProducer.sendMailAccountRefreshMessage();
mailAccountMapper.updateById(mailAccountDO);
}
@Override
public void delete(Long id) {
public void deleteMailAccount(Long id) {
// 校验是否存在账号
validateMailAccountExists(id);
// 校验是否存在关联模版
validateMailTemplateByAccountId(id);
if (mailTemplateService.countByAccountId(id) > 0) {
throw exception(MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS);
}
// 删除
mailAccountMapper.deleteById(id);
// 更新
// 发送刷新消息
mailProducer.sendMailAccountRefreshMessage();
}
private void validateMailAccountExists(Long id) {
if (mailAccountMapper.selectById(id) == null) {
throw exception(MAIL_ACCOUNT_NOT_EXISTS);
}
}
@Override
public MailAccountDO getMailAccount(Long id) {
return mailAccountMapper.selectById(id);
@ -140,33 +122,4 @@ public class MailAccountServiceImpl implements MailAccountService {
return mailAccountMapper.selectList();
}
private void validateMailAccountExists(Long id) {
if (mailAccountMapper.selectById(id) == null) {
throw exception(MAIL_ACCOUNT_NOT_EXISTS);
}
}
private void validateMailAccountOnlyByUserName(String userName){
mailAccountCache.forEach((key,value)->{
if(value.getUsername().equals(userName)){
throw exception(MAIL_ACCOUNT_EXISTS);
}
});
}
private void validateMailAccountOnlyByUserNameAndId(String userName,Long id){
mailAccountCache.forEach((key , value)->{
if (value.getUsername().equals(userName)){
if (!key.equals(id)){
throw exception(MAIL_ACCOUNT_EXISTS);
}
}
});
}
private void validateMailTemplateByAccountId(Long accountId){
MailTemplateDO mailTemplateDO = mailTemplateMapper.selectOneByAccountId(accountId);
if (mailTemplateDO != null) {
// TODO wangjingyiMAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS DONE
throw exception(MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS);
}
}
}

View File

@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
import cn.iocoder.yudao.module.system.service.mail.MailLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@ -51,7 +50,7 @@ public class MailLogServiceImpl implements MailLogService {
logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus()
: MailSendStatusEnum.IGNORE.getStatus())
// 设置邮箱相关字段
.fromAddress(mailAccountDO.getFromAddress())
.fromMail(mailAccountDO.getMail())
.accountId(mailAccountDO.getId())
// TODO @wangjingyiuserId、userType
//用户信息
@ -60,7 +59,7 @@ public class MailLogServiceImpl implements MailLogService {
.templateId(template.getId()).templateParams(templateParams).templateContent(templateContent);
logDOBuilder.fromAddress(mailAccountDO.getFromAddress());
logDOBuilder.fromMail(mailAccountDO.getMail());
logDOBuilder.accountId(mailAccountDO.getId());
// TODO @wangjingyi每个接收人一条日志。发送多个人就调用多次业务方。因为某个邮箱有问题会导致所有都发送失败。 DONE
// 设置模板相关字段

View File

@ -53,19 +53,19 @@ public class MailTemplateServiceImpl implements MailTemplateService {
*/
private volatile Map<Long, MailTemplateDO> mailTemplateCache;
private volatile Date maxUpdateTime;
@Override
@PostConstruct
public void initLocalCache() {
List<MailTemplateDO> mailTemplateDOList = this.loadMailTemplateIfUpdate(maxUpdateTime);
if (true) {
return;
}
List<MailTemplateDO> mailTemplateDOList = this.loadMailTemplateIfUpdate(null);
if (CollUtil.isEmpty(mailTemplateDOList)) {
return;
}
// 写入缓存
mailTemplateCache = CollectionUtils.convertMap(mailTemplateDOList, MailTemplateDO::getId);
maxUpdateTime = CollectionUtils.getMaxValue(mailTemplateDOList, MailTemplateDO::getUpdateTime);
log.info("[initLocalCache][初始化 mailTemplate 数量为 {}]", mailTemplateDOList.size());
}
@ -156,4 +156,10 @@ public class MailTemplateServiceImpl implements MailTemplateService {
// 第二步,如果有更新,则从数据库加载所有邮件模板
return mailTemplateMapper.selectList();
}
@Override
public long countByAccountId(Long accountId) {
return mailTemplateMapper.selectCountByAccountId(accountId);
}
}

View File

@ -81,7 +81,7 @@ public class SmsChannelServiceImpl implements SmsChannelService {
public void deleteSmsChannel(Long id) {
// 校验存在
this.validateSmsChannelExists(id);
// 校验是否有字典数据
// 校验是否有在使用该账号的模版
if (smsTemplateService.countByChannelId(id) > 0) {
throw exception(SMS_CHANNEL_HAS_CHILDREN);
}

View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 创建邮箱账号
export function createMailAccount(data) {
return request({
url: '/system/mail-account/create',
method: 'post',
data: data
})
}
// 更新邮箱账号
export function updateMailAccount(data) {
return request({
url: '/system/mail-account/update',
method: 'put',
data: data
})
}
// 删除邮箱账号
export function deleteMailAccount(id) {
return request({
url: '/system/mail-account/delete?id=' + id,
method: 'delete'
})
}
// 获得邮箱账号
export function getMailAccount(id) {
return request({
url: '/system/mail-account/get?id=' + id,
method: 'get'
})
}
// 获得邮箱账号分页
export function getMailAccountPage(query) {
return request({
url: '/system/mail-account/page',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,226 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="邮箱" prop="mail">
<el-input v-model="queryParams.mail" placeholder="请输入邮箱" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input v-model="queryParams.username" placeholder="请输入用户名" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['system:mail-account:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="主键" align="center" prop="id" />
<el-table-column label="邮箱" align="center" prop="mail" />
<el-table-column label="用户名" align="center" prop="username" />
<el-table-column label="主机" align="center" prop="host" />
<el-table-column label="端口" align="center" prop="port" />
<el-table-column label="是否开启 SSL" align="center" prop="sslEnable">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.sslEnable" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['system:mail-account:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['system:mail-account:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item label="邮箱" prop="mail">
<el-input v-model="form.mail" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" placeholder="请输入密码" />
</el-form-item>
<el-form-item label="主机" prop="host">
<el-input v-model="form.host" placeholder="请输入主机" />
</el-form-item>
<el-form-item label="端口" prop="port">
<el-input v-model="form.port" placeholder="请输入端口" />
</el-form-item>
<el-form-item label="是否开启 SSL" prop="sslEnable">
<el-radio-group v-model="form.sslEnable">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.value === 'true'">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createMailAccount, updateMailAccount, deleteMailAccount, getMailAccount, getMailAccountPage } from "@/api/system/mail/account";
export default {
name: "MailAccount",
components: {
},
data() {
return {
//
loading: true,
//
exportLoading: false,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10,
mail: null,
username: null
},
//
form: {},
//
rules: {
mail: [{ required: true, message: "邮箱不能为空", trigger: "blur" }],
username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
password: [{ required: true, message: "密码不能为空", trigger: "blur" }],
host: [{ required: true, message: "主机不能为空", trigger: "blur" }],
port: [{ required: true, message: "端口不能为空", trigger: "blur" }],
sslEnable: [{ required: true, message: "是否开启 SSL不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
getMailAccountPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
mail: undefined,
username: undefined,
password: undefined,
host: undefined,
port: undefined,
sslEnable: true,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加邮箱账号";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getMailAccount(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改邮箱账号";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
//
if (this.form.id != null) {
updateMailAccount(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
//
createMailAccount(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除邮箱账号编号为"' + id + '"的数据项?').then(function() {
return deleteMailAccount(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
}
}
};
</script>