376 lines
12 KiB
Java
376 lines
12 KiB
Java
package com.bao.dating.service.impl;
|
|
|
|
import com.bao.dating.common.Result;
|
|
import com.bao.dating.common.ResultCode;
|
|
import com.bao.dating.common.aliyun.AliOssUtil;
|
|
import com.bao.dating.common.aliyun.GreenImageScan;
|
|
import com.bao.dating.common.aliyun.GreenTextScan;
|
|
import com.bao.dating.common.result.AliOssResult;
|
|
import com.bao.dating.common.result.GreenAuditResult;
|
|
import com.bao.dating.config.RedisConfig;
|
|
import com.bao.dating.context.UserContext;
|
|
import com.bao.dating.mapper.UserMapper;
|
|
import com.bao.dating.pojo.dto.UserInfoUpdateDTO;
|
|
import com.bao.dating.pojo.dto.UserLoginDTO;
|
|
import com.bao.dating.pojo.entity.User;
|
|
import com.bao.dating.pojo.vo.UserInfoVO;
|
|
import com.bao.dating.pojo.vo.UserLoginVO;
|
|
import com.bao.dating.service.UserService;
|
|
import com.bao.dating.service.VerificationCodeService;
|
|
import com.bao.dating.util.FileUtil;
|
|
import com.bao.dating.util.JwtUtil;
|
|
import com.bao.dating.util.MD5Util;
|
|
import io.jsonwebtoken.Claims;
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import java.time.LocalDateTime;
|
|
import java.util.*;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* 用户服务实现类
|
|
*
|
|
* @author KilLze
|
|
*/
|
|
@Service
|
|
public class UserServiceImpl implements UserService {
|
|
|
|
@Autowired
|
|
private AliOssUtil ossUtil;
|
|
|
|
@Autowired
|
|
private GreenTextScan greenTextScan;
|
|
|
|
@Autowired
|
|
private GreenImageScan greenImageScan;
|
|
|
|
@Autowired
|
|
private RedisTemplate<String, Object> redisTemplate;
|
|
|
|
@Autowired
|
|
private UserMapper userMapper;
|
|
|
|
@Autowired
|
|
private VerificationCodeService verificationCodeService;
|
|
|
|
/**
|
|
* 用户登录
|
|
*
|
|
* @param userLoginDTO 登录参数
|
|
* @return 登录信息
|
|
*/
|
|
@Override
|
|
public UserLoginVO userLogin(UserLoginDTO userLoginDTO) {
|
|
// 参数校验
|
|
if (userLoginDTO == null || userLoginDTO.getUsername() == null || userLoginDTO.getPassword() == null) {
|
|
throw new RuntimeException("用户名或密码不能为空");
|
|
}
|
|
// 查询用户
|
|
User user = userMapper.getByUsername(userLoginDTO.getUsername());
|
|
if (user == null) {
|
|
throw new RuntimeException("用户不存在");
|
|
}
|
|
// 密码校验
|
|
boolean match = MD5Util.verifyWithSalt(
|
|
userLoginDTO.getPassword(),
|
|
user.getSalt(),
|
|
user.getPasswordHash()
|
|
);
|
|
if (!match) {
|
|
throw new RuntimeException("密码错误");
|
|
}
|
|
// 生成token
|
|
String token = JwtUtil.generateToken(String.valueOf(user.getUserId()));
|
|
|
|
String redisKey = "login:token:" + user.getUserId();
|
|
redisTemplate.opsForValue().set(
|
|
redisKey,
|
|
token,
|
|
7,
|
|
TimeUnit.DAYS
|
|
);
|
|
|
|
// 封装返回
|
|
UserLoginVO userLoginVO = new UserLoginVO();
|
|
userLoginVO.setUserId(user.getUserId());
|
|
userLoginVO.setNickname(user.getNickname());
|
|
userLoginVO.setToken(token);
|
|
return userLoginVO;
|
|
}
|
|
|
|
/**
|
|
* 退出登录
|
|
* @param token 登录凭证
|
|
*/
|
|
@Override
|
|
public void logout(String token) {
|
|
Claims claims = JwtUtil.getClaimsFromToken(token);
|
|
Date expiration = claims.getExpiration();
|
|
// 判断 token 是否已过期
|
|
long ttl = expiration.getTime() - System.currentTimeMillis();
|
|
// 如果 token 已过期,则不用处理
|
|
if (ttl <= 0) {
|
|
return;
|
|
}
|
|
|
|
String logoutKey = "jwt:blacklist:" + token;
|
|
redisTemplate.opsForValue().set(
|
|
logoutKey,
|
|
"logout",
|
|
ttl,
|
|
TimeUnit.MILLISECONDS);
|
|
}
|
|
|
|
/**
|
|
* 获取用户信息
|
|
*
|
|
* @param userId 用户ID
|
|
* @return 用户信息
|
|
*/
|
|
@Override
|
|
public UserInfoVO getUserInfo(Long userId) {
|
|
User user = userMapper.selectByUserId(userId);
|
|
if (user == null) {
|
|
throw new RuntimeException("用户不存在");
|
|
}
|
|
UserInfoVO userInfoVO = new UserInfoVO();
|
|
BeanUtils.copyProperties(user, userInfoVO);
|
|
return userInfoVO;
|
|
}
|
|
|
|
/**
|
|
* 上传头像接口
|
|
*
|
|
* @param file 头像文件
|
|
* @return 上传后的文件URL
|
|
*/
|
|
@Override
|
|
public String uploadAvatar(MultipartFile file) {
|
|
// 参数校验
|
|
if (file == null || file.isEmpty()) {
|
|
throw new RuntimeException("图片不存在");
|
|
}
|
|
|
|
String originalFilename = file.getOriginalFilename();
|
|
if (originalFilename == null) {
|
|
throw new RuntimeException("文件名非法");
|
|
}
|
|
|
|
String fileType = FileUtil.getFileType(originalFilename);
|
|
if (!AliOssResult.IMAGE.equals(fileType)) {
|
|
throw new RuntimeException("仅支持图片上传");
|
|
}
|
|
|
|
//生成 OSS 路径
|
|
String extension = FileUtil.getFileExtension(originalFilename);
|
|
String fileName = UUID.randomUUID().toString().replace("-", "") + "." + extension;
|
|
Long userId = UserContext.getUserId();
|
|
String objectKey = "user/" + userId + "/avatar/" + fileName;
|
|
|
|
try {
|
|
byte[] fileBytes = file.getBytes();
|
|
String ossUrl = ossUtil.upload(fileBytes, objectKey);
|
|
|
|
if (ossUrl == null || ossUrl.isEmpty()) {
|
|
throw new RuntimeException("图片上传失败");
|
|
}
|
|
|
|
return ossUrl;
|
|
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("上传图片失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 上传背景图片
|
|
*
|
|
* @param file 背景图片文件
|
|
* @return 上传后的文件URL
|
|
*/
|
|
@Override
|
|
public String uploadBackground(MultipartFile file) {
|
|
// 参数校验
|
|
if (file == null || file.isEmpty()) {
|
|
throw new RuntimeException("图片不存在");
|
|
}
|
|
|
|
String originalFilename = file.getOriginalFilename();
|
|
if (originalFilename == null) {
|
|
throw new RuntimeException("文件名非法");
|
|
}
|
|
|
|
String fileType = FileUtil.getFileType(originalFilename);
|
|
if (!AliOssResult.IMAGE.equals(fileType)) {
|
|
throw new RuntimeException("仅支持图片上传");
|
|
}
|
|
|
|
//生成 OSS 路径
|
|
String extension = FileUtil.getFileExtension(originalFilename);
|
|
String fileName = UUID.randomUUID().toString().replace("-", "") + "." + extension;
|
|
Long userId = UserContext.getUserId();
|
|
String objectKey = "user/" + userId + "/background/" + fileName;
|
|
|
|
try {
|
|
byte[] fileBytes = file.getBytes();
|
|
String ossUrl = ossUtil.upload(fileBytes, objectKey);
|
|
|
|
if (ossUrl == null || ossUrl.isEmpty()) {
|
|
throw new RuntimeException("图片上传失败");
|
|
}
|
|
|
|
return ossUrl;
|
|
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("上传图片失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新用户信息
|
|
*
|
|
* @param userInfoUpdateDTO 用户信息更新参数
|
|
*/
|
|
@Override
|
|
public UserInfoVO updateUserInfo(UserInfoUpdateDTO userInfoUpdateDTO) {
|
|
Long userId = userInfoUpdateDTO.getUserId();
|
|
User user = userMapper.selectByUserId(userId);
|
|
if (user == null) {
|
|
throw new RuntimeException("用户不存在");
|
|
}
|
|
|
|
// 将需要审核的内容合并成一个文本,用于减少调用次数
|
|
StringBuilder textBuilder = new StringBuilder();
|
|
|
|
if (userInfoUpdateDTO.getNickname() != null && !userInfoUpdateDTO.getNickname().isEmpty()) {
|
|
textBuilder.append(userInfoUpdateDTO.getNickname()).append(" ");
|
|
}
|
|
if (userInfoUpdateDTO.getHobbies() != null && !userInfoUpdateDTO.getHobbies().isEmpty()) {
|
|
// 将爱好列表转换为字符串,用空格分隔
|
|
String hobbiesStr = String.join(" ", userInfoUpdateDTO.getHobbies());
|
|
textBuilder.append(hobbiesStr).append(" ");
|
|
}
|
|
if (userInfoUpdateDTO.getSignature() != null && !userInfoUpdateDTO.getSignature().isEmpty()) {
|
|
textBuilder.append(userInfoUpdateDTO.getSignature()).append(" ");
|
|
}
|
|
// 文本审核
|
|
if (textBuilder.length() > 0) {
|
|
Map textResult;
|
|
try {
|
|
textResult = greenTextScan.greeTextScan(textBuilder.toString());
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("用户信息文本审核失败");
|
|
}
|
|
|
|
String suggestion = (String) textResult.get("suggestion");
|
|
|
|
if (GreenAuditResult.BLOCK.equals(suggestion)) {
|
|
throw new RuntimeException("用户信息包含违规内容,修改失败");
|
|
}
|
|
if (GreenAuditResult.REVIEW.equals(suggestion)) {
|
|
throw new RuntimeException("用户信息需要人工审核,暂无法修改");
|
|
}
|
|
}
|
|
|
|
// 图片审核
|
|
List<String> imageKeys = new ArrayList<>();
|
|
if (userInfoUpdateDTO.getAvatarUrl() != null && !userInfoUpdateDTO.getAvatarUrl().isEmpty()) {
|
|
imageKeys.add(userInfoUpdateDTO.getAvatarUrl());
|
|
}
|
|
if (userInfoUpdateDTO.getBackgroundUrl() != null && !userInfoUpdateDTO.getBackgroundUrl().isEmpty()) {
|
|
imageKeys.add(userInfoUpdateDTO.getBackgroundUrl());
|
|
}
|
|
if (!imageKeys.isEmpty()) {
|
|
Map imageResult;
|
|
try {
|
|
imageResult = greenImageScan.imageScan(imageKeys);
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("用户图片审核失败");
|
|
}
|
|
|
|
String suggestion = (String) imageResult.get("suggestion");
|
|
|
|
if (GreenAuditResult.BLOCK.equals(suggestion)) {
|
|
throw new RuntimeException("头像或背景图不合规,修改失败");
|
|
}
|
|
if (GreenAuditResult.REVIEW.equals(suggestion)) {
|
|
throw new RuntimeException("头像或背景图需要人工审核,暂无法修改");
|
|
}
|
|
}
|
|
// 默认昵称兜底
|
|
if (userInfoUpdateDTO.getNickname() == null || userInfoUpdateDTO.getNickname().trim().isEmpty()) {
|
|
userInfoUpdateDTO.setNickname(user.getUserName());
|
|
}
|
|
userInfoUpdateDTO.setUpdatedAt(LocalDateTime.now());
|
|
|
|
// 更新数据库
|
|
userMapper.updateUserInfoByUserId(userInfoUpdateDTO);
|
|
|
|
// 封装返回结果
|
|
User updatedUser = userMapper.selectByUserId(userInfoUpdateDTO.getUserId());
|
|
UserInfoVO userInfoVO = new UserInfoVO();
|
|
BeanUtils.copyProperties(updatedUser, userInfoVO);
|
|
return userInfoVO;
|
|
}
|
|
|
|
/**
|
|
* 查询用户
|
|
* @param userName 用户民称
|
|
* @return
|
|
*/
|
|
@Override
|
|
public Boolean registerUser(String userName,String userPassword) {
|
|
//校验参数是否为空
|
|
if (userName.isEmpty() || userPassword.isEmpty()){
|
|
return false;
|
|
}
|
|
//产看数据库是否存在已注册用户
|
|
User user = userMapper.getByUsername(userName);
|
|
if (user != null){
|
|
return false;
|
|
}
|
|
//将用户数据存入苏数据库
|
|
String salt = "lyy123";
|
|
String passwordHash = MD5Util.encryptWithSalt(userPassword, salt);
|
|
//查询最大用户id
|
|
Long maxId = userMapper.selectMaxId();
|
|
User saveUser = new User();
|
|
saveUser.setUserId(maxId+1);
|
|
saveUser.setUserName(userName);
|
|
saveUser.setPasswordHash(passwordHash);
|
|
saveUser.setSalt(salt);
|
|
saveUser.setUpdatedAt(LocalDateTime.now());
|
|
saveUser.setCreatedAt(LocalDateTime.now());
|
|
int count = userMapper.saveUser(saveUser);
|
|
return count > 0;
|
|
}
|
|
|
|
/**
|
|
* 邮箱登录
|
|
* @param email 邮箱
|
|
* @param code 验证码
|
|
* @return 脱敏用户信息
|
|
*/
|
|
@Override
|
|
public UserLoginVO emailLogin(String email, String code) {
|
|
User user = userMapper.selectByUserEmailUser(email);
|
|
if (user == null)
|
|
return null;
|
|
boolean flag = verificationCodeService.verifyEmailCode(email, code);
|
|
if (!flag)
|
|
return null;
|
|
// 生成token
|
|
String token = JwtUtil.generateToken(String.valueOf(user.getUserId()));
|
|
UserLoginVO userLoginVO = new UserLoginVO();
|
|
userLoginVO.setUserId(user.getUserId());
|
|
userLoginVO.setNickname(user.getNickname());
|
|
userLoginVO.setToken(token);
|
|
return userLoginVO;
|
|
}
|
|
}
|