From 9e465b1ebc84d518e39ca982489d35066c9dc05d Mon Sep 17 00:00:00 2001 From: chenyuepan <3158614516@qq.com> Date: Thu, 19 Jun 2025 08:44:55 +0800 Subject: [PATCH] =?UTF-8?q?netty=E5=AE=9E=E7=8E=B0=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 21 +++ .../VenueReservationServiceApplication.java | 9 +- .../domain/VeException.java | 67 +++++++ .../exception/GlobalExceptionHandler.java | 90 +++++++++ .../exception/ImageValidateException.java | 16 ++ .../exception/MinioRemoveException.java | 8 + .../exception/MinioUploadException.java | 8 + .../mapper/VeExceptionMapper.java | 18 ++ .../netty/ChatServerHandler.java | 177 ++++++++++++++++++ .../netty/NettyChatServer.java | 67 +++++++ .../service/VeExceptionService.java | 13 ++ .../service/impl/UserServiceImpl.java | 58 +++--- .../service/impl/VeExceptionServiceImpl.java | 22 +++ .../utils/ImageValidator.java | 34 ++++ src/main/resources/application.yml | 2 + .../resources/mapper/VeExceptionMapper.xml | 23 +++ .../dcpint/icdcshare/IfsDcpIcdcShareTest.java | 21 ++- 17 files changed, 613 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/example/venue_reservation_service/domain/VeException.java create mode 100644 src/main/java/com/example/venue_reservation_service/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/example/venue_reservation_service/exception/ImageValidateException.java create mode 100644 src/main/java/com/example/venue_reservation_service/exception/MinioRemoveException.java create mode 100644 src/main/java/com/example/venue_reservation_service/exception/MinioUploadException.java create mode 100644 src/main/java/com/example/venue_reservation_service/mapper/VeExceptionMapper.java create mode 100644 src/main/java/com/example/venue_reservation_service/netty/ChatServerHandler.java create mode 100644 src/main/java/com/example/venue_reservation_service/netty/NettyChatServer.java create mode 100644 src/main/java/com/example/venue_reservation_service/service/VeExceptionService.java create mode 100644 src/main/java/com/example/venue_reservation_service/service/impl/VeExceptionServiceImpl.java create mode 100644 src/main/resources/mapper/VeExceptionMapper.xml diff --git a/pom.xml b/pom.xml index 2e63fcd..9ebbc55 100644 --- a/pom.xml +++ b/pom.xml @@ -181,6 +181,27 @@ org.springframework.boot spring-boot-starter-webflux + + org.springframework.boot + spring-boot-starter-cache + + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + + + + io.netty + netty-all + 4.1.86.Final + + + + com.alibaba + fastjson + 2.0.34 + diff --git a/src/main/java/com/example/venue_reservation_service/VenueReservationServiceApplication.java b/src/main/java/com/example/venue_reservation_service/VenueReservationServiceApplication.java index 24db822..16ff701 100644 --- a/src/main/java/com/example/venue_reservation_service/VenueReservationServiceApplication.java +++ b/src/main/java/com/example/venue_reservation_service/VenueReservationServiceApplication.java @@ -1,24 +1,19 @@ package com.example.venue_reservation_service; -import com.example.venue_reservation_service.utils.ScheduleUtil; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import jakarta.annotation.Resource; import org.mybatis.spring.annotation.MapperScan; -import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.http.MediaType; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.client.RestTemplate; - -import java.time.LocalDate; -import java.time.LocalTime; import java.util.Arrays; import java.util.TimeZone; @@ -30,7 +25,7 @@ public class VenueReservationServiceApplication { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("GMT+08:00")); - SpringApplication.run(VenueReservationServiceApplication.class, args); + ConfigurableApplicationContext context = SpringApplication.run(VenueReservationServiceApplication.class, args); System.out.println("(*^▽^*)启动成功!!!(〃'▽'〃)"); } diff --git a/src/main/java/com/example/venue_reservation_service/domain/VeException.java b/src/main/java/com/example/venue_reservation_service/domain/VeException.java new file mode 100644 index 0000000..93a2f1d --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/domain/VeException.java @@ -0,0 +1,67 @@ +package com.example.venue_reservation_service.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * 系统异常日志表 + * @TableName venue_exception + */ +@TableName(value ="venue_exception") +@Data +public class VeException implements Serializable { + /** + * 主键ID + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 异常类型 + */ + private String exceptionType; + + /** + * 异常信息 + */ + private String exceptionMessage; + + /** + * 异常堆栈 + */ + private String exceptionStack; + + /** + * 方法名称 + */ + private String methodName; + + /** + * 参数数据 + */ + private String parameterData; + + /** + * 用户ID + */ + private Integer userId; + + /** + * 操作时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime operationTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/exception/GlobalExceptionHandler.java b/src/main/java/com/example/venue_reservation_service/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..e1e156a --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/exception/GlobalExceptionHandler.java @@ -0,0 +1,90 @@ +package com.example.venue_reservation_service.exception; + +import com.example.venue_reservation_service.domain.VeException; +import com.example.venue_reservation_service.service.VeExceptionService; +import com.example.venue_reservation_service.vo.Result; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerMapping; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.time.LocalDateTime; +import java.util.Map; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + @Autowired + private VeExceptionService exceptionService; + + // 文件移除异常处理 + @ExceptionHandler(MinioRemoveException.class) + public Result handleMinioRemoveException(MinioRemoveException e) { + log.error("文件移除失败: {}", e.getMessage()); + recordException(e, "MinioRemoveException"); + return Result.fail().message("旧头像删除失败,请重试"); + } + + // 文件校验异常处理 + @ExceptionHandler(ImageValidateException.class) + public Result handleImageValidateException(ImageValidateException e) { + String errorMsg = "FILE_SIZE".equals(e.getErrorType()) + ? "文件大小超过250MB限制" : "不支持的文件类型"; + log.error("文件校验失败: {} - {}", e.getErrorType(), e.getMessage()); + recordException(e, "ImageValidateException"); + return Result.fail().message(errorMsg); + } + + // 文件上传异常处理 + @ExceptionHandler(MinioUploadException.class) + public Result handleMinioUploadException(MinioUploadException e) { + log.error("文件上传失败: {}", e.getMessage()); + recordException(e, "MinioUploadException"); + return Result.fail().message("头像上传失败,请稍后重试"); + } + + // 记录异常到数据库 + private void recordException(Exception e, String exceptionType) { + try { + HttpServletRequest request = ((ServletRequestAttributes) + RequestContextHolder.currentRequestAttributes()).getRequest(); + + HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute( + HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE); + + VeException logEntity = new VeException(); + logEntity.setExceptionType(exceptionType); + logEntity.setExceptionMessage(e.getMessage()); + + // 获取堆栈信息 + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + logEntity.setExceptionStack(sw.toString()); + + logEntity.setMethodName(handlerMethod.getMethod().getName()); + + // 获取参数数据 + Map params = request.getParameterMap(); + logEntity.setParameterData(params.toString()); + + // 从请求中获取用户ID + String userId = request.getParameter("userId"); + if (userId != null) { + logEntity.setUserId(Integer.parseInt(userId)); + } + + logEntity.setOperationTime(LocalDateTime.now()); + + exceptionService.save(logEntity); + } catch (Exception ex) { + log.error("记录异常日志失败: {}", ex.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/exception/ImageValidateException.java b/src/main/java/com/example/venue_reservation_service/exception/ImageValidateException.java new file mode 100644 index 0000000..a92e02d --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/exception/ImageValidateException.java @@ -0,0 +1,16 @@ +package com.example.venue_reservation_service.exception; + +// 文件校验异常(包含文件过大和非图片文件) +public class ImageValidateException extends RuntimeException { + private final String errorType; + + public ImageValidateException(String message, String errorType) { + super(message); + this.errorType = errorType; + } + + public String getErrorType() { + return errorType; + } +} + diff --git a/src/main/java/com/example/venue_reservation_service/exception/MinioRemoveException.java b/src/main/java/com/example/venue_reservation_service/exception/MinioRemoveException.java new file mode 100644 index 0000000..6e407d4 --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/exception/MinioRemoveException.java @@ -0,0 +1,8 @@ +package com.example.venue_reservation_service.exception; + +// 文件移除异常 +public class MinioRemoveException extends RuntimeException { + public MinioRemoveException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/venue_reservation_service/exception/MinioUploadException.java b/src/main/java/com/example/venue_reservation_service/exception/MinioUploadException.java new file mode 100644 index 0000000..41af926 --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/exception/MinioUploadException.java @@ -0,0 +1,8 @@ +package com.example.venue_reservation_service.exception; + +// 文件上传异常 +public class MinioUploadException extends RuntimeException { + public MinioUploadException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/mapper/VeExceptionMapper.java b/src/main/java/com/example/venue_reservation_service/mapper/VeExceptionMapper.java new file mode 100644 index 0000000..8a70108 --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/mapper/VeExceptionMapper.java @@ -0,0 +1,18 @@ +package com.example.venue_reservation_service.mapper; + +import com.example.venue_reservation_service.domain.VeException; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author 31586 +* @description 针对表【venue_exception(系统异常日志表)】的数据库操作Mapper +* @createDate 2025-06-18 09:39:17 +* @Entity generator.domain.VeException +*/ +public interface VeExceptionMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/example/venue_reservation_service/netty/ChatServerHandler.java b/src/main/java/com/example/venue_reservation_service/netty/ChatServerHandler.java new file mode 100644 index 0000000..dbf2ac2 --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/netty/ChatServerHandler.java @@ -0,0 +1,177 @@ +package com.example.venue_reservation_service.netty; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.util.concurrent.GlobalEventExecutor; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; + +public class ChatServerHandler extends SimpleChannelInboundHandler { + + private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + private static final Map userChannelMap = new HashMap<>(); + private static final Map roleMap = new HashMap<>(); + + static { + roleMap.put(0, "普通用户"); + roleMap.put(1, "普通用户"); + roleMap.put(2, "普通用户"); + roleMap.put(3, "系统管理员"); + roleMap.put(4, "超级管理员"); + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + Channel incoming = ctx.channel(); + channels.add(incoming); + System.out.println("New client connected: " + incoming.remoteAddress()); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + Channel incoming = ctx.channel(); + String username = null; + + // 查找并移除用户 + for (Map.Entry entry : userChannelMap.entrySet()) { + if (entry.getValue().equals(incoming)) { + username = entry.getKey(); + break; + } + } + + if (username != null) { + userChannelMap.remove(username); + broadcastMessage("系统通知", username + " 已退出聊天", 3); + } + + channels.remove(incoming); + System.out.println("Client disconnected: " + incoming.remoteAddress()); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception { + String request = frame.text(); + Channel incoming = ctx.channel(); + + try { + JSONObject jsonObject = JSON.parseObject(request); + String type = jsonObject.getString("type"); + + if ("login".equals(type)) { + handleLogin(incoming, jsonObject); + } else if ("message".equals(type)) { + handleMessage(incoming, jsonObject); + } else { + sendErrorMessage(incoming, "未知消息类型: " + type); + } + } catch (Exception e) { + System.err.println("Error processing message: " + e.getMessage()); + e.printStackTrace(); + sendErrorMessage(incoming, "消息格式错误"); + } + } + + private void handleLogin(Channel incoming, JSONObject jsonObject) { + String username = jsonObject.getString("username"); + int userRole = jsonObject.getIntValue("userRole"); + + if (username == null || username.isEmpty()) { + sendErrorMessage(incoming, "用户名不能为空"); + return; + } + + if (userChannelMap.containsKey(username)) { + sendErrorMessage(incoming, "用户名已被使用"); + return; + } + + userChannelMap.put(username, incoming); + String roleName = roleMap.getOrDefault(userRole, "未知角色"); + broadcastMessage("系统通知", username + " (" + roleName + ") 加入了聊天", 3); + System.out.println(username + " logged in with role: " + roleName); + } + + private void handleMessage(Channel incoming, JSONObject jsonObject) { + String username = jsonObject.getString("username"); + int userRole = jsonObject.getIntValue("userRole"); + String content = jsonObject.getString("content"); + + if (content == null || content.isEmpty()) { + return; + } + + if (!userChannelMap.containsKey(username) || !userChannelMap.get(username).equals(incoming)) { + sendErrorMessage(incoming, "用户未登录"); + return; + } + + String roleName = roleMap.getOrDefault(userRole, "未知角色"); + String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + + JSONObject messageJson = new JSONObject(); + messageJson.put("type", "message"); + messageJson.put("sender", username); + messageJson.put("senderRole", userRole); + messageJson.put("roleName", roleName); + messageJson.put("content", content); + messageJson.put("timestamp", timestamp); + + broadcastMessage(messageJson.toJSONString()); + } + + private void broadcastMessage(String message) { + for (Channel channel : channels) { + channel.writeAndFlush(new TextWebSocketFrame(message)); + } + } + + private void broadcastMessage(String from, String content, int role) { + JSONObject messageJson = new JSONObject(); + messageJson.put("type", "system"); + messageJson.put("sender", from); + messageJson.put("senderRole", role); + messageJson.put("message", content); + messageJson.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + + broadcastMessage(messageJson.toJSONString()); + } + + private void sendErrorMessage(Channel channel, String message) { + JSONObject errorJson = new JSONObject(); + errorJson.put("type", "error"); + errorJson.put("message", message); + channel.writeAndFlush(new TextWebSocketFrame(errorJson.toJSONString())); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + Channel channel = ctx.channel(); + String username = null; + + // 查找并移除用户 + for (Map.Entry entry : userChannelMap.entrySet()) { + if (entry.getValue().equals(channel)) { + username = entry.getKey(); + break; + } + } + + if (username != null) { + userChannelMap.remove(username); + broadcastMessage("系统通知", username + " 连接异常断开", 3); + } + + cause.printStackTrace(); + ctx.close(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/netty/NettyChatServer.java b/src/main/java/com/example/venue_reservation_service/netty/NettyChatServer.java new file mode 100644 index 0000000..9aa17af --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/netty/NettyChatServer.java @@ -0,0 +1,67 @@ +package com.example.venue_reservation_service.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; +import io.netty.handler.stream.ChunkedWriteHandler; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class NettyChatServer { + + @Value("${server.netty.port}") + private int port; + + private EventLoopGroup bossGroup; + private EventLoopGroup workerGroup; + + @PostConstruct + public void start() throws Exception { + bossGroup = new NioEventLoopGroup(); + workerGroup = new NioEventLoopGroup(); + + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 100) + .childOption(ChannelOption.SO_KEEPALIVE, true) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast( + new HttpServerCodec(), + new HttpObjectAggregator(65536), + new ChunkedWriteHandler(), + new WebSocketServerCompressionHandler(), + new WebSocketServerProtocolHandler("/ws", null, true), + new ChatServerHandler() + ); + } + }); + + ChannelFuture f = b.bind(port).sync(); + System.out.println("Netty Chat Server started and listen on port " + port); + } + + @PreDestroy + public void stop() { + if (bossGroup != null) { + bossGroup.shutdownGracefully(); + } + if (workerGroup != null) { + workerGroup.shutdownGracefully(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/service/VeExceptionService.java b/src/main/java/com/example/venue_reservation_service/service/VeExceptionService.java new file mode 100644 index 0000000..96ee9a2 --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/service/VeExceptionService.java @@ -0,0 +1,13 @@ +package com.example.venue_reservation_service.service; + +import com.example.venue_reservation_service.domain.VeException; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author 31586 +* @description 针对表【venue_exception(系统异常日志表)】的数据库操作Service +* @createDate 2025-06-18 09:39:17 +*/ +public interface VeExceptionService extends IService { + +} diff --git a/src/main/java/com/example/venue_reservation_service/service/impl/UserServiceImpl.java b/src/main/java/com/example/venue_reservation_service/service/impl/UserServiceImpl.java index 34bc38b..8efdef6 100644 --- a/src/main/java/com/example/venue_reservation_service/service/impl/UserServiceImpl.java +++ b/src/main/java/com/example/venue_reservation_service/service/impl/UserServiceImpl.java @@ -1,16 +1,16 @@ package com.example.venue_reservation_service.service.impl; import cn.hutool.core.lang.UUID; -import cn.hutool.jwt.JWTUtil; -import com.auth0.jwt.JWT; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.venue_reservation_service.domain.*; import com.example.venue_reservation_service.dto.*; +import com.example.venue_reservation_service.exception.ImageValidateException; +import com.example.venue_reservation_service.exception.MinioRemoveException; +import com.example.venue_reservation_service.exception.MinioUploadException; import com.example.venue_reservation_service.mapper.AdminMapper; import com.example.venue_reservation_service.service.*; import com.example.venue_reservation_service.mapper.UserMapper; @@ -21,20 +21,15 @@ import com.example.venue_reservation_service.vo.Result; import com.github.yulichang.wrapper.MPJLambdaWrapper; import jakarta.annotation.Resource; import org.apache.commons.io.FilenameUtils; -import org.checkerframework.checker.units.qual.A; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; - import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.Optional; - /** * @author 31586 * @description 针对表【venue_user】的数据库操作Service实现 @@ -353,34 +348,47 @@ public class UserServiceImpl extends ServiceImpl 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("服务错误,请稍后重试"); + + try { + // 检查是否上传过头像 + if(user.getIsUpload() == 1) { + try { + minioUtil.removeFile(user.getAvatar()); + } catch (Exception e) { + throw new MinioRemoveException("删除旧文件失败: " + e.getMessage()); + } } - } - if (ImageValidator.validateImageFile(file)) { - // 生成新的名称 - String extension = FilenameUtils.getExtension(file.getOriginalFilename()); // 获取文件扩展名 + + // 文件校验(修改为抛出异常) + ImageValidator.validateImageFileWithException(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("服务错误,图片上传失败,请稍后重试"); + } catch (Exception e) { + // 上传失败时重置用户状态 + user.setIsUpload(0); + updateById(user); + throw new MinioUploadException("文件上传失败: " + e.getMessage()); } + + // 更新用户信息 user.setIsUpload(1); user.setAvatar(filename); updateById(user); - return Result.ok(imgUrl + user.getAvatar()).message("头像图片上传成功"); + return Result.ok(imgUrl + user.getAvatar()).message("头像上传成功"); + + } catch (MinioRemoveException | ImageValidateException | MinioUploadException e) { + throw e; // 抛出给全局异常处理器 + } catch (Exception e) { + log.error("系统未知错误: {}" + e.getMessage()); + return Result.fail().message("系统错误,请稍后重试"); } - return Result.fail().message("非图片文件或文件过大"); } + @Override public Result getUserInfo(Integer userId) { User user = getById(userId); diff --git a/src/main/java/com/example/venue_reservation_service/service/impl/VeExceptionServiceImpl.java b/src/main/java/com/example/venue_reservation_service/service/impl/VeExceptionServiceImpl.java new file mode 100644 index 0000000..dbf0521 --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/service/impl/VeExceptionServiceImpl.java @@ -0,0 +1,22 @@ +package com.example.venue_reservation_service.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.example.venue_reservation_service.mapper.VeExceptionMapper; +import com.example.venue_reservation_service.service.VeExceptionService; +import com.example.venue_reservation_service.domain.VeException; +import org.springframework.stereotype.Service; + +/** +* @author 31586 +* @description 针对表【venue_exception(系统异常日志表)】的数据库操作Service实现 +* @createDate 2025-06-18 09:39:17 +*/ +@Service +public class VeExceptionServiceImpl extends ServiceImpl + implements VeExceptionService { + +} + + + + diff --git a/src/main/java/com/example/venue_reservation_service/utils/ImageValidator.java b/src/main/java/com/example/venue_reservation_service/utils/ImageValidator.java index 4c8d148..c507f38 100644 --- a/src/main/java/com/example/venue_reservation_service/utils/ImageValidator.java +++ b/src/main/java/com/example/venue_reservation_service/utils/ImageValidator.java @@ -1,5 +1,6 @@ package com.example.venue_reservation_service.utils; +import com.example.venue_reservation_service.exception.ImageValidateException; import org.springframework.web.multipart.MultipartFile; import javax.imageio.ImageIO; import javax.imageio.ImageReader; @@ -15,6 +16,39 @@ public class ImageValidator { // 250MB 字节限制 (250 * 1024 * 1024) private static final long MAX_SIZE_BYTES = 262144000L; + // 修改后的文件校验方法(抛出异常) + public static void validateImageFileWithException(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new ImageValidateException("文件为空", "FILE_EMPTY"); + } + + if (file.getSize() > ImageValidator.MAX_SIZE_BYTES) { + throw new ImageValidateException("文件大小超过限制", "FILE_SIZE"); + } + + String originalName = file.getOriginalFilename(); + if (originalName == null || originalName.lastIndexOf(".") == -1) { + throw new ImageValidateException("文件无扩展名", "FILE_EXT"); + } + + String extension = originalName.substring(originalName.lastIndexOf(".") + 1).toLowerCase(); + if (!ImageValidator.ALLOWED_EXTENSIONS.contains(extension)) { + throw new ImageValidateException("不支持的文件类型", "FILE_TYPE"); + } + + try (ImageInputStream iis = ImageIO.createImageInputStream(file.getInputStream())) { + if (iis == null) { + throw new ImageValidateException("无法读取文件内容", "FILE_READ"); + } + Iterator readers = ImageIO.getImageReaders(iis); + if (!readers.hasNext()) { + throw new ImageValidateException("不是有效的图片文件", "FILE_FORMAT"); + } + } catch (IOException e) { + throw new ImageValidateException("文件读取失败: " + e.getMessage(), "FILE_IO"); + } + } + /** * 验证是否为图片文件且大小<250MB * @param file MultipartFile对象 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c43b36f..683f9ec 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,8 @@ server: port: 9020 # servlet: # context-path: /api + netty: + port: 9030 #localhosturl: 172.16.6.53 # 学校服务器 localhosturl: 119.29.191.232 # 双创服务器 diff --git a/src/main/resources/mapper/VeExceptionMapper.xml b/src/main/resources/mapper/VeExceptionMapper.xml new file mode 100644 index 0000000..9304ec2 --- /dev/null +++ b/src/main/resources/mapper/VeExceptionMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + id,exception_type,exception_message, + exception_stack,method_name,parameter_data, + user_id,operation_time + + diff --git a/src/test/java/com/example/venue_reservation_service/dcpint/icdcshare/IfsDcpIcdcShareTest.java b/src/test/java/com/example/venue_reservation_service/dcpint/icdcshare/IfsDcpIcdcShareTest.java index 11ed60a..29b1a8b 100644 --- a/src/test/java/com/example/venue_reservation_service/dcpint/icdcshare/IfsDcpIcdcShareTest.java +++ b/src/test/java/com/example/venue_reservation_service/dcpint/icdcshare/IfsDcpIcdcShareTest.java @@ -22,16 +22,19 @@ public class IfsDcpIcdcShareTest { String param="" + "IcdcShare" //+ "getUnit" - + "08192ec57d80193b9fa4b9a0" + + "cgyy_dcpint_getJbxx" +// + "getJzgjcxx2" + +"08192ec57d80193b9fa4b9a0" //+"pageSize=8&pageNo=1" - +"STUDENTID=1966&pageSize=5&pageNo=1" +// +"GH=1966&pageSize=5&pageNo=1" //+"STUDENTID=2017" + ""; param= IfsDcpIntUtils.desEncode(param); Map params = new HashMap(); //注意:参数名"clienttype","name"和"param"不能变 params.put("clienttype", "java"); - params.put("name", "thirdpartytest"); +// params.put("name", "thirdpartytest"); + params.put("name", "getJzgjcxx2"); params.put("param", param); /*返回值说明 (1)正确的返回为:{"success":true,"message":true} @@ -40,13 +43,13 @@ public class IfsDcpIcdcShareTest { 具体判断可以简单以返回串不包含false字符串为成功标志。 */ String result = IfsDcpIntUtils.httpPostRequest(params); + System.out.println(result); // System.out.println("result:" + result); - ObjectMapper mapper = new ObjectMapper(); - ResponseResult responseResult = mapper.readValue(result, ResponseResult.class); - for (Unit unit : responseResult.getMessage()) { - System.out.println(unit); - } - System.out.println(responseResult.getPage()); +// ResponseResult responseResult = mapper.readValue(result, ResponseResult.class); +// for (Unit unit : responseResult.getMessage()) { +// System.out.println(unit); +// } +// System.out.println(responseResult.getPage()); // result }