Commit 0d81a6b5 by 段启岩

邮箱,手机号绑定完成

parent 3be4b2ff
...@@ -132,13 +132,13 @@ POST /api/user/register/email ...@@ -132,13 +132,13 @@ POST /api/user/register/email
POST /api/user/active/{activeCode} POST /api/user/active/{activeCode}
``` ```
###### 23.绑定手机号 ###### :white_check_mark: 23.绑定手机号
``` ```
POST /api/user/binding/mobile POST /api/user/binding/mobile
``` ```
###### 24.绑定邮箱 ###### :white_check_mark: 24.绑定邮箱
``` ```
POST /api/user/binding/email POST /api/user/binding/email
...@@ -657,3 +657,13 @@ GET /api/content/{contentId} ...@@ -657,3 +657,13 @@ GET /api/content/{contentId}
``` ```
GET /api/channel/{channelId}/contents GET /api/channel/{channelId}/contents
``` ```
### 十二、邮箱
###### :white_check_mark: 1.发送验证码
```
GET /api/mail/verifyCode
```
...@@ -6,7 +6,9 @@ package cn.meteor.beyondclouds.core.redis; ...@@ -6,7 +6,9 @@ package cn.meteor.beyondclouds.core.redis;
*/ */
public final class RedisKey { public final class RedisKey {
private static final String KEY_PREFIX_MOBILE_VERIFY_CODE = "vcode:"; private static final String KEY_PREFIX_MOBILE_VERIFY_CODE = "verify_code_mobile:";
private static final String KEY_PREFIX_EMAIL_VERIFY_CODE = "verify_code_email:";
private static final String KEY_PREFIX_EMAIL_ACTIVE_CODE = "email_active_code:"; private static final String KEY_PREFIX_EMAIL_ACTIVE_CODE = "email_active_code:";
...@@ -19,6 +21,20 @@ public final class RedisKey { ...@@ -19,6 +21,20 @@ public final class RedisKey {
return KEY_PREFIX_MOBILE_VERIFY_CODE + mobile; return KEY_PREFIX_MOBILE_VERIFY_CODE + mobile;
} }
/**
* 邮箱验证码
* @param email
* @return
*/
public static String EMAIL_VERIFY_CODE(String email) {
return KEY_PREFIX_MOBILE_VERIFY_CODE + email;
}
/**
* 邮箱激活码
* @param activeCode
* @return
*/
public static String EMAIL_ACTIVE_CODE(String activeCode) { public static String EMAIL_ACTIVE_CODE(String activeCode) {
return KEY_PREFIX_EMAIL_ACTIVE_CODE + activeCode; return KEY_PREFIX_EMAIL_ACTIVE_CODE + activeCode;
} }
......
package cn.meteor.beyondclouds.modules.mail.api;
import cn.meteor.beyondclouds.common.enums.ErrorCode;
import cn.meteor.beyondclouds.core.annotation.Anonymous;
import cn.meteor.beyondclouds.core.api.Response;
import cn.meteor.beyondclouds.modules.mail.service.IMailService;
import cn.meteor.beyondclouds.util.VerifyCodeUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author meteor
*/
@RestController
@RequestMapping("/api/mail")
@Api(tags = "邮箱API")
public class MailApi {
private IMailService mailService;
@Autowired
public void setMailService(IMailService mailService) {
this.mailService = mailService;
}
@Anonymous
@ApiOperation("发送验证码")
@GetMapping("/verifyCode")
public Response sendVerify(@RequestParam("email") String email) {
try {
mailService.sendVerifyCode(email, VerifyCodeUtils.randomVerifyCode());
return Response.success();
} catch (Exception e) {
e.printStackTrace();
return Response.error(ErrorCode.OPERATION_FAILED);
}
}
}
...@@ -20,4 +20,10 @@ public interface IMailService { ...@@ -20,4 +20,10 @@ public interface IMailService {
*/ */
void sendHtmlMail(EmailDTO email); void sendHtmlMail(EmailDTO email);
/**
* 发送验证码
* @param email
* @param randomVerifyCode
*/
void sendVerifyCode(String email, String randomVerifyCode);
} }
package cn.meteor.beyondclouds.modules.mail.service.impl; package cn.meteor.beyondclouds.modules.mail.service.impl;
import cn.meteor.beyondclouds.common.helper.IRedisHelper;
import cn.meteor.beyondclouds.core.redis.RedisKey;
import cn.meteor.beyondclouds.modules.mail.dto.EmailDTO; import cn.meteor.beyondclouds.modules.mail.dto.EmailDTO;
import cn.meteor.beyondclouds.modules.mail.service.IMailService; import cn.meteor.beyondclouds.modules.mail.service.IMailService;
import cn.meteor.beyondclouds.modules.mail.util.EmailUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.SimpleMailMessage;
...@@ -19,10 +22,12 @@ import javax.mail.internet.MimeMessage; ...@@ -19,10 +22,12 @@ import javax.mail.internet.MimeMessage;
public class MailServiceImpl implements IMailService { public class MailServiceImpl implements IMailService {
private JavaMailSender mailSender; private JavaMailSender mailSender;
private IRedisHelper redisHelper;
@Autowired @Autowired
public MailServiceImpl(JavaMailSender mailSender) { public MailServiceImpl(JavaMailSender mailSender, IRedisHelper redisHelper) {
this.mailSender = mailSender; this.mailSender = mailSender;
this.redisHelper = redisHelper;
} }
@Override @Override
...@@ -56,4 +61,15 @@ public class MailServiceImpl implements IMailService { ...@@ -56,4 +61,15 @@ public class MailServiceImpl implements IMailService {
log.error("发送邮件异常!", e.getMessage()); log.error("发送邮件异常!", e.getMessage());
} }
} }
@Override
public void sendVerifyCode(String email, String randomVerifyCode) {
//1. 发送验证码
EmailDTO emailDTO =
new EmailDTO("13546386889@163.com", email,
"云里云外开源社区-验证码", EmailUtils.generateVerifyCodeMail(randomVerifyCode));
sendHtmlMail(emailDTO);
//2.存储验证码到redis
redisHelper.set(RedisKey.EMAIL_VERIFY_CODE(email), randomVerifyCode, 5 * 60);
}
} }
package cn.meteor.beyondclouds.modules.user.util; package cn.meteor.beyondclouds.modules.mail.util;
/** /**
* @author meteor * @author meteor
*/ */
public class ActivceCodeEmailUtils { public class EmailUtils {
/** /**
* 生成激活邮件 * 生成激活邮件
* @param activeUrl * @param activeUrl
* @return * @return
*/ */
public static String generateMail(String activeUrl) { public static String generateActiveMail(String activeUrl) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("<html>"); builder.append("<html>");
builder.append("<head>"); builder.append("<head>");
...@@ -31,7 +31,26 @@ public class ActivceCodeEmailUtils { ...@@ -31,7 +31,26 @@ public class ActivceCodeEmailUtils {
return builder.toString(); return builder.toString();
} }
public static void main(String[] args) { /**
System.out.println(generateMail("http://a.com/aaa")); * 生成验证码邮件
* @param verifyCode
* @return
*/
public static String generateVerifyCodeMail(String verifyCode) {
StringBuilder builder = new StringBuilder();
builder.append("<html>");
builder.append("<head>");
builder.append("<title>");
builder.append("云里云外开源社区激活邮件");
builder.append("</title>");
builder.append("<meta charset=\"UTF-8\">");
builder.append("</head>");
builder.append("<body>");
builder.append("云里云外开源社区,您的验证码为<strong>");
builder.append(verifyCode);
builder.append("</strong>");
builder.append("</body>");
builder.append("</html>");
return builder.toString();
} }
} }
...@@ -10,9 +10,7 @@ import cn.meteor.beyondclouds.modules.user.entity.User; ...@@ -10,9 +10,7 @@ import cn.meteor.beyondclouds.modules.user.entity.User;
import cn.meteor.beyondclouds.modules.user.entity.UserBlacklist; import cn.meteor.beyondclouds.modules.user.entity.UserBlacklist;
import cn.meteor.beyondclouds.modules.user.entity.UserFollow; import cn.meteor.beyondclouds.modules.user.entity.UserFollow;
import cn.meteor.beyondclouds.modules.user.exception.UserServiceException; import cn.meteor.beyondclouds.modules.user.exception.UserServiceException;
import cn.meteor.beyondclouds.modules.user.form.EmailRegisterFrom; import cn.meteor.beyondclouds.modules.user.form.*;
import cn.meteor.beyondclouds.modules.user.form.UserBaseInfoFrom;
import cn.meteor.beyondclouds.modules.user.form.MobileRegisterFrom;
import cn.meteor.beyondclouds.modules.user.service.IUserBlacklistService; import cn.meteor.beyondclouds.modules.user.service.IUserBlacklistService;
import cn.meteor.beyondclouds.modules.user.service.IUserFollowService; import cn.meteor.beyondclouds.modules.user.service.IUserFollowService;
import cn.meteor.beyondclouds.modules.user.service.IUserService; import cn.meteor.beyondclouds.modules.user.service.IUserService;
...@@ -85,6 +83,39 @@ public class UserApi { ...@@ -85,6 +83,39 @@ public class UserApi {
} }
} }
@ApiOperation("绑定邮箱")
@PostMapping("/user/binding/email")
public Response bindEmail(@Valid EmailBindingFrom emailBindingFrom, BindingResult result, @CurrentSubject Subject subject) {
if (result.hasErrors()) {
return Response.fieldError(result.getFieldError());
}
try {
userService.bindEMail(emailBindingFrom.getEmail(), emailBindingFrom.getVerifyCode(), (String) subject.getId());
return Response.success();
} catch (UserServiceException e) {
e.printStackTrace();
return Response.error(e);
}
}
@ApiOperation("绑定手机号")
@PostMapping("/user/binding/mobile")
public Response bindMobile(@Valid MobileBindingFrom mobileBindingFrom, BindingResult result, @CurrentSubject Subject subject) {
if (result.hasErrors()) {
return Response.fieldError(result.getFieldError());
}
try {
userService.bindMobile(mobileBindingFrom.getMobile(), mobileBindingFrom.getVerifyCode(), (String) subject.getId());
return Response.success();
} catch (UserServiceException e) {
e.printStackTrace();
return Response.error(e);
}
}
@Anonymous @Anonymous
@ApiOperation("激活账号") @ApiOperation("激活账号")
@GetMapping("/user/active/{activeCode}") @GetMapping("/user/active/{activeCode}")
......
...@@ -11,8 +11,8 @@ public enum UserErrorCode implements IErrorCode { ...@@ -11,8 +11,8 @@ public enum UserErrorCode implements IErrorCode {
/** /**
* 手机号已经被注册// * 手机号已经被注册//
*/ */
MOBILE_REGISTERED(1001, "该手机号已被注册"), MOBILE_REGISTERED(1001, "该手机号已被占用"),
EMAIL_REGISTERED(1002, "该邮箱已被注册"), EMAIL_REGISTERED(1002, "该邮箱已被占用"),
REG_VERIFY_CODE_ERROR(1003, "验证码错误"), REG_VERIFY_CODE_ERROR(1003, "验证码错误"),
CAN_NOT_FOLLOW_SELF(1004, "不能关注自己"), CAN_NOT_FOLLOW_SELF(1004, "不能关注自己"),
FOLLOWED_USER_NOT_EXISTS(1005, "被关注用户不存在"), FOLLOWED_USER_NOT_EXISTS(1005, "被关注用户不存在"),
...@@ -23,7 +23,10 @@ public enum UserErrorCode implements IErrorCode { ...@@ -23,7 +23,10 @@ public enum UserErrorCode implements IErrorCode {
YOU_ALREADY_BLACKED(10010,"你已经被对方拉进黑名单"), YOU_ALREADY_BLACKED(10010,"你已经被对方拉进黑名单"),
CAN_NOT_BLACK_SELF(10011,"不能拉黑自己"), CAN_NOT_BLACK_SELF(10011,"不能拉黑自己"),
USER_NOT_BLACKED(10012,"用户没有被拉黑"), USER_NOT_BLACKED(10012,"用户没有被拉黑"),
INVALID_ACTIVE_CODE(10013, "非法的激活码"); INVALID_ACTIVE_CODE(10013, "非法的激活码"),
BINDING_EMAIL_VERIFY_CODE_ERROR(10014, "邮箱验证码错误"),
BINDING_MOBILE_VERIFY_CODE_ERROR(10015, "手机验证码错误"),
NON_LOCAL_AUTH_INFO(10016, "该手机未注册");
UserErrorCode(long code, String msg) { UserErrorCode(long code, String msg) {
this.code = code; this.code = code;
......
package cn.meteor.beyondclouds.modules.user.form;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* @author meteor
*/
@Data
public class EmailBindingFrom {
@Email(message = "邮箱格式不准确")
@NotNull(message = "邮箱不能为空")
private String email;
@NotEmpty(message = "验证码")
private String verifyCode;
}
package cn.meteor.beyondclouds.modules.user.form;
import cn.meteor.beyondclouds.core.constant.RegexPatterns;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* @author meteor
*/
@Data
public class MobileBindingFrom {
@NotEmpty(message = "手机不能为空")
@NotNull(message = "手机号不能为空")
@Pattern(regexp = RegexPatterns.MOBILE, message = "手机号格式不正确")
private String mobile;
@NotEmpty(message = "验证码不能为空")
private String verifyCode;
}
...@@ -61,4 +61,20 @@ public interface IUserService extends IService<User> { ...@@ -61,4 +61,20 @@ public interface IUserService extends IService<User> {
* @param activeCode * @param activeCode
*/ */
void activeAccount(String activeCode) throws UserServiceException; void activeAccount(String activeCode) throws UserServiceException;
/**
* 绑定邮箱
* @param email
* @param verifyCode
* @param userId
*/
void bindEMail(String email, String verifyCode, String userId) throws UserServiceException;
/**
* 绑定手机号
* @param mobile
* @param verifyCode
* @param userId
*/
void bindMobile(String mobile, String verifyCode, String userId) throws UserServiceException;
} }
...@@ -17,7 +17,7 @@ import cn.meteor.beyondclouds.modules.user.mapper.IUserMapper; ...@@ -17,7 +17,7 @@ import cn.meteor.beyondclouds.modules.user.mapper.IUserMapper;
import cn.meteor.beyondclouds.modules.user.service.IUserAuthAppService; import cn.meteor.beyondclouds.modules.user.service.IUserAuthAppService;
import cn.meteor.beyondclouds.modules.user.service.IUserAuthLocalService; import cn.meteor.beyondclouds.modules.user.service.IUserAuthLocalService;
import cn.meteor.beyondclouds.modules.user.service.IUserService; import cn.meteor.beyondclouds.modules.user.service.IUserService;
import cn.meteor.beyondclouds.modules.user.util.ActivceCodeEmailUtils; import cn.meteor.beyondclouds.modules.mail.util.EmailUtils;
import cn.meteor.beyondclouds.util.Md5Utils; import cn.meteor.beyondclouds.util.Md5Utils;
import cn.meteor.beyondclouds.util.UUIDUtils; import cn.meteor.beyondclouds.util.UUIDUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
...@@ -27,8 +27,11 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -27,8 +27,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.List;
/** /**
* @author meteor * @author meteor
*/ */
...@@ -138,7 +141,7 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, User> implements I ...@@ -138,7 +141,7 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, User> implements I
String activeUrl = "http://opensource.yundingshuyuan.com/api/user/active/" + activeCode; String activeUrl = "http://opensource.yundingshuyuan.com/api/user/active/" + activeCode;
EmailDTO emailDTO = new EmailDTO("13546386889@163.com", EmailDTO emailDTO = new EmailDTO("13546386889@163.com",
email, "云里云外激活邮件", email, "云里云外激活邮件",
ActivceCodeEmailUtils.generateMail(activeUrl)); EmailUtils.generateActiveMail(activeUrl));
mailService.sendHtmlMail(emailDTO); mailService.sendHtmlMail(emailDTO);
} }
...@@ -179,18 +182,25 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, User> implements I ...@@ -179,18 +182,25 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, User> implements I
@Override @Override
public void alterPassword(String mobile, String password, String verifyCode) throws UserServiceException { public void alterPassword(String mobile, String password, String verifyCode) throws UserServiceException {
//1.检查验证码是否正确 // 1.检查验证码是否正确
String realVerifyCode = redisHelper.get(RedisKey.MOBILE_VERIFY_CODE(mobile)); String realVerifyCode = redisHelper.get(RedisKey.MOBILE_VERIFY_CODE(mobile));
if (StringUtils.isEmpty(realVerifyCode) || !realVerifyCode.equals(verifyCode)) { if (StringUtils.isEmpty(realVerifyCode) || !realVerifyCode.equals(verifyCode)) {
throw new UserServiceException(UserErrorCode.REG_VERIFY_CODE_ERROR); throw new UserServiceException(UserErrorCode.REG_VERIFY_CODE_ERROR);
} }
// 删除验证码 // 2.删除验证码
redisHelper.del(RedisKey.MOBILE_VERIFY_CODE(mobile)); redisHelper.del(RedisKey.MOBILE_VERIFY_CODE(mobile));
// 3.判断手机号是否注册
UserAuthLocal userAuthLocal = userAuthLocalService.getByAccount(mobile);
if (null == userAuthLocal) {
throw new UserServiceException(UserErrorCode.NON_LOCAL_AUTH_INFO);
}
// 4.更新所有本地认证信息里面的密码
UpdateWrapper updateWrapper = new UpdateWrapper(); UpdateWrapper updateWrapper = new UpdateWrapper();
updateWrapper.set("password", Md5Utils.encode(password)); updateWrapper.set("password", Md5Utils.encode(password));
updateWrapper.eq("account", mobile); updateWrapper.eq("user_id", userAuthLocal.getUserId());
userAuthLocalService.update(updateWrapper); userAuthLocalService.update(updateWrapper);
} }
...@@ -221,4 +231,82 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, User> implements I ...@@ -221,4 +231,82 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, User> implements I
// 3.删除激活码 // 3.删除激活码
redisHelper.del(RedisKey.EMAIL_ACTIVE_CODE(activeCode)); redisHelper.del(RedisKey.EMAIL_ACTIVE_CODE(activeCode));
} }
@Transactional(rollbackFor = Exception.class)
@Override
public void bindEMail(String email, String verifyCode, String userId) throws UserServiceException {
// 1.检测验证码
String realVerifyCode = redisHelper.get(RedisKey.EMAIL_VERIFY_CODE(email));
if (StringUtils.isEmpty(realVerifyCode) || !realVerifyCode.equals(verifyCode)) {
throw new UserServiceException(UserErrorCode.BINDING_EMAIL_VERIFY_CODE_ERROR);
}
// 2.删除验证码
redisHelper.del(RedisKey.EMAIL_VERIFY_CODE(email));
// 3.检测该邮箱是否已被占用
UserAuthLocal userAuthLocal = userAuthLocalService.getByAccount(email);
if (null != userAuthLocal) {
throw new UserServiceException(UserErrorCode.EMAIL_REGISTERED);
}
// 4.从其他本地认证里面查找用户密码
String password = getPasswordInUserAuthLocal(userId);
// 5.绑定邮箱
userAuthLocal = new UserAuthLocal();
userAuthLocal.setUserId(userId);
userAuthLocal.setAccount(email);
userAuthLocal.setPassword(password);
userAuthLocal.setAccountType(AccountType.EMAIL.getType());
userAuthLocal.setStatus(AuthStatus.NORMAL.getStatus());
userAuthLocalService.save(userAuthLocal);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void bindMobile(String mobile, String verifyCode, String userId) throws UserServiceException {
// 1.检测验证码
String realVerifyCode = redisHelper.get(RedisKey.MOBILE_VERIFY_CODE(mobile));
if (StringUtils.isEmpty(realVerifyCode) || !realVerifyCode.equals(verifyCode)) {
throw new UserServiceException(UserErrorCode.BINDING_MOBILE_VERIFY_CODE_ERROR);
}
// 2.删除验证码
redisHelper.del(RedisKey.MOBILE_VERIFY_CODE(mobile));
// 3.检测该手机号是否已被占用
UserAuthLocal userAuthLocal = userAuthLocalService.getByAccount(mobile);
if (null != userAuthLocal) {
throw new UserServiceException(UserErrorCode.MOBILE_REGISTERED);
}
// 4.查找该用户的其他本地认证信息
String password = getPasswordInUserAuthLocal(userId);
// 5.绑定手机号
userAuthLocal = new UserAuthLocal();
userAuthLocal.setUserId(userId);
userAuthLocal.setAccount(mobile);
userAuthLocal.setPassword(password);
userAuthLocal.setAccountType(AccountType.MOBILE.getType());
userAuthLocal.setStatus(AuthStatus.NORMAL.getStatus());
userAuthLocalService.save(userAuthLocal);
}
private String getPasswordInUserAuthLocal(String userId) {
QueryWrapper<UserAuthLocal> userAuthLocalQueryWrapper = new QueryWrapper<>();
userAuthLocalQueryWrapper.eq("user_id", userId);
List<UserAuthLocal> userAuthLocalList = userAuthLocalService.list(userAuthLocalQueryWrapper);
if (!CollectionUtils.isEmpty(userAuthLocalList)) {
for (UserAuthLocal ual : userAuthLocalList) {
if (!StringUtils.isEmpty(ual.getPassword())) {
return ual.getPassword();
}
}
}
return null;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment