367 lines
13 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package edu.whut.smilepicturebackend.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import edu.whut.smilepicturebackend.constant.CacheConstant;
import edu.whut.smilepicturebackend.constant.UserConstant;
import edu.whut.smilepicturebackend.exception.BusinessException;
import edu.whut.smilepicturebackend.exception.ErrorCode;
import edu.whut.smilepicturebackend.exception.ThrowUtils;
import edu.whut.smilepicturebackend.manager.auth.StpKit;
import edu.whut.smilepicturebackend.manager.email.EmailManager;
import edu.whut.smilepicturebackend.manager.upload.FilePictureUpload;
import edu.whut.smilepicturebackend.model.dto.user.UserEditPasswordRequest;
import edu.whut.smilepicturebackend.model.dto.user.UserEditRequest;
import edu.whut.smilepicturebackend.model.dto.user.UserQueryRequest;
import edu.whut.smilepicturebackend.model.entity.User;
import edu.whut.smilepicturebackend.model.enums.UserRoleEnum;
import edu.whut.smilepicturebackend.model.file.UploadPictureResult;
import edu.whut.smilepicturebackend.model.vo.LoginUserVO;
import edu.whut.smilepicturebackend.model.vo.UserVO;
import edu.whut.smilepicturebackend.service.UserService;
import edu.whut.smilepicturebackend.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @author 张三
* @description 针对表【user(用户)】的数据库操作Service实现
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService{
private final BCryptPasswordEncoder passwordEncoder;
private final EmailManager emailManager;
private final StringRedisTemplate stringRedisTemplate;
private final FilePictureUpload uploadPictureFile;
/**
* 用户注册
*/
@Override
public void userRegister(String userEmail, String codeKey, String codeValue) {
String KEY = String.format(CacheConstant.EMAIL_CODE_KEY, codeKey, userEmail);
// 获取 Redis 中的验证码
String code = stringRedisTemplate.opsForValue().get(KEY);
// 删除验证码
if (StrUtil.isEmpty(code) || !code.equals(codeValue)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误");
}
boolean existed = existedUserByEmail(userEmail);
if (existed) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在, 请直接登录!");
}
// 构建参数
User user = new User();
user.setUserEmail(userEmail);
// 默认值填充
fillDefaultValue(user);
boolean result = this.save(user);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR,"注册失败!");
stringRedisTemplate.delete(KEY);
emailManager.sendEmailAsRegisterSuccess(userEmail, "Smile图库 - 注册成功通知");
}
/**
* 获取加密后的密码
*
* @param userPassword 用户密码
* @return 加密后的密码
*/
@Override
public String getEncryptPassword(String userPassword) {
return passwordEncoder.encode(userPassword);
// // 加盐,混淆密码
// final String SALT = "smile12306";
// return DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
}
@Override
public LoginUserVO userLogin(String userAccount, String userPassword, String captchaKey, String captchaCode,HttpServletRequest request) {
String KEY = String.format(CacheConstant.CAPTCHA_CODE_KEY, captchaKey);
// 获取 Redis 中的验证码
String code = stringRedisTemplate.opsForValue().get(KEY);
// 删除验证码
stringRedisTemplate.delete(KEY);
if (StrUtil.isEmpty(code) || !code.equals(captchaCode)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误");
}
User user;
if (ReUtil.isMatch(RegexPool.EMAIL, userAccount)) {
// 根据 userEmail 获取用户信息
user = this.getOne(new LambdaQueryWrapper<User>().eq(User::getUserEmail, userAccount));
} else {
// 根据 userAccount 获取用户信息
user = this.getOne(new LambdaQueryWrapper<User>().eq(User::getUserAccount, userAccount));
}
if (user == null) {
throw new BusinessException(ErrorCode.DATA_ERROR, "用户不存在或密码错误");
}
// 校验密码
if (!passwordEncoder.matches(userPassword, user.getUserPassword())) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或者密码错误");
}
// 存储用户登录态
request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user);
// 记录用户登录态到 Sa-token便于空间鉴权时使用注意保证该用户信息与 SpringSession 中的信息过期时间一致
StpKit.SPACE.login(user.getId());
StpKit.SPACE.getSession().set(UserConstant.USER_LOGIN_STATE, user);
return this.getLoginUserVO(user);
}
/**
* 获取脱敏后的用户信息(无密码)
*
* @param user 用户
* @return 脱敏后的用户信息
*/
@Override
public LoginUserVO getLoginUserVO(User user) {
if (user == null) {
return null;
}
LoginUserVO loginUserVO = new LoginUserVO();
BeanUtil.copyProperties(user, loginUserVO);
return loginUserVO;
}
/**
* 获得脱敏后的用户信息(无密码、更新、编辑时间)
*
* @param user
* @return
*/
@Override
public UserVO getUserVO(User user) {
if (user == null) {
return null;
}
UserVO userVO = new UserVO();
BeanUtil.copyProperties(user, userVO);
return userVO;
}
/**
* 获取脱敏后的用户列表
*
* @param userList
* @return
*/
@Override
public List<UserVO> getUserVOList(List<User> userList) {
if (CollUtil.isEmpty(userList)) {
return new ArrayList<>();
}
return userList.stream()
.map(this::getUserVO)
.collect(Collectors.toList());
}
/**
* 获取当前登录用户
* @param request
* @return
*/
@Override
public User getLoginUser(HttpServletRequest request) {
// 判断是否已经登录
Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
User currentUser = (User) userObj;
if (currentUser == null || currentUser.getId() == null) {
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
}
// // 从数据库中查询(追求性能的话可以注释,直接返回上述结果)
// Long userId = currentUser.getId();
// currentUser = this.getById(userId);
// if (currentUser == null) {
// throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
// }
return currentUser;
}
/**
* 退出登录
*
* @param request
* @return
*/
@Override
public boolean userLogout(HttpServletRequest request) {
// 判断是否已经登录
Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
if (userObj == null) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
}
// 先从 session 拿出 userId 清掉 Sa-Token SPACE 会话
User user = (User) userObj;
StpKit.SPACE.logout(user.getId()); // 退出 Sa-Token SPACE 登录态
// 移除登录态
request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);
return true;
}
/**
* 查询条件
* @param req
* @return
*/
@Override
public LambdaQueryWrapper<User> getQueryWrapper(UserQueryRequest req) {
if (req == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
}
LambdaQueryWrapper<User> qw = Wrappers.lambdaQuery(User.class);
// 基本等值和模糊匹配
qw.eq(ObjUtil.isNotNull(req.getId()), User::getId, req.getId())
.eq(StrUtil.isNotBlank(req.getUserRole()), User::getUserRole, req.getUserRole())
.like(StrUtil.isNotBlank(req.getUserAccount()), User::getUserAccount, req.getUserAccount())
.like(StrUtil.isNotBlank(req.getUserName()), User::getUserName, req.getUserName())
.like(StrUtil.isNotBlank(req.getUserProfile()), User::getUserProfile, req.getUserProfile());
// 动态排序
if (StrUtil.isNotBlank(req.getSortField())) {
String column = StrUtil.toUnderlineCase(req.getSortField());
boolean asc = "ascend".equalsIgnoreCase(req.getSortOrder());
qw.last("ORDER BY " + column + (asc ? " ASC" : " DESC"));
}
return qw;
}
@Override
public boolean isAdmin(User user) {
return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
}
@Override
public String sendEmailCode(String userEmail) {
boolean existed = existedUserByEmail(userEmail);
if (existed) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在, 请直接登录!");
}
// 发送邮箱验证码
String code = RandomUtil.randomNumbers(4);
emailManager.sendEmailCode(userEmail, "Smile图库 - 注册验证码", code);
// 生成一个唯一 ID, 后面注册前端需要带过来
String key = UUID.randomUUID().toString();
// 存入 Redis, 5 分钟过期
stringRedisTemplate.opsForValue().set(String.format(CacheConstant.EMAIL_CODE_KEY, key, userEmail), code, 5, TimeUnit.MINUTES);
return key;
}
/**
* 用户修改密码
*/
@Override
public void editUserPassword(UserEditPasswordRequest userEditPasswordRequest,HttpServletRequest request) {
User loginUser = this.getLoginUser(request);
log.info(loginUser.toString());
if (!passwordEncoder.matches(userEditPasswordRequest.getOriginPassword(),loginUser.getUserPassword())) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "原密码错误");
}
User user=new User();
user.setId(loginUser.getId());
user.setUserPassword(getEncryptPassword(userEditPasswordRequest.getNewPassword()));
boolean result = this.updateById(user);
if (result) {
return;
}
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "密码修改失败!");
}
@Override
public void editUser(UserEditRequest userEditRequest,HttpServletRequest request) {
boolean existed= this.getBaseMapper()
.exists(new QueryWrapper<User>().eq("id", userEditRequest.getId()));
if (!existed) {
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在!");
}
User user=new User();
BeanUtil.copyProperties(userEditRequest,user);
boolean result = this.updateById(user);
if (result) {
return;
}
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "编辑失败!");
}
@Override
public String uploadAvatar(MultipartFile avatarFile) {
long userId = StpKit.SPACE.getLoginIdAsLong();
String pathPrefix = "avatar/" + userId + "/";
// 调用上传图片
UploadPictureResult uploadPictureResult = uploadPictureFile.uploadPicture(avatarFile, pathPrefix);
String avatarUrl = uploadPictureResult.getUrl();
if (StrUtil.isEmpty(avatarUrl)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "头像上传失败");
}
User user=new User();
user.setId(userId);
user.setUserAvatar(avatarUrl);
boolean result = this.updateById(user);
if (!result) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "头像更新失败");
}
return avatarUrl;
}
public boolean existedUserByEmail(String userEmail) {
return this.getBaseMapper()
.exists(new QueryWrapper<User>()
.eq("user_email", userEmail)
);
}
/**
* 填充默认值
*/
public void fillDefaultValue(User user) {
String random = RandomUtil.randomString(6);
user.setUserAccount("user_" + random);
user.setUserName("用户_" + random);
user.setUserRole(UserRoleEnum.USER.getValue());
user.setUserProfile("这个人很懒,什么也没留下!");
user.setUserPassword(getEncryptPassword("12345678"));
}
}