Commit e44011754c583ad7d6b397a53382e6b511f26a7c

Authored by YunaiV
1 parent fe4a774c

feat(member): 会员增加 email 字段

yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java
@@ -36,6 +36,10 @@ public class MemberUserRespDTO { @@ -36,6 +36,10 @@ public class MemberUserRespDTO {
36 */ 36 */
37 private String mobile; 37 private String mobile;
38 /** 38 /**
  39 + * 邮箱
  40 + */
  41 + private String email;
  42 + /**
39 * 创建时间(注册时间) 43 * 创建时间(注册时间)
40 */ 44 */
41 private LocalDateTime createTime; 45 private LocalDateTime createTime;
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java
@@ -5,7 +5,9 @@ import lombok.Data; @@ -5,7 +5,9 @@ import lombok.Data;
5 import org.hibernate.validator.constraints.URL; 5 import org.hibernate.validator.constraints.URL;
6 import org.springframework.format.annotation.DateTimeFormat; 6 import org.springframework.format.annotation.DateTimeFormat;
7 7
  8 +import jakarta.validation.constraints.Email;
8 import jakarta.validation.constraints.NotNull; 9 import jakarta.validation.constraints.NotNull;
  10 +import jakarta.validation.constraints.Size;
9 import java.time.LocalDateTime; 11 import java.time.LocalDateTime;
10 import java.util.List; 12 import java.util.List;
11 13
@@ -26,6 +28,11 @@ public class MemberUserBaseVO { @@ -26,6 +28,11 @@ public class MemberUserBaseVO {
26 @NotNull(message = "状态不能为空") 28 @NotNull(message = "状态不能为空")
27 private Byte status; 29 private Byte status;
28 30
  31 + @Schema(description = "邮箱", example = "member@iocoder.cn")
  32 + @Email(message = "邮箱格式不正确")
  33 + @Size(max = 50, message = "邮箱长度不能超过 50 个字符")
  34 + private String email;
  35 +
29 @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") 36 @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
30 @NotNull(message = "用户昵称不能为空") 37 @NotNull(message = "用户昵称不能为空")
31 private String nickname; 38 private String nickname;
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java
@@ -21,6 +21,9 @@ public class MemberUserPageReqVO extends PageParam { @@ -21,6 +21,9 @@ public class MemberUserPageReqVO extends PageParam {
21 @Schema(description = "手机号", example = "15601691300") 21 @Schema(description = "手机号", example = "15601691300")
22 private String mobile; 22 private String mobile;
23 23
  24 + @Schema(description = "邮箱", example = "member@iocoder.cn")
  25 + private String email;
  26 +
24 @Schema(description = "用户昵称", example = "李四") 27 @Schema(description = "用户昵称", example = "李四")
25 private String nickname; 28 private String nickname;
26 29
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java
@@ -23,6 +23,9 @@ public class AppMemberUserInfoRespVO { @@ -23,6 +23,9 @@ public class AppMemberUserInfoRespVO {
23 @Schema(description = "用户手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") 23 @Schema(description = "用户手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
24 private String mobile; 24 private String mobile;
25 25
  26 + @Schema(description = "邮箱", example = "member@iocoder.cn")
  27 + private String email;
  28 +
26 @Schema(description = "用户性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 29 @Schema(description = "用户性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
27 private Integer sex; 30 private Integer sex;
28 31
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java
1 package cn.iocoder.yudao.module.member.controller.app.user.vo; 1 package cn.iocoder.yudao.module.member.controller.app.user.vo;
2 2
3 -import cn.iocoder.yudao.framework.common.validation.InEnum;  
4 -import cn.iocoder.yudao.module.system.enums.common.SexEnum;  
5 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.Email;
  5 +import jakarta.validation.constraints.Size;
6 import lombok.Data; 6 import lombok.Data;
7 import org.hibernate.validator.constraints.URL; 7 import org.hibernate.validator.constraints.URL;
8 8
@@ -17,6 +17,11 @@ public class AppMemberUserUpdateReqVO { @@ -17,6 +17,11 @@ public class AppMemberUserUpdateReqVO {
17 @URL(message = "头像必须是 URL 格式") 17 @URL(message = "头像必须是 URL 格式")
18 private String avatar; 18 private String avatar;
19 19
  20 + @Schema(description = "邮箱", example = "member@iocoder.cn")
  21 + @Email(message = "邮箱格式不正确")
  22 + @Size(max = 50, message = "邮箱长度不能超过 50 个字符")
  23 + private String email;
  24 +
20 @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 25 @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
21 private Integer sex; 26 private Integer sex;
22 27
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java
@@ -46,6 +46,10 @@ public class MemberUserDO extends TenantBaseDO { @@ -46,6 +46,10 @@ public class MemberUserDO extends TenantBaseDO {
46 */ 46 */
47 private String mobile; 47 private String mobile;
48 /** 48 /**
  49 + * 邮箱
  50 + */
  51 + private String email;
  52 + /**
49 * 加密后的密码 53 * 加密后的密码
50 * 54 *
51 * 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐 55 * 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java
@@ -26,6 +26,10 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> { @@ -26,6 +26,10 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
26 return selectOne(MemberUserDO::getMobile, mobile); 26 return selectOne(MemberUserDO::getMobile, mobile);
27 } 27 }
28 28
  29 + default MemberUserDO selectByEmail(String email) {
  30 + return selectOne(MemberUserDO::getEmail, email);
  31 + }
  32 +
29 default List<MemberUserDO> selectListByNicknameLike(String nickname) { 33 default List<MemberUserDO> selectListByNicknameLike(String nickname) {
30 return selectList(new LambdaQueryWrapperX<MemberUserDO>() 34 return selectList(new LambdaQueryWrapperX<MemberUserDO>()
31 .likeIfPresent(MemberUserDO::getNickname, nickname)); 35 .likeIfPresent(MemberUserDO::getNickname, nickname));
@@ -42,6 +46,7 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; { @@ -42,6 +46,7 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; {
42 // 分页查询 46 // 分页查询
43 return selectPage(reqVO, new LambdaQueryWrapperX<MemberUserDO>() 47 return selectPage(reqVO, new LambdaQueryWrapperX<MemberUserDO>()
44 .likeIfPresent(MemberUserDO::getMobile, reqVO.getMobile()) 48 .likeIfPresent(MemberUserDO::getMobile, reqVO.getMobile())
  49 + .likeIfPresent(MemberUserDO::getEmail, reqVO.getEmail())
45 .betweenIfPresent(MemberUserDO::getLoginDate, reqVO.getLoginDate()) 50 .betweenIfPresent(MemberUserDO::getLoginDate, reqVO.getLoginDate())
46 .likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname()) 51 .likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname())
47 .betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime()) 52 .betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime())
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java
@@ -14,6 +14,7 @@ public interface ErrorCodeConstants { @@ -14,6 +14,7 @@ public interface ErrorCodeConstants {
14 ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_004_001_001, "手机号未注册用户"); 14 ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_004_001_001, "手机号未注册用户");
15 ErrorCode USER_MOBILE_USED = new ErrorCode(1_004_001_002, "修改手机失败,该手机号({})已经被使用"); 15 ErrorCode USER_MOBILE_USED = new ErrorCode(1_004_001_002, "修改手机失败,该手机号({})已经被使用");
16 ErrorCode USER_POINT_NOT_ENOUGH = new ErrorCode(1_004_001_003, "用户积分余额不足"); 16 ErrorCode USER_POINT_NOT_ENOUGH = new ErrorCode(1_004_001_003, "用户积分余额不足");
  17 + ErrorCode USER_EMAIL_USED = new ErrorCode(1_004_001_004, "修改邮箱失败,该邮箱({})已经被使用");
17 18
18 // ========== AUTH 模块 1-004-003-000 ========== 19 // ========== AUTH 模块 1-004-003-000 ==========
19 ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确"); 20 ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确");
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
@@ -142,6 +142,12 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -142,6 +142,12 @@ public class MemberUserServiceImpl implements MemberUserService {
142 142
143 @Override 143 @Override
144 public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) { 144 public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) {
  145 + // 1.1 检测用户是否存在
  146 + validateUserExists(userId);
  147 + // 1.2 校验手机是否已经被绑定
  148 + validateEmailUnique(userId, reqVO.getEmail());
  149 +
  150 + // 2. 更新用户
145 MemberUserDO updateObj = BeanUtils.toBean(reqVO, MemberUserDO.class).setId(userId); 151 MemberUserDO updateObj = BeanUtils.toBean(reqVO, MemberUserDO.class).setId(userId);
146 memberUserMapper.updateById(updateObj); 152 memberUserMapper.updateById(updateObj);
147 } 153 }
@@ -238,6 +244,8 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -238,6 +244,8 @@ public class MemberUserServiceImpl implements MemberUserService {
238 validateUserExists(updateReqVO.getId()); 244 validateUserExists(updateReqVO.getId());
239 // 校验手机唯一 245 // 校验手机唯一
240 validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile()); 246 validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());
  247 + // 校验邮箱唯一
  248 + validateEmailUnique(updateReqVO.getId(), updateReqVO.getEmail());
241 249
242 // 更新 250 // 更新
243 MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO); 251 MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO);
@@ -274,6 +282,24 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -274,6 +282,24 @@ public class MemberUserServiceImpl implements MemberUserService {
274 } 282 }
275 } 283 }
276 284
  285 + @VisibleForTesting
  286 + void validateEmailUnique(Long id, String email) {
  287 + if (StrUtil.isBlank(email)) {
  288 + return;
  289 + }
  290 + MemberUserDO user = memberUserMapper.selectByEmail(email);
  291 + if (user == null) {
  292 + return;
  293 + }
  294 + // 如果 id 为空,说明不用比较是否为相同 id 的用户
  295 + if (id == null) {
  296 + throw exception(USER_EMAIL_USED, email);
  297 + }
  298 + if (!user.getId().equals(id)) {
  299 + throw exception(USER_EMAIL_USED, email);
  300 + }
  301 + }
  302 +
277 @Override 303 @Override
278 public PageResult<MemberUserDO> getUserPage(MemberUserPageReqVO pageReqVO) { 304 public PageResult<MemberUserDO> getUserPage(MemberUserPageReqVO pageReqVO) {
279 return memberUserMapper.selectPage(pageReqVO); 305 return memberUserMapper.selectPage(pageReqVO);
yudao-module-member/src/test/resources/sql/create_tables.sql
@@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS &quot;member_user&quot; @@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS &quot;member_user&quot;
11 "avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像', 11 "avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
12 "status" tinyint NOT NULL COMMENT '状态', 12 "status" tinyint NOT NULL COMMENT '状态',
13 "mobile" varchar(11) NOT NULL COMMENT '手机号', 13 "mobile" varchar(11) NOT NULL COMMENT '手机号',
  14 + "email" varchar(50) NULL COMMENT '邮箱',
14 "password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码', 15 "password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
15 "register_ip" varchar(32) NOT NULL COMMENT '注册 IP', 16 "register_ip" varchar(32) NOT NULL COMMENT '注册 IP',
16 "login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP', 17 "login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',