用户信息修改及头像上传功能实现

pull/2/MERGE
chenyuepan 1 month ago
parent 750ba603fb
commit 75d444a532

@ -9,6 +9,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController @RestController
@RequestMapping("/user") @RequestMapping("/user")
@ -39,8 +40,8 @@ public class UserController {
@ApiOperation("用户个人信息完善") @ApiOperation("用户个人信息完善")
@PostMapping("/update") @PostMapping("/update")
public Result update(@RequestBody User user){ public Result update(@RequestBody UserInfo userInfo){
return userService.userUpdate(user); return userService.userUpdate(userInfo);
} }
@ApiOperation("获取用户列表") @ApiOperation("获取用户列表")
@ -103,4 +104,16 @@ public class UserController {
public Result passwd(@RequestBody PasswdDTO dto){ public Result passwd(@RequestBody PasswdDTO dto){
return userService.passwd(dto); return userService.passwd(dto);
} }
@ApiOperation("用户头像上传")
@PostMapping("/upload")
public Result upload(@RequestParam("id") Integer userId, @RequestParam("image")MultipartFile file){
return userService.upload(userId, file);
}
@ApiOperation("用户信息获取")
@GetMapping("/info/{id}")
public Result info(@PathVariable("id") Integer userId){
return userService.getUserInfo(userId);
}
} }

@ -66,6 +66,24 @@ public class User implements Serializable {
@DateTimeFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDateTime registerTime; private LocalDateTime registerTime;
/**
*
*/
private String avatar;
/**
*
*/
private Integer isUpload;
/**
*
*/
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDateTime updateTime;
@TableField(exist = false) @TableField(exist = false)
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

@ -0,0 +1,15 @@
package com.example.venue_reservation_service.dto;
import lombok.Data;
@Data
public class UserInfo {
private Integer id;
private String username;
private String phone;
private String email;
}

@ -4,6 +4,7 @@ import com.example.venue_reservation_service.domain.User;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.example.venue_reservation_service.dto.*; import com.example.venue_reservation_service.dto.*;
import com.example.venue_reservation_service.vo.Result; import com.example.venue_reservation_service.vo.Result;
import org.springframework.web.multipart.MultipartFile;
/** /**
* @author 31586 * @author 31586
@ -16,7 +17,7 @@ public interface UserService extends IService<User> {
Result adminLogin(AdminDTO dto); Result adminLogin(AdminDTO dto);
Result userUpdate(User user); Result userUpdate(UserInfo userInfo);
Result getUserList(UserDTO dto); Result getUserList(UserDTO dto);
@ -38,4 +39,7 @@ public interface UserService extends IService<User> {
Result passwd(PasswdDTO dto); Result passwd(PasswdDTO dto);
Result upload(Integer userId, MultipartFile file);
Result getUserInfo(Integer userId);
} }

@ -1,5 +1,6 @@
package com.example.venue_reservation_service.service.impl; package com.example.venue_reservation_service.service.impl;
import cn.hutool.core.lang.UUID;
import cn.hutool.jwt.JWTUtil; import cn.hutool.jwt.JWTUtil;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -18,18 +19,19 @@ import com.example.venue_reservation_service.vo.QueryVO;
import com.example.venue_reservation_service.vo.Result; import com.example.venue_reservation_service.vo.Result;
import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.github.yulichang.wrapper.MPJLambdaWrapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.io.FilenameUtils;
import org.checkerframework.checker.units.qual.A; import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
/** /**
* @author 31586 * @author 31586
@ -66,6 +68,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
@Value("${spring.application.name}") @Value("${spring.application.name}")
private String name; private String name;
@Value("${access.url}")
private String imgUrl;
@Resource
private MinioUtil minioUtil;
@Override @Override
public Result getOpenId(String code) { public Result getOpenId(String code) {
@ -86,9 +93,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
user = new User(); user = new User();
user.setUsername("用户" + System.currentTimeMillis()); user.setUsername("用户" + System.currentTimeMillis());
user.setOpenId(openid); user.setOpenId(openid);
user.setType(0); user.setType(0); // 表示普通用户
user.setAccount(LocalDate.now().getYear() +"-"+ System.currentTimeMillis()); user.setAccount(LocalDate.now().getYear() +"-"+ System.currentTimeMillis());
user.setRegisterTime(LocalDateTime.now()); user.setRegisterTime(LocalDateTime.now());
user.setIsUpload(0);
save(user); save(user);
// 添加新的账户余额信息 // 添加新的账户余额信息
@ -98,6 +106,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
// 获取用户账号余额数据 // 获取用户账号余额数据
Double money = balanceService.getOne(Wrappers.<Balance>lambdaQuery().eq(Balance::getUserId, user.getId())) Double money = balanceService.getOne(Wrappers.<Balance>lambdaQuery().eq(Balance::getUserId, user.getId()))
.getBalance(); .getBalance();
if(user.getIsUpload() == 1){
user.setAvatar(imgUrl + user.getAvatar());
}
user.setBalance(money); user.setBalance(money);
LoginVo loginVo = new LoginVo(user, JwtUtil.createToken(user.getId())); LoginVo loginVo = new LoginVo(user, JwtUtil.createToken(user.getId()));
return Result.ok(loginVo).message("登录成功"); return Result.ok(loginVo).message("登录成功");
@ -133,23 +144,32 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
return Result.fail().message("账号密码不匹配"); return Result.fail().message("账号密码不匹配");
} }
user.setAccessCode(""); user.setAccessCode("");
if(user.getIsUpload() == 1){
user.setAvatar(imgUrl + user.getAvatar());
}
LoginVo vo = new LoginVo(user, JwtUtil.createToken(user.getId())); LoginVo vo = new LoginVo(user, JwtUtil.createToken(user.getId()));
return Result.ok(vo).message("登录成功"); return Result.ok(vo).message("登录成功");
} }
/**
*
* usernamephoneemail
*/
@Override @Override
public Result userUpdate(User user) { public Result userUpdate(UserInfo userInfo) {
String username = getById(user.getId()).getUsername(); User user = getById(userInfo.getId());
if(!username.equals(user.getUsername())){ if(Optional.ofNullable(user).isEmpty()){
// 表示用户修改了用户名 return Result.fail().message("用户信息不存在");
User one = getOne(Wrappers.<User>lambdaQuery()
.eq(User::getUsername, user.getUsername()));
if (Optional.ofNullable(one).isPresent()) {
return Result.fail().message("当前用户名已经被使用");
}
} }
user.setUsername(userInfo.getUsername());
// TODO 校验手机号是否合法
// 1. 手机号未被他人使用
// 2. 符合国家标准
user.setUserPhone(userInfo.getPhone());
user.setEmail(userInfo.getEmail());
user.setUpdateTime(LocalDateTime.now());
updateById(user); updateById(user);
user.setAccessCode("");
return Result.ok(user).message("用户信息修改成功"); return Result.ok(user).message("用户信息修改成功");
} }
@ -324,6 +344,54 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
updateById(user); updateById(user);
return Result.ok().message("密码修改成功"); return Result.ok().message("密码修改成功");
} }
@Override
public Result upload(Integer userId, MultipartFile file) {
User user = getById(userId);
if (Optional.ofNullable(user).isEmpty()) {
return Result.fail().message("用户信息不存在");
}
// 检查是否上传头像
if(user.getIsUpload() == 1){
// 删除原来的图片
try {
minioUtil.removeFile(user.getAvatar());
}catch (Exception e){
e.printStackTrace();
return Result.fail().message("服务错误,请稍后重试");
}
}
if (ImageValidator.validateImageFile(file)) {
// 生成新的名称
String extension = FilenameUtils.getExtension(file.getOriginalFilename()); // 获取文件扩展名
String filename = UUID.randomUUID().toString().replaceAll("-", "") + "." + extension;
try {
minioUtil.uploadFile(file, filename, file.getContentType());
}catch (Exception e){
e.printStackTrace();
return Result.fail().message("服务错误,图片上传失败,请稍后重试");
}
user.setIsUpload(1);
user.setAvatar(filename);
updateById(user);
return Result.ok().message("头像图片上传成功");
}
return Result.fail().message("非图片文件或文件过大");
}
@Override
public Result getUserInfo(Integer userId) {
User user = getById(userId);
Integer type = user.getType();
if(type == 1 || type == 0){
// 脱敏处理
user.setUserPhone(PhoneUtil.maskPhone(user.getUserPhone()));
user.setEmail(PhoneUtil.maskEmail(user.getEmail()));
}
user.setAvatar(imgUrl + user.getAvatar());
user.setAccessCode("");
return Result.ok(user).message("用户信息查询成功");
}
} }

@ -14,7 +14,7 @@ public class DownExcel {
response.setCharacterEncoding("UTF-8");// 设置字符编码 response.setCharacterEncoding("UTF-8");// 设置字符编码
response.setHeader("Content-disposition", "attachment;filename=reservation-"+substring+".xlsx"); // 设置响应头 response.setHeader("Content-disposition", "attachment;filename=reservation-"+substring+".xlsx"); // 设置响应头
EasyExcel.write(response.getOutputStream(), t) EasyExcel.write(response.getOutputStream(), t)
.sheet("模板") .sheet("数据报表")
.doWrite(list); //用io流来写入数据 .doWrite(list); //用io流来写入数据
} }
} }

@ -0,0 +1,54 @@
package com.example.venue_reservation_service.utils;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
public class ImageValidator {
// 允许的图片扩展名(小写)
private static final Set<String> ALLOWED_EXTENSIONS = Set.of("jpg", "jpeg", "png", "gif", "bmp", "webp");
// 250MB 字节限制 (250 * 1024 * 1024)
private static final long MAX_SIZE_BYTES = 262144000L;
/**
* <250MB
* @param file MultipartFile
* @return true=, false=
*/
public static boolean validateImageFile(MultipartFile file) {
// 1. 基础检查
if (file == null || file.isEmpty()) {
return false; // 空文件直接拒绝[3,5](@ref)
}
// 2. 验证文件大小
if (file.getSize() > MAX_SIZE_BYTES) {
return false; // 超过250MB[6,7](@ref)
}
// 3. 校验扩展名
String originalName = file.getOriginalFilename();
if (originalName == null || originalName.lastIndexOf(".") == -1) {
return false; // 无扩展名文件[5](@ref)
}
String extension = originalName.substring(originalName.lastIndexOf(".") + 1).toLowerCase();
if (!ALLOWED_EXTENSIONS.contains(extension)) {
return false; // 扩展名不在白名单[5](@ref)
}
// 4. 通过文件头验证真实格式(防止伪扩展名)
try (ImageInputStream iis = ImageIO.createImageInputStream(file.getInputStream())) {
if (iis == null) return false; // 无法创建流
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
return readers.hasNext(); // 存在ImageReader说明是有效图片[2,5](@ref)
} catch (IOException e) {
return false; // 读取异常视为非图片
}
}
}

@ -50,4 +50,11 @@ public class PhoneUtil {
// 替换为6个星号 // 替换为6个星号
return "******" + domain; return "******" + domain;
} }
// 手机号脱敏
public static String maskPhone(String phone) {
if (phone == null || phone.length() != 11) return phone;
return phone.substring(0, 3) + "****" + phone.substring(7);
}
} }

@ -33,6 +33,7 @@ spring:
min-idle: 0 min-idle: 0
jackson: jackson:
date-format: yyyy-MM-dd HH:mm:ss date-format: yyyy-MM-dd HH:mm:ss
weixin: weixin:
app_id: wxce0025f2c0b10a8e app_id: wxce0025f2c0b10a8e
secret: 686870f1b1a875369ee979e0648fa950 secret: 686870f1b1a875369ee979e0648fa950
@ -48,7 +49,8 @@ minio:
access-key: minioadmin access-key: minioadmin
secret-key: minioadmin secret-key: minioadmin
bucket: venue-sport bucket: venue-sport
access:
url: http://${localhosturl}:9000/${minio.bucket}/
logging: logging:
level: level:
root: info root: info

@ -1,5 +1,6 @@
package com.example.venue_reservation_service; package com.example.venue_reservation_service;
import cn.hutool.core.lang.UUID;
import com.example.venue_reservation_service.controller.UserController; import com.example.venue_reservation_service.controller.UserController;
import com.example.venue_reservation_service.dto.AdminDTO; import com.example.venue_reservation_service.dto.AdminDTO;
import com.example.venue_reservation_service.utils.MD5Util; import com.example.venue_reservation_service.utils.MD5Util;
@ -8,6 +9,7 @@ import jakarta.annotation.Resource;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import org.apache.commons.io.FilenameUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory;
@ -24,7 +26,8 @@ class VenueReservationServiceApplicationTests {
@Test @Test
void contextLoads() throws Exception { void contextLoads() throws Exception {
String extension = FilenameUtils.getExtension("image.jpeg"); // 返回 "jpeg"
System.out.println( UUID.randomUUID().toString().replaceAll("-", ""));
} }

Loading…
Cancel
Save