用户信息管理,帖子模块

main
2991692032 1 month ago
parent ca6ab29332
commit f632c54b7f

@ -9,6 +9,37 @@
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="0@127.0.0.1" uuid="a9faee19-21f5-4be8-a112-2b0ac06aaaaf">
<driver-ref>redis</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>$PROJECT_DIR$/unilife-server/src/main/resources/application.yml</remarks>
<jdbc-driver>jdbc.RedisDriver</jdbc-driver>
<jdbc-url>jdbc:redis://127.0.0.1:6379/0</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="UniLife@localhost" uuid="82d366b7-3273-49cc-8ccc-6fef4a2d408d">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>$PROJECT_DIR$/unilife-server/src/main/resources/application.yml</remarks>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306/UniLife?allowPublicKeyRetrieval=true&amp;useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=UTF-8</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>

@ -36,6 +36,12 @@
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
@ -117,6 +123,10 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>

@ -40,7 +40,7 @@ public class Result<T>{
}
public static <T> Result<T> success(T data, String message) {
return new Result<>(200, message, null);
return new Result<>(200, message, data);
}
/**

@ -5,6 +5,9 @@ import com.unilife.model.dto.EmailDTO;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.dto.LoginEmailDTO;
import com.unilife.model.dto.RegisterDTO;
import com.unilife.model.dto.UpdateEmailDTO;
import com.unilife.model.dto.UpdatePasswordDTO;
import com.unilife.model.dto.UpdateProfileDTO;
import com.unilife.model.vo.LoginVO;
import com.unilife.service.UserService;
import com.unilife.utils.BaseContext;
@ -13,10 +16,15 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
@ -63,7 +71,72 @@ public class UserController {
@Operation(summary = "邮箱验证码登录")
@PostMapping("login/code")
public Result<?> loginWithEmailCode(@RequestBody LoginEmailDTO loginEmailDTO, HttpServletRequest request) {
return userService.loginWithEmail(loginEmailDTO, request);
Result<?> login = userService.loginWithEmail(loginEmailDTO, request);
LoginVO vo = (LoginVO) login.getData();
if (vo == null) {
return login;
}
Long id = vo.getId();
String token = jwtUtil.generateToken(id);
vo.setToken(token);
BaseContext.setId(id);
return Result.success(vo);
}
// 用户信息管理相关API
@Operation(summary = "获取用户个人信息")
@GetMapping("profile")
public Result<?> getUserProfile() {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.getUserProfile(userId);
}
@Operation(summary = "更新用户个人信息")
@PutMapping("profile")
public Result<?> updateUserProfile(@RequestBody UpdateProfileDTO profileDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.updateUserProfile(userId, profileDTO);
}
@Operation(summary = "修改用户密码")
@PutMapping("password")
public Result<?> updatePassword(@RequestBody UpdatePasswordDTO passwordDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.updatePassword(userId, passwordDTO);
}
@Operation(summary = "上传用户头像")
@PostMapping("avatar")
public Result<?> updateAvatar(@RequestParam("file") MultipartFile file) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.updateAvatar(userId, file);
}
@Operation(summary = "更新用户邮箱")
@PutMapping("email")
public Result<?> updateEmail(@RequestBody UpdateEmailDTO emailDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.updateEmail(userId, emailDTO);
}
}

@ -25,15 +25,22 @@ public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("JwtInterceptor preHandle");
String token = request.getHeader("Authorization");
if(StrUtil.isBlank(token)){
String authHeader = request.getHeader("Authorization");
if(StrUtil.isBlank(authHeader)){
response.setStatus(401);
return false;
}
// 处理Bearer token格式
String token = authHeader;
if(authHeader.startsWith("Bearer ")){
token = authHeader.substring(7);
}
log.info("Extracted token:{}", token);
boolean verified = jwtUtil.verifyToken(token);
if (!verified) {
response.setStatus(401);
@ -42,9 +49,11 @@ public class JwtInterceptor implements HandlerInterceptor {
//从token中获取userid并存入threadlocal
Long userId = jwtUtil.getUserIdFromToken(token);
if(userId == null) {
response.setStatus(401);
return false;
}
BaseContext.setId(userId);
return true;
}

@ -4,11 +4,21 @@ import com.unilife.model.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
@Mapper
public interface UserMapper {
void insert(User user);
User FindByEmail(@Param("email") String email, @Param("password") String password);
User getUserByEmail(String email);
User findByEmail(String email);
void updateLoginInfo(@Param("userId") Long userId,
@Param("ipLocation") String ipLocation,
@Param("loginTime") Date loginTime);
void UpdateIPLocation(@Param("email") String email,@Param("loginIp") String loginIp);
User FindByOnlyEmail(@Param("email") String email);
// 用户信息管理相关方法
User getUserById(Long id);
void updateUserProfile(User user);
void updatePassword(@Param("id") Long id, @Param("newPassword") String newPassword);
void updateAvatar(@Param("id") Long id, @Param("avatar") String avatarUrl);
void updateEmail(@Param("id") Long id, @Param("email") String email);
}

@ -4,7 +4,11 @@ import com.unilife.common.result.Result;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.dto.LoginEmailDTO;
import com.unilife.model.dto.RegisterDTO;
import com.unilife.model.dto.UpdateEmailDTO;
import com.unilife.model.dto.UpdatePasswordDTO;
import com.unilife.model.dto.UpdateProfileDTO;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.MultipartFile;
public interface UserService {
@ -12,7 +16,18 @@ public interface UserService {
Result login(LoginDTO loginDTO, HttpServletRequest request);
Result sendVerificationCode(String email,HttpServletRequest request);
Result sendVerificationCode(String email, HttpServletRequest request);
Result loginWithEmail(LoginEmailDTO loginEmailDTO,HttpServletRequest request);
Result loginWithEmail(LoginEmailDTO loginEmailDTO, HttpServletRequest request);
// 用户信息管理相关方法
Result getUserProfile(Long userId);
Result updateUserProfile(Long userId, UpdateProfileDTO profileDTO);
Result updatePassword(Long userId, UpdatePasswordDTO passwordDTO);
Result updateAvatar(Long userId, MultipartFile file);
Result updateEmail(Long userId, UpdateEmailDTO emailDTO);
}

@ -8,6 +8,9 @@ import com.unilife.mapper.UserMapper;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.dto.LoginEmailDTO;
import com.unilife.model.dto.RegisterDTO;
import com.unilife.model.dto.UpdateEmailDTO;
import com.unilife.model.dto.UpdatePasswordDTO;
import com.unilife.model.dto.UpdateProfileDTO;
import com.unilife.model.entity.User;
import com.unilife.model.vo.LoginVO;
import com.unilife.model.vo.RegisterVO;
@ -18,7 +21,9 @@ import com.unilife.utils.RegexUtils;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
@ -26,10 +31,13 @@ import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@ -69,7 +77,7 @@ public class UserServiceImpl implements UserService {
if(registerDTO.getPassword().length() < 6) {
return Result.error(400,"密码长度过短!");
}
User getuser = userMapper.FindByOnlyEmail(registerDTO.getEmail());
User getuser = userMapper.findByEmail(registerDTO.getEmail());
if(getuser != null) {
return Result.error(400,"用户已存在!");
}
@ -86,24 +94,36 @@ public class UserServiceImpl implements UserService {
@Override
public Result login(LoginDTO loginDTO,HttpServletRequest request) {
User user = new User();
BeanUtil.copyProperties(loginDTO,user);//将登录的前端传来的消息拷贝给这个user
User getuser = userMapper.FindByEmail(user.getEmail(),user.getPassword());
if(getuser == null)
{
return Result.error(loginDTO,"用户不存在,登录失败!");
if(loginDTO==null|| StringUtils.isEmpty(loginDTO.getEmail())||StringUtils.isEmpty(loginDTO.getPassword())){
return Result.error(400,"邮箱或密码不能为空");
}
if(!user.getPassword().equals(getuser.getPassword()))
{
return Result.error(loginDTO,"密码错误,登录失败!");
User user = userMapper.findByEmail(loginDTO.getEmail());
if (user == null) {
return Result.error(400, "账号或密码错误");
}
String LastLogIpLocation = getuser.getLoginIp();
String IPAddress = ipLocationService.getClientIP(request);
String Location = ipLocationService.getIPLocation(IPAddress);
getuser.setLoginIp(Location);
userMapper.UpdateIPLocation(getuser.getEmail(), getuser.getLoginIp());
if (!loginDTO.getPassword().equals(user.getPassword())) {
return Result.error(400, "账号或密码错误");
}
if (user.getStatus() != 1) {
return Result.error(403, "账号已被禁用,请联系管理员");
}
String LastLogIpLocation = user.getLoginIp();
String currentIp = ipLocationService.getClientIP(request);
String ipLocation = ipLocationService.getIPLocation(currentIp);
user.setLoginIp(ipLocation);
user.setLoginTime(LocalDateTime.now());
userMapper.updateLoginInfo(user.getId(),ipLocation,new Date());
LoginVO loginVO = new LoginVO();
return Result.success(loginVO,"上次登录IP归属地为" + LastLogIpLocation);
BeanUtil.copyProperties(user,loginVO);
String message = StringUtils.isEmpty(LastLogIpLocation) ? "首次登录" : "上次登录IP归属地为" + LastLogIpLocation;
return Result.success(loginVO, message);
}
@Override
@ -177,39 +197,43 @@ public class UserServiceImpl implements UserService {
String email=loginEmailDTO.getEmail();
if(RegexUtils.isEmailInvalid(email)){
return Result.error(null,"请输入正确的邮箱");
return Result.error(400,"请输入正确的邮箱");
}
String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstant.LOGIN_EMAIL_KEY + email);
if (cacheCode == null) {
return Result.error(null, "验证码已过期或未发送,请重新获取");
return Result.error(400, "验证码已过期或未发送,请重新获取");
}
// 3. 校验验证码是否正确
String code = loginEmailDTO.getCode();
if (!cacheCode.equals(code)) {
return Result.error(null, "验证码错误");
return Result.error(400, "验证码错误");
}
// 4. 验证通过,删除验证码
stringRedisTemplate.delete(RedisConstant.LOGIN_EMAIL_KEY + email);
// 5. 查询用户是否存在
User user=userMapper.getUserByEmail(email);
User user=userMapper.findByEmail(email);
if(user == null){
user = createUserWithEmail(email,request);
}
//6.生成登录凭证
//TODO
// 更新登录信息
String currentIp = ipLocationService.getClientIP(request);
String ipLocation = ipLocationService.getIPLocation(currentIp);
user.setLoginIp(ipLocation);
user.setLoginTime(LocalDateTime.now());
userMapper.updateLoginInfo(user.getId(), ipLocation, new Date());
String token = jwtUtil.generateToken(user.getId());
// 8. 返回用户信息和登录凭证
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("token", token);
userInfo.put("user", user);
// 创建LoginVO对象与普通登录保持一致的返回格式
LoginVO loginVO = new LoginVO();
BeanUtil.copyProperties(user, loginVO);
return Result.success(userInfo);
// 返回结果不在这里生成token由Controller统一处理
String message = "邮箱验证码登录成功";
return Result.success(loginVO, message);
}
/**
@ -243,4 +267,147 @@ public class UserServiceImpl implements UserService {
return user;
}
@Override
public Result getUserProfile(Long userId) {
// 根据用户ID获取用户信息
User user = userMapper.getUserById(userId);
if (user == null) {
return Result.error(404, "用户不存在");
}
// 出于安全考虑,不返回密码字段
user.setPassword(null);
return Result.success(user);
}
@Override
public Result updateUserProfile(Long userId, UpdateProfileDTO profileDTO) {
// 检查用户是否存在
User user = userMapper.getUserById(userId);
if (user == null) {
return Result.error(404, "用户不存在");
}
// 更新用户信息
user.setNickname(profileDTO.getNickname());
user.setBio(profileDTO.getBio());
user.setGender(profileDTO.getGender());
user.setDepartment(profileDTO.getDepartment());
user.setMajor(profileDTO.getMajor());
user.setGrade(profileDTO.getGrade());
// 保存更新
userMapper.updateUserProfile(user);
return Result.success(null, "个人资料更新成功");
}
@Override
public Result updatePassword(Long userId, UpdatePasswordDTO passwordDTO) {
// 检查用户是否存在
User user = userMapper.getUserById(userId);
if (user == null) {
return Result.error(404, "用户不存在");
}
// 验证验证码
String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstant.LOGIN_EMAIL_KEY + user.getEmail());
if (cacheCode == null) {
return Result.error(400, "验证码已过期或未发送,请重新获取");
}
if (!cacheCode.equals(passwordDTO.getCode())) {
return Result.error(400, "验证码错误");
}
// 验证通过,删除验证码
stringRedisTemplate.delete(RedisConstant.LOGIN_EMAIL_KEY + user.getEmail());
// 更新密码
userMapper.updatePassword(userId, passwordDTO.getNewPassword());
return Result.success(null, "密码修改成功");
}
@Override
public Result updateAvatar(Long userId, MultipartFile file) {
// 检查用户是否存在
User user = userMapper.getUserById(userId);
if (user == null) {
return Result.error(404, "用户不存在");
}
// 检查文件是否为空
if (file.isEmpty()) {
return Result.error(400, "上传文件不能为空");
}
// 检查文件类型
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
return Result.error(400, "只能上传图片文件");
}
try {
// 生成文件名
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename != null ? originalFilename.substring(originalFilename.lastIndexOf(".")) : ".jpg";
String filename = "avatar_" + userId + "_" + System.currentTimeMillis() + suffix;
// TODO: 实际项目中应该将文件保存到云存储或服务器指定目录
// 这里简化处理假设保存成功并返回URL
String avatarUrl = "https://example.com/avatars/" + filename;
// 更新用户头像URL
userMapper.updateAvatar(userId, avatarUrl);
Map<String, String> data = new HashMap<>();
data.put("avatar", avatarUrl);
return Result.success(data, "头像上传成功");
} catch (Exception e) {
log.error("头像上传失败", e);
return Result.error(500, "头像上传失败");
}
}
@Override
public Result updateEmail(Long userId, UpdateEmailDTO emailDTO) {
// 检查用户是否存在
User user = userMapper.getUserById(userId);
if (user == null) {
return Result.error(404, "用户不存在");
}
// 检查邮箱格式
String email = emailDTO.getEmail();
if (RegexUtils.isEmailInvalid(email)) {
return Result.error(400, "邮箱格式不正确");
}
// 检查邮箱是否已被使用
User existingUser = userMapper.findByEmail(email);
if (existingUser != null && !existingUser.getId().equals(userId)) {
return Result.error(400, "该邮箱已被其他用户使用");
}
// 验证验证码
String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstant.LOGIN_EMAIL_KEY + email);
if (cacheCode == null) {
return Result.error(400, "验证码已过期或未发送,请重新获取");
}
if (!cacheCode.equals(emailDTO.getCode())) {
return Result.error(400, "验证码错误");
}
// 验证通过,删除验证码
stringRedisTemplate.delete(RedisConstant.LOGIN_EMAIL_KEY + email);
// 更新邮箱
userMapper.updateEmail(userId, email);
return Result.success(null, "邮箱更新成功");
}
}

@ -2,6 +2,7 @@ package com.unilife.utils;
import cn.hutool.core.date.DateTime;
import cn.hutool.jwt.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -9,6 +10,7 @@ import java.util.HashMap;
import java.util.Map;
@Component
@Slf4j
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@ -36,7 +38,7 @@ public class JwtUtil {
}
public Long getUserIdFromToken(String token) {
try {
return (Long)JWTUtil.parseToken(token).getPayload("userId");
return Long.valueOf(JWTUtil.parseToken(token).getPayload("userId").toString());
}catch (Exception e){
return null;
}

@ -37,18 +37,22 @@
)
</insert>
<select id="FindByEmail" resultMap="userResultMap">
SELECT id, email, password, username, nickname, avatar, role, is_verified, status,login_ip
FROM users
WHERE email = #{email} AND password = #{password}
</select>
<select id="FindByOnlyEmail" resultMap="userResultMap">
<select id="findByEmail" resultMap="userResultMap">
SELECT id, email, password, username, nickname, avatar, role, is_verified, status,login_ip
FROM users
WHERE email = #{email}
</select>
<update id="updateLoginInfo">
UPDATE users
SET login_ip = #{ipLocation},
login_time = #{loginTime},
updated_at = NOW()
WHERE id = #{userId}
</update>
<update id="UpdateIPLocation" parameterType="com.unilife.model.entity.User">
UPDATE users
SET login_ip = #{loginIp}
@ -75,4 +79,62 @@
login_time
from users where email=#{email};
</select>
</mapper>
<!-- 用户信息管理相关SQL -->
<select id="getUserById" resultType="com.unilife.model.entity.User">
SELECT id,
username,
email,
nickname,
avatar,
bio,
gender,
student_id as studentId,
department,
major,
grade,
points,
role,
status,
is_verified as isVerified,
login_ip as loginIp,
login_time as loginTime,
created_at as createdAt,
updated_at as updatedAt
FROM users
WHERE id = #{id}
</select>
<update id="updateUserProfile" parameterType="com.unilife.model.entity.User">
UPDATE users
SET nickname = #{nickname},
bio = #{bio},
gender = #{gender},
department = #{department},
major = #{major},
grade = #{grade},
updated_at = NOW()
WHERE id = #{id}
</update>
<update id="updatePassword">
UPDATE users
SET password = #{newPassword},
updated_at = NOW()
WHERE id = #{id}
</update>
<update id="updateAvatar">
UPDATE users
SET avatar = #{avatar},
updated_at = NOW()
WHERE id = #{id}
</update>
<update id="updateEmail">
UPDATE users
SET email = #{email},
updated_at = NOW()
WHERE id = #{id}
</update>
</mapper>

Loading…
Cancel
Save