mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-12 12:52:07 +08:00
1、贴皮库上传图片大小限制;2、全局异常捕获文件请求过大类型避免异步写审计日志oom;2、新增编辑贴皮入参校验修正;
This commit is contained in:
+1
@@ -19,6 +19,7 @@ public class GlobalErrorCodeConstants {
|
||||
// ========== 客户端错误段 ==========
|
||||
|
||||
public static final ErrorCode BAD_REQUEST = new ErrorCode(400, "global.error.bad.request");
|
||||
public static final ErrorCode UPLOAD_SIZE_EXCEEDED = new ErrorCode(413, "global.error.request.upload.size.exceed");
|
||||
public static final ErrorCode REQUEST_ORGAN_ID_NOT_EXIST = new ErrorCode(400, "global.error.request.organId.notExist");
|
||||
public static final ErrorCode NO_PERMISSION_TO_VISIT_ORG = new ErrorCode(400, "global.error.no.permission.to.visit.org");
|
||||
public static final ErrorCode REQUEST_PARAM_TYPE_ERROR = new ErrorCode(400, "global.error.request.param.type.error");
|
||||
|
||||
+20
-1
@@ -28,6 +28,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
|
||||
@@ -90,6 +91,9 @@ public class GlobalExceptionHandler {
|
||||
if (ex instanceof HttpRequestMethodNotSupportedException httpRequestMethodNotSupportedException) {
|
||||
return httpRequestMethodNotSupportedExceptionHandler(httpRequestMethodNotSupportedException);
|
||||
}
|
||||
if (ex instanceof MaxUploadSizeExceededException maxUploadSizeExceededException) {
|
||||
return maxUploadSizeExceededExceptionHandler(maxUploadSizeExceededException);
|
||||
}
|
||||
if (ex instanceof ServiceException serviceException) {
|
||||
return serviceExceptionHandler(serviceException);
|
||||
}
|
||||
@@ -253,6 +257,16 @@ public class GlobalExceptionHandler {
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 multipart 请求超过全局上传限制。此类异常发生在请求体解析阶段,不能进入默认异常日志流程,
|
||||
* 否则错误日志会尝试读取并序列化整个 multipart 请求体,可能导致内存溢出。
|
||||
*/
|
||||
@ExceptionHandler(value = MaxUploadSizeExceededException.class)
|
||||
public CommonResult maxUploadSizeExceededExceptionHandler(MaxUploadSizeExceededException ex) {
|
||||
log.warn("[maxUploadSizeExceededExceptionHandler] multipart request exceeds configured upload limit");
|
||||
return CommonResult.error(UPLOAD_SIZE_EXCEEDED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据库DuplicateKeyException,违反唯一约束异常
|
||||
*
|
||||
@@ -336,7 +350,7 @@ public class GlobalExceptionHandler {
|
||||
errorLog.setRequestUrl(request.getRequestURI());
|
||||
Map<String, Object> requestParams = MapUtil.<String, Object>builder()
|
||||
.put("query", ServletUtils.getParamMap(request))
|
||||
.put("body", ServletUtils.getBody(request)).build();
|
||||
.put("body", isMultipartRequest(request) ? "[multipart body omitted]" : ServletUtils.getBody(request)).build();
|
||||
errorLog.setRequestParams(JsonUtils.toJsonString(requestParams));
|
||||
errorLog.setRequestMethod(request.getMethod());
|
||||
errorLog.setUserAgent(ServletUtils.getUserAgent(request));
|
||||
@@ -344,6 +358,11 @@ public class GlobalExceptionHandler {
|
||||
errorLog.setExceptionTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
private boolean isMultipartRequest(HttpServletRequest request) {
|
||||
String contentType = request.getContentType();
|
||||
return contentType != null && contentType.regionMatches(true, 0, "multipart/", 0, "multipart/".length());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 Table 不存在的异常情况
|
||||
*
|
||||
|
||||
+1
@@ -172,6 +172,7 @@ public class ErrorCodeConstants {
|
||||
public static final ErrorCode VENEER_ORG_CLASSIFICATION_NAME_EMPTY = new ErrorCode(1_003_009_010, "veneer.org.classification.name.empty");
|
||||
public static final ErrorCode VENEER_ORG_CLASSIFICATION_NAME_NOT_EXIST = new ErrorCode(1_003_009_011, "veneer.org.classification.name.not.exist");
|
||||
public static final ErrorCode VENEER_ORG_CLASSIFICATION_NAME_USED = new ErrorCode(1_003_009_012, "veneer.org.classification.name.used");
|
||||
public static final ErrorCode VENEER_PIC_EXCEED_SIZE = new ErrorCode(1_003_009_013, "veneer.pic.exceed.size");
|
||||
|
||||
//=========== 部件管理 1-003-010-000 ============
|
||||
public static final ErrorCode ORDER_COMPONENT_NOT_EXIST = new ErrorCode(1_003_010_000, "order.component.not.exist");
|
||||
|
||||
+7
@@ -1,6 +1,7 @@
|
||||
package com.cf.imes.module.executor.controller.admin.veneer;
|
||||
|
||||
import com.alibaba.excel.EasyExcelFactory;
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.framework.common.pojo.PageParam;
|
||||
import com.cf.imes.framework.common.pojo.PageResult;
|
||||
@@ -37,6 +38,7 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static com.cf.imes.framework.common.pojo.CommonResult.success;
|
||||
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_PIC_EXCEED_SIZE;
|
||||
import static com.cf.imes.framework.operatelog.core.enums.OperateTypeEnum.CREATE;
|
||||
import static com.cf.imes.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.cf.imes.framework.operatelog.core.enums.OperateTypeEnum.IMPORT;
|
||||
@@ -53,6 +55,8 @@ import static com.cf.imes.framework.operatelog.core.enums.OperateTypeEnum.IMPORT
|
||||
@Validated
|
||||
public class VeneerController {
|
||||
|
||||
private static final long VENEER_PIC_MAX_SIZE = 5L * 1024 * 1024;
|
||||
|
||||
@Resource
|
||||
private VeneerService veneerService;
|
||||
|
||||
@@ -119,6 +123,9 @@ public class VeneerController {
|
||||
@OperateLog(logArgs = false, type = CREATE)
|
||||
public CommonResult<Long> uploadVeneerPic(@RequestParam(value = "id", required = false) Long id,
|
||||
@RequestPart(value = "file") MultipartFile file) throws IOException {
|
||||
if (file.getSize() > VENEER_PIC_MAX_SIZE) {
|
||||
throw new ServiceException(VENEER_PIC_EXCEED_SIZE);
|
||||
}
|
||||
return success(veneerService.uploadPic(id, file));
|
||||
}
|
||||
|
||||
|
||||
+20
-3
@@ -1,10 +1,14 @@
|
||||
package com.cf.imes.module.executor.controller.admin.veneer.vo;
|
||||
|
||||
import com.cf.imes.framework.common.validation.InEnum;
|
||||
import com.cf.imes.framework.common.validation.NumberValid;
|
||||
import com.cf.imes.module.executor.enums.veneer.VeneerProductLevelEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@@ -25,6 +29,10 @@ public class VeneerSaveReqVO {
|
||||
@Schema(description = "贴皮编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{veneer.save.veneerNo.not.null}")
|
||||
@Length(max = 32, message = "{veneer.save.veneerNo.length.max}")
|
||||
@Pattern(
|
||||
regexp = "^[^\\\\/:*?\",]*$",
|
||||
message = "{veneer.save.veneerNo.special.character}"
|
||||
)
|
||||
private String veneerNo;
|
||||
|
||||
@Schema(description = "批次号")
|
||||
@@ -47,18 +55,26 @@ public class VeneerSaveReqVO {
|
||||
|
||||
@Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{veneer.save.num.not.null}")
|
||||
@NumberValid(name = "veneer.field.num", integer = 4, fraction = 0)
|
||||
@Min(value = 1, message = "{veneer.save.num.positive}")
|
||||
private Integer num;
|
||||
|
||||
@Schema(description = "宽度", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{veneer.save.width.not.null}")
|
||||
@NumberValid(name = "veneer.field.width", integer = 4, fraction = 2)
|
||||
@DecimalMin(value = "0", inclusive = false, message = "{veneer.save.width.positive}")
|
||||
private BigDecimal width;
|
||||
|
||||
@Schema(description = "长度", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{veneer.save.length.not.null}")
|
||||
@NumberValid(name = "veneer.field.width", integer = 4, fraction = 2)
|
||||
@DecimalMin(value = "0", inclusive = false, message = "{veneer.save.length.positive}")
|
||||
private BigDecimal length;
|
||||
|
||||
@Schema(description = "厚度", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{veneer.save.thickness.not.null}")
|
||||
@NumberValid(name = "veneer.field.width", integer = 4, fraction = 2)
|
||||
@DecimalMin(value = "0", inclusive = false, message = "{veneer.save.thickness.positive}")
|
||||
private BigDecimal thickness;
|
||||
|
||||
@Schema(description = "颜色")
|
||||
@@ -66,7 +82,7 @@ public class VeneerSaveReqVO {
|
||||
private String color;
|
||||
|
||||
@Schema(description = "材料")
|
||||
@Length(max = 64, message = "{veneer.save.material.length.max}")
|
||||
@Length(max = 32, message = "{veneer.save.material.length.max}")
|
||||
private String material;
|
||||
|
||||
@Schema(description = "规格")
|
||||
@@ -74,11 +90,11 @@ public class VeneerSaveReqVO {
|
||||
private String spec;
|
||||
|
||||
@Schema(description = "品牌")
|
||||
@Length(max = 64, message = "{veneer.save.brand.length.max}")
|
||||
@Length(max = 32, message = "{veneer.save.brand.length.max}")
|
||||
private String brand;
|
||||
|
||||
@Schema(description = "厂家")
|
||||
@Length(max = 128, message = "{veneer.save.factory.length.max}")
|
||||
@Length(max = 64, message = "{veneer.save.factory.length.max}")
|
||||
private String factory;
|
||||
|
||||
@Schema(description = "备注", example = "备注")
|
||||
@@ -86,5 +102,6 @@ public class VeneerSaveReqVO {
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "贴皮照片文件id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{veneer.save.fileId.not.null}")
|
||||
private Long fileId;
|
||||
}
|
||||
|
||||
+13
-3
@@ -7,13 +7,23 @@ page.pageSize.min=每頁條數最小值為 -1
|
||||
page.pageSize.notNull=每頁條數不能為空
|
||||
|
||||
veneer.save.batchNo.length.max=批次號不能超過32個字元
|
||||
veneer.save.brand.length.max=品牌不能超過64個字元
|
||||
veneer.save.brand.length.max=品牌不能超過32個字元
|
||||
veneer.save.classificationName.not.null=分類名稱不能為空
|
||||
veneer.save.color.length.max=顏色不能超過32個字元
|
||||
veneer.save.factory.length.max=廠家不能超過128個字元
|
||||
veneer.save.factory.length.max=廠家不能超過64個字元
|
||||
veneer.save.length.not.null=長度不能為空
|
||||
veneer.save.material.length.max=材質不能超過64個字元
|
||||
veneer.save.material.length.max=材質不能超過32個字元
|
||||
veneer.save.veneerNo.special.character=貼皮編號不能包含特殊字符
|
||||
veneer.save.fileId.not.null=請上傳貼皮照片
|
||||
veneer.field.num=數量
|
||||
veneer.field.width=寬度
|
||||
veneer.field.length=長度
|
||||
veneer.field.thickness=厚度
|
||||
veneer.save.num.not.null=數量不能為空
|
||||
veneer.save.num.positive=數量必須為正整數
|
||||
veneer.save.width.positive=寬度必須大於 0
|
||||
veneer.save.length.positive=長度必須大於 0
|
||||
veneer.save.thickness.positive=厚度必須大於 0
|
||||
veneer.save.productLevel.not.null=產品等級不能為空
|
||||
veneer.save.remark.length.max=備註不能超過255個字元
|
||||
veneer.save.spec.length.max=規格不能超過64個字元
|
||||
|
||||
+13
-3
@@ -7,13 +7,23 @@ page.pageSize.min=Page size must be at least -1
|
||||
page.pageSize.notNull=Page size cannot be empty
|
||||
|
||||
veneer.save.batchNo.length.max=batch no length cannot exceed 32 characters
|
||||
veneer.save.brand.length.max=brand length cannot exceed 64 characters
|
||||
veneer.save.brand.length.max=brand length cannot exceed 32 characters
|
||||
veneer.save.veneerNo.special.character=veneer number cannot contain special characters
|
||||
veneer.save.fileId.not.null=please upload a veneer image
|
||||
veneer.field.num=quantity
|
||||
veneer.field.width=width
|
||||
veneer.field.length=length
|
||||
veneer.field.thickness=thickness
|
||||
veneer.save.classificationName.not.null=classification name cannot be empty
|
||||
veneer.save.color.length.max=color length cannot exceed 32 characters
|
||||
veneer.save.factory.length.max=factory length cannot exceed 128 characters
|
||||
veneer.save.factory.length.max=factory length cannot exceed 64 characters
|
||||
veneer.save.length.not.null=length cannot be empty
|
||||
veneer.save.material.length.max=material length cannot exceed 64 characters
|
||||
veneer.save.material.length.max=material length cannot exceed 32 characters
|
||||
veneer.save.num.not.null=num cannot be empty
|
||||
veneer.save.num.positive=num must be a positive integer
|
||||
veneer.save.width.positive=width must be greater than 0
|
||||
veneer.save.length.positive=length must be greater than 0
|
||||
veneer.save.thickness.positive=thickness must be greater than 0
|
||||
veneer.save.productLevel.not.null=product level cannot be empty
|
||||
veneer.save.remark.length.max=remark length cannot exceed 255 characters
|
||||
veneer.save.spec.length.max=spec length cannot exceed 64 characters
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
global.error.request.param.missing=請求參數缺失:{0}
|
||||
global.error.bad.request=請求參數不正確:{0}
|
||||
global.error.request.upload.size.exceed=上傳檔案大小超過限制
|
||||
global.error.request.param.type.error=請求參數類型錯誤:{0}
|
||||
global.error.request.not.found=請求未找到:{0}
|
||||
global.error.request.method.error=請求方法不正確:{0}
|
||||
@@ -82,6 +83,7 @@ veneer.org.classification.name.not.exist=組織下分類名稱不存在,請重
|
||||
veneer.org.classification.name.used=目前分類名稱已被貼皮使用,無法刪除
|
||||
veneer.import.template.download.error=導入模版下載失敗
|
||||
veneer.import.org.classification.name.not.exist=第{0}行分類名稱{1}不存在,請前往[新增]視窗維護分類名稱
|
||||
veneer.pic.exceed.size=貼皮圖片大小不能超過 5MB
|
||||
|
||||
order.component.not.exist=訂單部件不存在
|
||||
order.component.delete.plate.in.plan=部件下存在已排單的板件,請移除部件後再次進行删除
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
global.error.request.param.missing=Missing request parameter: {0}
|
||||
global.error.bad.request=Invalid request parameter: {0}
|
||||
global.error.request.upload.size.exceed=Uploaded file size exceeds the limit
|
||||
global.error.request.param.type.error=Request parameter type mismatch: {0}
|
||||
global.error.request.not.found=Request not found: {0}
|
||||
global.error.request.method.error=Incorrect request method: {0}
|
||||
@@ -82,6 +83,7 @@ veneer.org.classification.name.not.exist=The classification name does not exist
|
||||
veneer.org.classification.name.used=The classification name is already used by veneer and cannot be deleted
|
||||
veneer.import.template.download.error=Import template download failed
|
||||
veneer.import.org.classification.name.not.exist=The classification name {1} does not exist in line {0}, please go to the [Add] window to maintain the classification name
|
||||
veneer.pic.exceed.size=Veneer image size cannot exceed 5MB
|
||||
|
||||
order.component.not.exist=The order component does not exist
|
||||
order.component.delete.plate.in.plan=There are already listed boards under the component. Please remove the component and delete it again
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
global.error.request.param.missing=请求参数缺失:{0}
|
||||
global.error.bad.request=请求参数不正确:{0}
|
||||
global.error.request.upload.size.exceed=上传文件大小超过限制
|
||||
global.error.request.param.type.error=请求参数类型错误:{0}
|
||||
global.error.request.not.found=请求未找到:{0}
|
||||
global.error.request.method.error=请求方法不正确:{0}
|
||||
@@ -82,6 +83,7 @@ veneer.org.classification.name.not.exist=组织下分类名称不存在,请刷
|
||||
veneer.org.classification.name.used=当前分类名称已被贴皮使用,无法删除
|
||||
veneer.import.template.download.error=导入模版下载失败
|
||||
veneer.import.org.classification.name.not.exist=第{0}行分类名称{1}不存在,请前往 [新增] 窗口维护分类名称
|
||||
veneer.pic.exceed.size=贴皮图片大小不能超过 5MB
|
||||
|
||||
order.component.not.exist=订单部件不存在
|
||||
order.component.delete.plate.in.plan=部件下存在已排单的板件,请移除部件后再次进行删除
|
||||
|
||||
+13
-3
@@ -7,13 +7,23 @@ page.pageSize.min=每页条数最小值为 -1
|
||||
page.pageSize.notNull=每页条数不能为空
|
||||
|
||||
veneer.save.batchNo.length.max=批次号不能超过32个字符
|
||||
veneer.save.brand.length.max=品牌不能超过64个字符
|
||||
veneer.save.brand.length.max=品牌不能超过32个字符
|
||||
veneer.save.classificationName.not.null=分类名称不能为空
|
||||
veneer.save.color.length.max=颜色不能超过32个字符
|
||||
veneer.save.factory.length.max=厂家不能超过128个字符
|
||||
veneer.save.factory.length.max=厂家不能超过64个字符
|
||||
veneer.save.length.not.null=长度不能为空
|
||||
veneer.save.material.length.max=材料不能超过64个字符
|
||||
veneer.save.material.length.max=材料不能超过32个字符
|
||||
veneer.save.veneerNo.special.character=贴皮编号不能包含特殊字符
|
||||
veneer.save.fileId.not.null=请上传贴皮照片
|
||||
veneer.field.num=数量
|
||||
veneer.field.width=宽度
|
||||
veneer.field.length=长度
|
||||
veneer.field.thickness=厚度
|
||||
veneer.save.num.not.null=数量不能为空
|
||||
veneer.save.num.positive=数量必须为正整数
|
||||
veneer.save.width.positive=宽度必须大于 0
|
||||
veneer.save.length.positive=长度必须大于 0
|
||||
veneer.save.thickness.positive=厚度必须大于 0
|
||||
veneer.save.productLevel.not.null=产品等级不能为空
|
||||
veneer.save.remark.length.max=备注不能超过255个字符
|
||||
veneer.save.spec.length.max=规格不能超过64个字符
|
||||
|
||||
+12
@@ -33,10 +33,12 @@ import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
@@ -170,6 +172,16 @@ class VeneerControllerTest extends BaseWebUnitTest {
|
||||
.andExpect(jsonPath("$.data").value(10L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void uploadVeneerPic_exceedMaxSize() throws Exception {
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "large.jpg", "image/jpeg", new byte[5 * 1024 * 1024 + 1]
|
||||
);
|
||||
|
||||
assertThrows(Exception.class, () -> mockMvc.perform(multipart("/executor/veneer/pic").file(file)));
|
||||
verifyNoInteractions(veneerService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void importExcel_success() throws Exception {
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
|
||||
Reference in New Issue
Block a user