diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java index 50548f9..323e0f1 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java +++ b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java @@ -36,6 +36,10 @@ public class MemberUserRespDTO { */ private String mobile; /** + * 邮箱 + */ + private String email; + /** * 创建时间(注册时间) */ private LocalDateTime createTime; diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java index b240602..01e4dd3 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java +++ b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java @@ -5,7 +5,9 @@ import lombok.Data; import org.hibernate.validator.constraints.URL; import org.springframework.format.annotation.DateTimeFormat; +import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import java.time.LocalDateTime; import java.util.List; @@ -26,6 +28,11 @@ public class MemberUserBaseVO { @NotNull(message = "状态不能为空") private Byte status; + @Schema(description = "邮箱", example = "member@iocoder.cn") + @Email(message = "邮箱格式不正确") + @Size(max = 50, message = "邮箱长度不能超过 50 个字符") + private String email; + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") @NotNull(message = "用户昵称不能为空") private String nickname; diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java index abb9428..299e6e2 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java +++ b/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 { @Schema(description = "手机号", example = "15601691300") private String mobile; + @Schema(description = "邮箱", example = "member@iocoder.cn") + private String email; + @Schema(description = "用户昵称", example = "李四") private String nickname; diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java index 72e4fa3..fd9b907 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java +++ b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java @@ -23,6 +23,9 @@ public class AppMemberUserInfoRespVO { @Schema(description = "用户手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") private String mobile; + @Schema(description = "邮箱", example = "member@iocoder.cn") + private String email; + @Schema(description = "用户性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer sex; diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java index cca08e9..4ddcdbd 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java +++ b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.member.controller.app.user.vo; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.common.SexEnum; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Size; import lombok.Data; import org.hibernate.validator.constraints.URL; @@ -17,6 +17,11 @@ public class AppMemberUserUpdateReqVO { @URL(message = "头像必须是 URL 格式") private String avatar; + @Schema(description = "邮箱", example = "member@iocoder.cn") + @Email(message = "邮箱格式不正确") + @Size(max = 50, message = "邮箱长度不能超过 50 个字符") + private String email; + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer sex; diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java index 97ddc19..11954c0 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java +++ b/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 { */ private String mobile; /** + * 邮箱 + */ + private String email; + /** * 加密后的密码 * * 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐 diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java index 3f87102..6200bf1 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java +++ b/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 { return selectOne(MemberUserDO::getMobile, mobile); } + default MemberUserDO selectByEmail(String email) { + return selectOne(MemberUserDO::getEmail, email); + } + default List selectListByNicknameLike(String nickname) { return selectList(new LambdaQueryWrapperX() .likeIfPresent(MemberUserDO::getNickname, nickname)); @@ -42,6 +46,7 @@ public interface MemberUserMapper extends BaseMapperX { // 分页查询 return selectPage(reqVO, new LambdaQueryWrapperX() .likeIfPresent(MemberUserDO::getMobile, reqVO.getMobile()) + .likeIfPresent(MemberUserDO::getEmail, reqVO.getEmail()) .betweenIfPresent(MemberUserDO::getLoginDate, reqVO.getLoginDate()) .likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname()) .betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime()) diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java index ee970a5..c28fcfc 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java +++ b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java @@ -14,6 +14,7 @@ public interface ErrorCodeConstants { ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_004_001_001, "手机号未注册用户"); ErrorCode USER_MOBILE_USED = new ErrorCode(1_004_001_002, "修改手机失败,该手机号({})已经被使用"); ErrorCode USER_POINT_NOT_ENOUGH = new ErrorCode(1_004_001_003, "用户积分余额不足"); + ErrorCode USER_EMAIL_USED = new ErrorCode(1_004_001_004, "修改邮箱失败,该邮箱({})已经被使用"); // ========== AUTH 模块 1-004-003-000 ========== ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确"); diff --git a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java index d596ab6..7cfbeb5 100644 --- a/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java +++ b/yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java @@ -142,6 +142,12 @@ public class MemberUserServiceImpl implements MemberUserService { @Override public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) { + // 1.1 检测用户是否存在 + validateUserExists(userId); + // 1.2 校验手机是否已经被绑定 + validateEmailUnique(userId, reqVO.getEmail()); + + // 2. 更新用户 MemberUserDO updateObj = BeanUtils.toBean(reqVO, MemberUserDO.class).setId(userId); memberUserMapper.updateById(updateObj); } @@ -238,6 +244,8 @@ public class MemberUserServiceImpl implements MemberUserService { validateUserExists(updateReqVO.getId()); // 校验手机唯一 validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile()); + // 校验邮箱唯一 + validateEmailUnique(updateReqVO.getId(), updateReqVO.getEmail()); // 更新 MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO); @@ -274,6 +282,24 @@ public class MemberUserServiceImpl implements MemberUserService { } } + @VisibleForTesting + void validateEmailUnique(Long id, String email) { + if (StrUtil.isBlank(email)) { + return; + } + MemberUserDO user = memberUserMapper.selectByEmail(email); + if (user == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的用户 + if (id == null) { + throw exception(USER_EMAIL_USED, email); + } + if (!user.getId().equals(id)) { + throw exception(USER_EMAIL_USED, email); + } + } + @Override public PageResult getUserPage(MemberUserPageReqVO pageReqVO) { return memberUserMapper.selectPage(pageReqVO); diff --git a/yudao-module-member/src/test/resources/sql/create_tables.sql b/yudao-module-member/src/test/resources/sql/create_tables.sql index 782a818..9775a30 100644 --- a/yudao-module-member/src/test/resources/sql/create_tables.sql +++ b/yudao-module-member/src/test/resources/sql/create_tables.sql @@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS "member_user" "avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像', "status" tinyint NOT NULL COMMENT '状态', "mobile" varchar(11) NOT NULL COMMENT '手机号', + "email" varchar(50) NULL COMMENT '邮箱', "password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码', "register_ip" varchar(32) NOT NULL COMMENT '注册 IP', "login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',