commit:集成Minio

This commit is contained in:
Jerry
2020-10-19 22:53:01 +08:00
parent c7bb5fd289
commit dc3a8ecfac
521 changed files with 6373 additions and 5339 deletions

View File

@@ -12,7 +12,7 @@ import java.util.Date;
* Controller的环绕拦截类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@ControllerAdvice
public class MyControllerAdvice {

View File

@@ -6,6 +6,7 @@ import com.orange.demo.common.core.exception.InvalidDataModelException;
import com.orange.demo.common.core.constant.ErrorCodeEnum;
import com.orange.demo.common.core.exception.RedisCacheAccessException;
import com.orange.demo.common.core.object.ResponseResult;
import com.orange.demo.common.core.util.ContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.PersistenceException;
import org.springframework.dao.DataAccessException;
@@ -15,6 +16,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeoutException;
/**
@@ -22,7 +24,7 @@ import java.util.concurrent.TimeoutException;
* 用不同的函数,处理不同类型的异常。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
@RestControllerAdvice("com.orange.demo")
@@ -38,7 +40,8 @@ public class MyExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseResult<Void> exceptionHandle(Exception ex, HttpServletRequest request) {
log.error("Unhandled exception from URL [" + request.getRequestURI() + "]", ex);
return ResponseResult.error(ErrorCodeEnum.UNHANDLED_EXCEPTION);
ContextUtil.getHttpResponse().setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return ResponseResult.error(ErrorCodeEnum.UNHANDLED_EXCEPTION, ex.getMessage());
}
/**

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记逻辑删除字段。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记更新字段。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记Service所依赖的数据源类型。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -9,7 +9,7 @@ import java.lang.annotation.Target;
* 标记Controller中的方法参数参数解析器会根据该注解将请求中的JSON数据映射到参数中的绑定字段。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记无需Token验证的接口
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 标识Model和常量字典之间的关联关系。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -8,7 +8,7 @@ import java.lang.annotation.*;
* 标识Model之间的字典关联关系。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 标注多对多的Model关系。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -8,7 +8,7 @@ import java.lang.annotation.*;
* 主要用于多对多的Model关系。标注通过从表关联字段或者关联表关联字段计算主表聚合计算字段的规则。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -8,7 +8,7 @@ import java.lang.annotation.*;
* 主要用于一对多的Model关系。标注通过从表关联字段计算主表聚合计算字段的规则。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -8,7 +8,7 @@ import java.lang.annotation.*;
* 标识Model之间的一对一关联关系。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -0,0 +1,24 @@
package com.orange.demo.common.core.annotation;
import com.orange.demo.common.core.upload.UploadStoreTypeEnum;
import java.lang.annotation.*;
/**
* 用于标记支持数据上传和下载的字段。
*
* @author Jerry
* @date 2020-10-19
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UploadFlagColumn {
/**
* 上传数据存储类型。
*
* @return 上传数据存储类型。
*/
UploadStoreTypeEnum storeType();
}

View File

@@ -9,7 +9,6 @@ import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
@@ -27,7 +26,7 @@ import java.util.List;
* 记录接口的链路traceId、请求参数、应答数据、错误信息和调用时长。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Aspect
@Component
@@ -59,7 +58,6 @@ public class AccessLogAspect {
long start = System.currentTimeMillis();
// 获取方法参数
List<Object> httpReqArgs = new ArrayList<>();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Object[] args = joinPoint.getArgs();
for (Object object : args) {
if (!(object instanceof HttpServletRequest)

View File

@@ -14,7 +14,7 @@ import java.util.Set;
* @param <D> 主DomainDto域数据对象类型。
* @param <K> 主键类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public interface BaseClient<D, K> {

View File

@@ -33,7 +33,7 @@ import java.util.stream.Collectors;
* @param <D> 主DomainDto域对象类型。
* @param <K> 主键类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public abstract class BaseController<M, D, K> {

View File

@@ -14,7 +14,7 @@ import java.util.Map;
*
* @param <M> 主Model实体对象。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@RegisterMapper
public interface BaseDaoMapper<M> extends Mapper<M>, InsertListMapper<M> {

View File

@@ -12,7 +12,7 @@ import java.util.stream.Collectors;
* @param <D> Domain域对象类型。
* @param <M> Model实体对象类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public interface BaseModelMapper<D, M> {

View File

@@ -8,7 +8,7 @@ import java.util.List;
*
* @param <M> 数据类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class DummyModelMapper<M> implements BaseModelMapper<M, M> {

View File

@@ -17,7 +17,7 @@ import java.util.List;
* @param <D> Model对应的DomainDto域对象类型。
* @param <K> Model对象主键的类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public abstract class BaseDictService<M, D, K> extends BaseService<M, D, K> {

View File

@@ -40,7 +40,7 @@ import static java.util.stream.Collectors.*;
* @param <D> Model对应的Dto对象类型。
* @param <K> Model对象主键的类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public abstract class BaseService<M, D, K> {

View File

@@ -9,7 +9,7 @@ import java.util.Set;
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public interface DictionaryCache<K, V> {

View File

@@ -9,7 +9,7 @@ import java.util.function.Function;
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class MapDictionaryCache<K, V> implements DictionaryCache<K, V> {

View File

@@ -12,7 +12,7 @@ import java.util.function.Function;
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {

View File

@@ -21,7 +21,7 @@ import java.util.List;
* 所有的项目拦截器、参数解析器、消息对象转换器都在这里集中配置。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Configuration
public class CommonWebMvcConfig implements WebMvcConfigurer {

View File

@@ -8,7 +8,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
* 目前用于用户密码加密UAA接入应用客户端的client_secret加密。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Configuration
public class EncryptConfig {

View File

@@ -11,7 +11,7 @@ import org.springframework.context.annotation.Configuration;
* FeignClient的配置对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Configuration
public class FeignConfig implements RequestInterceptor {

View File

@@ -24,7 +24,7 @@ import java.util.List;
* RestTemplate连接池配置对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Configuration
public class RestTemplateConfig {

View File

@@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
* tomcat配置对象。当前配置禁用了PUT和DELETE方法防止渗透攻击。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Configuration
public class TomcatConfig {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.constant;
* 在跨服务调用时,需要传递的聚合分类常量对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public final class AggregationKind {

View File

@@ -7,7 +7,7 @@ import java.util.Map;
* 聚合计算的常量类型对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public final class AggregationType {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.constant;
* 应用程序的常量声明对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public final class ApplicationConstant {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.constant;
* 返回应答中的错误代码和错误信息。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public enum ErrorCodeEnum {
@@ -21,6 +21,9 @@ public enum ErrorCodeEnum {
ARGUMENT_PK_ID_NULL("数据验证失败接口调用主键Id参数为空请核对"),
INVALID_ARGUMENT_FORMAT("数据验证失败,不合法的参数格式,请核对!"),
INVALID_STATUS_ARGUMENT("数据验证失败,无效的状态参数值,请核对!"),
UPLOAD_FAILED("数据验证失败,数据上传失败!"),
INVALID_UPLOAD_FIELD("数据验证失败,该字段不支持数据上传!"),
INVALID_UPLOAD_STORE_TYPE("数据验证失败,并不支持上传存储类型!"),
INVALID_UPLOAD_FILE_ARGUMENT("数据验证失败,上传文件参数错误,请核对!"),
INVALID_UPLOAD_FILE_IOERROR("上传文件写入失败,请联系管理员!"),
UNAUTHORIZED_LOGIN("当前用户尚未登录或登录已超时,请重新登录!"),

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.constant;
* 数据记录逻辑删除标记常量。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public final class GlobalDeletedFlag {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.exception;
* 数据验证失败的自定义异常。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class DataValidationException extends RuntimeException {

View File

@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
* 无效的类对象字段的自定义异常。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
@EqualsAndHashCode(callSuper = true)

View File

@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
* 无效的实体对象字段的自定义异常。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
@EqualsAndHashCode(callSuper = true)

View File

@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
* 无效的实体对象的自定义异常。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
@EqualsAndHashCode(callSuper = true)

View File

@@ -5,7 +5,7 @@ package com.orange.demo.common.core.exception;
* NOTE主要是为了避免SonarQube进行代码质量扫描时给出警告。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class MyRuntimeException extends RuntimeException {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.exception;
* 没有数据被修改的自定义异常。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class NoDataAffectException extends RuntimeException {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.exception;
* 没有数据访问权限的自定义异常。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class NoDataPermException extends RuntimeException {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.exception;
* Redis缓存访问失败。比如获取分布式数据锁超时、等待线程中断等。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class RedisCacheAccessException extends RuntimeException {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.exception;
* 关联远程服务数据失败的自定义异常。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class RemoteDataBuildException extends RuntimeException {

View File

@@ -29,7 +29,7 @@ import java.util.*;
* 2、多个对象需要封装到一个对象里才可以用@RequestBody接收。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver {

View File

@@ -11,7 +11,7 @@ import java.util.Map;
* 应用程序启动后的事件监听对象。主要负责加载Model之间的字典关联和一对一关联所对应的Service结构关系。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Component
public class LoadCachedDataListener implements ApplicationListener<ApplicationReadyEvent> {

View File

@@ -11,7 +11,7 @@ import java.util.Map;
* 应用程序启动后的事件监听对象。主要负责加载Model之间的字典关联和一对一关联所对应的Service结构关系。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Component
public class LoadServiceRelationListener implements ApplicationListener<ApplicationReadyEvent> {

View File

@@ -8,7 +8,7 @@ import lombok.Data;
* 同时为了提升效率减少查询次数可以根据具体的需求将部分验证关联对象存入data字段以供Controller使用。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
public class CallResult {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.object;
* 哑元对象,主要用于注解中的缺省对象占位符。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public final class DummyClass {

View File

@@ -10,7 +10,7 @@ import java.util.Set;
* 分组聚合查询参数。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
public class MyAggregationParam {

View File

@@ -7,7 +7,7 @@ import lombok.Data;
* Mybatis Mapper.xml中所需的分组条件对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
@AllArgsConstructor

View File

@@ -20,7 +20,7 @@ import java.util.List;
* 查询分组参数请求对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@EqualsAndHashCode(callSuper = true)
@Slf4j
@@ -31,7 +31,6 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
* SQL语句的SELECT LIST中分组字段的返回字段名称列表。
*/
private List<String> selectGroupFieldList;
/**
* 分组参数解析后构建的SQL语句中所需的分组数据如GROUP BY的字段列表和SELECT LIST中的分组字段显示列表。
*/

View File

@@ -18,7 +18,7 @@ import java.util.*;
* Controller参数中的排序请求对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@EqualsAndHashCode(callSuper = true)
@Slf4j
@@ -83,6 +83,9 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
private static OrderBaseData parseOrderBaseData(OrderInfo orderInfo, Class<?> modelClazz) {
OrderBaseData orderBaseData = new OrderBaseData();
if (StringUtils.isBlank(orderInfo.getFieldName())) {
return orderBaseData;
}
orderBaseData.fieldName = StringUtils.substringBefore(orderInfo.fieldName, DICT_MAP);
String[] stringArray = StringUtils.split(orderBaseData.fieldName, '.');
if (stringArray.length == 1) {

View File

@@ -6,7 +6,7 @@ import lombok.Getter;
* Controller参数中的分页请求对象
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Getter
public class MyPageParam {

View File

@@ -9,7 +9,7 @@ import java.util.*;
* 查询参数。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
public class MyQueryParam {

View File

@@ -8,7 +8,7 @@ import lombok.Data;
* BaseService中的实体对象数据组装函数会根据该参数对象进行数据组装。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
@Builder

View File

@@ -16,7 +16,7 @@ import java.util.List;
* Where中的条件语句。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
@Data

View File

@@ -1,13 +1,19 @@
package com.orange.demo.common.core.object;
import com.alibaba.fastjson.JSON;
import com.orange.demo.common.core.constant.ErrorCodeEnum;
import com.orange.demo.common.core.util.ContextUtil;
import lombok.Data;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 接口返回对象
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
public class ResponseResult<T> {
@@ -145,6 +151,47 @@ public class ResponseResult<T> {
return success;
}
/**
* 通过HttpServletResponse直接输出应该信息的工具方法。
*
* @param httpStatus http状态码。
* @param responseResult 应答内容。
* @param <T> 数据对象类型。
* @throws IOException 异常错误。
*/
public static <T> void output(int httpStatus, ResponseResult<T> responseResult) throws IOException {
HttpServletResponse response = ContextUtil.getHttpResponse();
PrintWriter out = response.getWriter();
response.setContentType("application/json; charset=utf-8");
response.setStatus(httpStatus);
if (responseResult != null) {
out.print(JSON.toJSONString(responseResult));
}
out.flush();
}
/**
* 通过HttpServletResponse直接输出应该信息的工具方法。
*
* @param httpStatus http状态码。
* @param <T> 数据对象类型。
* @throws IOException 异常错误。
*/
public static <T> void output(int httpStatus) throws IOException {
output(httpStatus, null);
}
/**
* 通过HttpServletResponse直接输出应该信息的工具方法。Http状态码为200。
*
* @param responseResult 应答内容。
* @param <T> 数据对象类型。
* @throws IOException 异常错误。
*/
public static <T> void output(ResponseResult<T> responseResult) throws IOException {
output(HttpServletResponse.SC_OK, responseResult);
}
private ResponseResult() {
}

View File

@@ -16,7 +16,7 @@ import java.nio.charset.StandardCharsets;
* 基于Jwt用于前后端传递的令牌对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
@ToString

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.object;
* 二元组对象。主要用于可以一次返回多个结果的场景,同时还能避免强制转换。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class Tuple2<T1, T2> {

View File

@@ -0,0 +1,137 @@
package com.orange.demo.common.core.upload;
import com.alibaba.fastjson.JSON;
import com.orange.demo.common.core.constant.ApplicationConstant;
import com.orange.demo.common.core.util.ContextUtil;
import com.orange.demo.common.core.util.MyCommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 上传或下载文件抽象父类。
* 包含存储本地文件的功能,以及上传和下载所需的通用方法。
*
* @author Jerry
* @date 2020-10-19
*/
@Slf4j
public abstract class BaseUpDownloader {
/**
* 构建上传文件的完整目录。
*
* @param rootBaseDir 文件下载的根目录。
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @return 上传文件的完整路径名。
*/
public String makeFullPath(
String rootBaseDir, String modelName, String fieldName, Boolean asImage) {
StringBuilder uploadPathBuilder = new StringBuilder(128);
if (StringUtils.isNotBlank(rootBaseDir)) {
uploadPathBuilder.append(rootBaseDir).append("/");
}
if (Boolean.TRUE.equals(asImage)) {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_IMAGE_PARENT_PATH);
} else {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_ATTACHMENT_PARENT_PATH);
}
uploadPathBuilder.append("/").append(modelName).append("/").append(fieldName).append("/");
return uploadPathBuilder.toString();
}
/**
* 构建上传操作的返回对象。
*
* @param serviceContextPath 微服务的上下文路径,如: /admin/upms。
* @param originalFilename 上传文件的原始文件名(包含扩展名)。
*/
public void fillUploadResponseInfo(
UploadResponseInfo responseInfo, String serviceContextPath, String originalFilename) {
// 根据请求上传的uri构建下载uri只是将末尾的/upload改为/download即可。
HttpServletRequest request = ContextUtil.getHttpRequest();
String uri = request.getRequestURI();
uri = StringUtils.removeEnd(uri, "/");
uri = StringUtils.removeEnd(uri, "/upload");
String downloadUri;
if (StringUtils.isBlank(serviceContextPath)) {
downloadUri = uri + "/download";
} else {
downloadUri = serviceContextPath + uri + "/download";
}
StringBuilder filenameBuilder = new StringBuilder(64);
filenameBuilder.append(MyCommonUtil.generateUuid())
.append(".").append(FilenameUtils.getExtension(originalFilename));
responseInfo.setDownloadUri(downloadUri);
responseInfo.setFilename(filenameBuilder.toString());
}
/**
* 执行下载操作从本地文件系统读取数据并将读取的数据直接写入到HttpServletResponse应答对象。
*
* @param rootBaseDir 文件下载的根目录。
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param fileName 文件名。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @param response Http 应答对象。
* @throws Exception 操作错误。
*/
public abstract void doDownload(
String rootBaseDir,
String modelName,
String fieldName,
String fileName,
Boolean asImage,
HttpServletResponse response) throws Exception;
/**
* 执行文件上传操作并存入本地文件系统再将与该文件下载对应的Url直接写入到HttpServletResponse应答对象返回给前端。
*
* @param serviceContextPath 微服务的上下文路径,如: /admin/upms。
* @param rootBaseDir 存放上传文件的根目录。
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param uploadFile Http请求中上传的文件对象。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @return 存储在本地上传文件名。
* @throws Exception 操作错误。
*/
public abstract UploadResponseInfo doUpload(
String serviceContextPath,
String rootBaseDir,
String modelName,
String fieldName,
Boolean asImage,
MultipartFile uploadFile) throws Exception;
/**
* 判断filename参数指定的文件名是否被包含在fileInfoJson参数中。
*
* @param fileInfoJson 内部类UploadFileInfo的JSONArray数组。
* @param filename 被包含的文件名。
* @return 存在返回true否则false。
*/
public static boolean containFile(String fileInfoJson, String filename) {
if (StringUtils.isAnyBlank(fileInfoJson, filename)) {
return false;
}
List<UploadResponseInfo> fileInfoList = JSON.parseArray(fileInfoJson, UploadResponseInfo.class);
if (CollectionUtils.isNotEmpty(fileInfoList)) {
for (UploadResponseInfo fileInfo : fileInfoList) {
if (StringUtils.equals(filename, fileInfo.getFilename())) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,149 @@
package com.orange.demo.common.core.upload;
import com.alibaba.fastjson.JSON;
import com.orange.demo.common.core.constant.ErrorCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
/**
* 存储本地文件的上传下载实现类。
*
* @author Jerry
* @date 2020-10-19
*/
@Slf4j
@Component
public class LocalUpDownloader extends BaseUpDownloader {
@Autowired
private UpDownloaderFactory factory;
@PostConstruct
public void doRegister() {
factory.registerUpDownloader(UploadStoreTypeEnum.LOCAL_SYSTEM, this);
}
/**
* 执行下载操作从本地文件系统读取数据并将读取的数据直接写入到HttpServletResponse应答对象。
*
* @param rootBaseDir 文件下载的根目录。
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param fileName 文件名。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @param response Http 应答对象。
*/
@Override
public void doDownload(
String rootBaseDir,
String modelName,
String fieldName,
String fileName,
Boolean asImage,
HttpServletResponse response) {
String uploadPath = makeFullPath(rootBaseDir, modelName, fieldName, asImage);
String fullFileanme = uploadPath + "/" + fileName;
File file = new File(fullFileanme);
if (!file.exists()) {
log.warn("Download file [" + fullFileanme + "] failed, no file found!");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
byte[] buff = new byte[2048];
try (OutputStream os = response.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
int i = bis.read(buff);
while (i != -1) {
os.write(buff, 0, buff.length);
os.flush();
i = bis.read(buff);
}
} catch (IOException e) {
log.error("Failed to call UpDownloadUtil.doDownload", e);
}
}
/**
* 执行文件上传操作并存入本地文件系统再将与该文件下载对应的Url直接写入到HttpServletResponse应答对象返回给前端。
*
* @param serviceContextPath 微服务的上下文路径,如: /admin/upms。
* @param rootBaseDir 存放上传文件的根目录。
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param uploadFile Http请求中上传的文件对象。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @return 存储在本地上传文件名。
* @throws IOException 文件操作错误。
*/
@Override
public UploadResponseInfo doUpload(
String serviceContextPath,
String rootBaseDir,
String modelName,
String fieldName,
Boolean asImage,
MultipartFile uploadFile) throws IOException {
UploadResponseInfo responseInfo = new UploadResponseInfo();
if (Objects.isNull(uploadFile) || uploadFile.isEmpty()) {
responseInfo.setUploadFailed(true);
responseInfo.setErrorMessage(ErrorCodeEnum.INVALID_UPLOAD_FILE_ARGUMENT.getErrorMessage());
return responseInfo;
}
String uploadPath = makeFullPath(rootBaseDir, modelName, fieldName, asImage);
fillUploadResponseInfo(responseInfo, serviceContextPath, uploadFile.getOriginalFilename());
try {
byte[] bytes = uploadFile.getBytes();
Path path = Paths.get(uploadPath + responseInfo.getFilename());
//如果没有files文件夹则创建
if (!Files.isWritable(path)) {
Files.createDirectories(Paths.get(uploadPath));
}
//文件写入指定路径
Files.write(path, bytes);
} catch (IOException e) {
log.error("Failed to write uploaded file [" + uploadFile.getOriginalFilename() + " ].", e);
responseInfo.setUploadFailed(true);
responseInfo.setErrorMessage(ErrorCodeEnum.INVALID_UPLOAD_FILE_IOERROR.getErrorMessage());
return responseInfo;
}
return responseInfo;
}
/**
* 判断filename参数指定的文件名是否被包含在fileInfoJson参数中。
*
* @param fileInfoJson 内部类UploadFileInfo的JSONArray数组。
* @param filename 被包含的文件名。
* @return 存在返回true否则false。
*/
public static boolean containFile(String fileInfoJson, String filename) {
if (StringUtils.isAnyBlank(fileInfoJson, filename)) {
return false;
}
List<UploadResponseInfo> fileInfoList = JSON.parseArray(fileInfoJson, UploadResponseInfo.class);
if (CollectionUtils.isNotEmpty(fileInfoList)) {
for (UploadResponseInfo fileInfo : fileInfoList) {
if (StringUtils.equals(filename, fileInfo.getFilename())) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,49 @@
package com.orange.demo.common.core.upload;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 业务对象根据上传下载存储类型,获取上传下载对象的工厂类。
*
* @author Jerry
* @date 2020-10-19
*/
@Component
public class UpDownloaderFactory {
private Map<UploadStoreTypeEnum, BaseUpDownloader> upDownloaderMap = new HashMap<>();
/**
* 根据存储类型获取上传下载对象。
* @param storeType 存储类型。
* @return 匹配的上传下载对象。
*/
public BaseUpDownloader get(UploadStoreTypeEnum storeType) {
BaseUpDownloader upDownloader = upDownloaderMap.get(storeType);
if (upDownloader == null) {
throw new UnsupportedOperationException(
"The storeType [" + storeType.name() + "] isn't supported, please add dependency jar first.");
}
return upDownloader;
}
/**
* 注册上传下载对象到工厂。
*
* @param storeType 存储类型。
* @param upDownloader 上传下载对象。
*/
public void registerUpDownloader(UploadStoreTypeEnum storeType, BaseUpDownloader upDownloader) {
if (storeType == null || upDownloader == null) {
throw new IllegalArgumentException("The Argument can't be NULL.");
}
if (upDownloaderMap.containsKey(storeType)) {
throw new UnsupportedOperationException(
"The storeType [" + storeType.name() + "] has been registered already.");
}
upDownloaderMap.put(storeType, upDownloader);
}
}

View File

@@ -0,0 +1,29 @@
package com.orange.demo.common.core.upload;
import lombok.Data;
/**
* 数据上传操作的应答信息对象。
*
* @author Jerry
* @date 2020-10-19
*/
@Data
public class UploadResponseInfo {
/**
* 上传是否出现错误。
*/
private Boolean uploadFailed = false;
/**
* 具体错误信息。
*/
private String errorMessage;
/**
* 返回前端的下载url。
*/
private String downloadUri;
/**
* 返回给前端的文件名。
*/
private String filename;
}

View File

@@ -0,0 +1,22 @@
package com.orange.demo.common.core.upload;
import lombok.Data;
/**
* 上传数据存储信息对象。这里之所以使用对象,主要是便于今后扩展。
*
* @author Jerry
* @date 2020-10-19
*/
@Data
public class UploadStoreInfo {
/**
* 是否支持上传。
*/
private boolean supportUpload;
/**
* 上传数据存储类型。
*/
private UploadStoreTypeEnum storeType;
}

View File

@@ -0,0 +1,19 @@
package com.orange.demo.common.core.upload;
/**
* 上传数据存储介质类型枚举。
*
* @author Jerry
* @date 2020-10-19
*/
public enum UploadStoreTypeEnum {
/**
* 本地系统。
*/
LOCAL_SYSTEM,
/**
* minio分布式存储。
*/
MINIO_SYSTEM
}

View File

@@ -11,7 +11,7 @@ import java.lang.reflect.Field;
* 获取JDK动态代理/CGLIB代理对象代理的目标对象的工具类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public class AopTargetUtil {
@@ -38,6 +38,12 @@ public class AopTargetUtil {
}
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private AopTargetUtil() {
}
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);

View File

@@ -10,7 +10,7 @@ import org.springframework.stereotype.Component;
* Spring 系统启动应用感知对象主要用于获取Spring Bean的上下文对象后续的代码中可以直接查找系统中加载的Bean对象。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {

View File

@@ -10,7 +10,7 @@ import javax.servlet.http.HttpServletResponse;
* 获取Servlet HttpRequest和HttpResponse的工具类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class ContextUtil {

View File

@@ -23,7 +23,7 @@ import java.util.*;
* 导出工具类目前支持xlsx和csv两种类型。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public class ExportUtil {

View File

@@ -22,7 +22,7 @@ import java.util.stream.Collectors;
* 导入工具类目前支持xlsx和csv两种类型。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public class ImportUtil {

View File

@@ -17,7 +17,7 @@ import java.util.List;
* Ip工具类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public class IpUtil {

View File

@@ -12,7 +12,7 @@ import java.util.Map;
* 基于JWT的Token生成工具类
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public class JwtUtil {

View File

@@ -6,7 +6,7 @@ package com.orange.demo.common.core.util;
* 提高代码的规范度和可维护性。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class LogMessageUtil {

View File

@@ -16,7 +16,7 @@ import java.util.UUID;
* 脚手架中常用的基本工具方法集合,一般而言工程内部使用的方法。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class MyCommonUtil {

View File

@@ -16,7 +16,7 @@ import static org.joda.time.PeriodType.days;
* 日期工具类主要封装了部分joda-time中的方法让很多代码一行完成同时统一了日期到字符串的pattern格式。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class MyDateUtil {

View File

@@ -5,8 +5,10 @@ import cn.hutool.core.util.ReflectUtil;
import com.orange.demo.common.core.annotation.RelationConstDict;
import com.orange.demo.common.core.annotation.RelationDict;
import com.orange.demo.common.core.annotation.RelationOneToOne;
import com.orange.demo.common.core.annotation.UploadFlagColumn;
import com.orange.demo.common.core.exception.MyRuntimeException;
import com.orange.demo.common.core.object.Tuple2;
import com.orange.demo.common.core.upload.UploadStoreInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
@@ -26,7 +28,7 @@ import java.util.stream.Collectors;
* 负责Model数据操作、类型转换和关系关联等行为的工具类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public class MyModelUtil {
@@ -495,6 +497,30 @@ public class MyModelUtil {
return e;
}
/**
* 获取上传字段的存储信息。
*
* @param modelClass model的class对象。
* @param uploadFieldName 上传字段名。
* @param <T> model的类型。
* @return 字段的上传存储信息对象。该值始终不会返回null。
*/
public static <T> UploadStoreInfo getUploadStoreInfo(Class<T> modelClass, String uploadFieldName) {
UploadStoreInfo uploadStoreInfo = new UploadStoreInfo();
Field uploadField = ReflectUtil.getField(modelClass, uploadFieldName);
if (uploadField == null) {
throw new UnsupportedOperationException("The Field ["
+ uploadFieldName + "] doesn't exist in Model [" + modelClass.getSimpleName() + "].");
}
uploadStoreInfo.setSupportUpload(false);
UploadFlagColumn anno = uploadField.getAnnotation(UploadFlagColumn.class);
if (anno != null) {
uploadStoreInfo.setSupportUpload(true);
uploadStoreInfo.setStoreType(anno.storeType());
}
return uploadStoreInfo;
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/

View File

@@ -13,7 +13,7 @@ import java.util.List;
* 生成带有分页信息的数据列表
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class MyPageUtil {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.util;
* Redis 键生成工具类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class RedisKeyUtil {

View File

@@ -15,7 +15,7 @@ import java.util.Map;
* Java RSA 加密工具类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class RsaUtil {
@@ -64,7 +64,8 @@ public class RsaUtil {
//base64编码的公钥
byte[] decoded = Base64.getDecoder().decode(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
//RSA加密。后面这个更安全但是SonarQube始终report安全漏洞。"RSA/ECB/PKCS1Padding"
//而浏览器自带的Javascript加密功能目前safari不支持而且用的人也不太多。所以暂时都不考虑了。
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));

View File

@@ -15,7 +15,7 @@ import java.util.function.Function;
* @param <K> 节点之间关联键的类型。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Data
public class TreeNode<T, K> {

View File

@@ -1,182 +0,0 @@
package com.orange.demo.common.core.util;
import com.alibaba.fastjson.JSON;
import com.orange.demo.common.core.constant.ApplicationConstant;
import com.orange.demo.common.core.constant.ErrorCodeEnum;
import com.orange.demo.common.core.object.ResponseResult;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
/**
* 上传或下载附件文件的工具类。
*
* @author Jerry
* @date 2020-09-27
*/
@Slf4j
public class UpDownloadUtil {
/**
* 执行下载操作并将读取的文件数据直接写入到HttpServletResponse应答对象。
*
* @param rootBaseDir 文件下载的根目录。
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param fileName 文件名。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @param response Http 应答对象。
*/
public static void doDownload(
String rootBaseDir,
String modelName,
String fieldName,
String fileName,
Boolean asImage,
HttpServletResponse response) {
StringBuilder uploadPathBuilder = new StringBuilder(128);
uploadPathBuilder.append(rootBaseDir).append("/");
if (Boolean.TRUE.equals(asImage)) {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_IMAGE_PARENT_PATH);
} else {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_ATTACHMENT_PARENT_PATH);
}
uploadPathBuilder.append("/").append(modelName).append("/").append(fieldName).append("/").append(fileName);
File file = new File(uploadPathBuilder.toString());
if (!file.exists()) {
log.warn("Download file [" + uploadPathBuilder.toString() + "] failed, no file found!");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
byte[] buff = new byte[2048];
try (OutputStream os = response.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
int i = bis.read(buff);
while (i != -1) {
os.write(buff, 0, buff.length);
os.flush();
i = bis.read(buff);
}
} catch (IOException e) {
log.error("Failed to call UpDownloadUtil.doDownload", e);
}
}
/**
* 执行文件上传操作并将与该文件下载对应的Url直接写入到HttpServletResponse应答对象返回给前端。
*
* @param rootBaseDir 存放上传文件的根目录。
* @param serviceContextPath 微服务的上下文路径,如: /admin/upms。
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param uploadFile Http请求中上传的文件对象。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @param response Http 应答对象。
* @return 存储在本地上传文件名。
* @throws IOException 文件操作错误。
*/
public static String doUpload(
String rootBaseDir,
String serviceContextPath,
String modelName,
String fieldName,
Boolean asImage,
MultipartFile uploadFile,
HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
response.setContentType("application/json; charset=utf-8");
if (Objects.isNull(uploadFile) || uploadFile.isEmpty() || MyCommonUtil.isBlankOrNull(fieldName)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
out.print(JSON.toJSONString(ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FILE_ARGUMENT)));
return null;
}
StringBuilder uploadPathBuilder = new StringBuilder(128);
uploadPathBuilder.append(rootBaseDir).append("/");
if (Boolean.TRUE.equals(asImage)) {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_IMAGE_PARENT_PATH);
} else {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_ATTACHMENT_PARENT_PATH);
}
uploadPathBuilder.append("/").append(modelName).append("/").append(fieldName).append("/");
// 根据请求上传的uri构建下载uri只是将末尾的/upload改为/download即可。
HttpServletRequest request = ContextUtil.getHttpRequest();
String uri = request.getRequestURI();
uri = StringUtils.removeEnd(uri, "/");
uri = StringUtils.removeEnd(uri, "/upload");
String downloadUri = serviceContextPath + uri + "/download";
StringBuilder filenameBuilder = new StringBuilder(64);
filenameBuilder.append(MyCommonUtil.generateUuid())
.append(".").append(FilenameUtils.getExtension(uploadFile.getOriginalFilename()));
UploadFileInfo fileInfo = new UploadFileInfo();
fileInfo.downloadUri = downloadUri;
fileInfo.filename = filenameBuilder.toString();
try {
byte[] bytes = uploadFile.getBytes();
Path path = Paths.get(uploadPathBuilder.toString() + filenameBuilder.toString());
//如果没有files文件夹则创建
if (!Files.isWritable(path)) {
Files.createDirectories(Paths.get(uploadPathBuilder.toString()));
}
//文件写入指定路径
Files.write(path, bytes);
} catch (IOException e) {
log.error("Failed to write uploaded file [" + uploadFile.getOriginalFilename() + " ].", e);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
out.print(JSON.toJSONString(ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FILE_IOERROR)));
return null;
}
out.print(JSON.toJSONString(ResponseResult.success(fileInfo)));
out.flush();
out.close();
return fileInfo.filename;
}
/**
* 判断filename参数指定的文件名是否被包含在fileInfoJson参数中。
*
* @param fileInfoJson 内部类UploadFileInfo的JSONArray数组。
* @param filename 被包含的文件名。
* @return 存在返回true否则false。
*/
public static boolean containFile(String fileInfoJson, String filename) {
if (StringUtils.isAnyBlank(fileInfoJson, filename)) {
return false;
}
List<UploadFileInfo> fileInfoList = JSON.parseArray(fileInfoJson, UploadFileInfo.class);
if (CollectionUtils.isNotEmpty(fileInfoList)) {
for (UploadFileInfo fileInfo : fileInfoList) {
if (StringUtils.equals(filename, fileInfo.filename)) {
return true;
}
}
}
return false;
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private UpDownloadUtil() {
}
@Data
static class UploadFileInfo {
private String downloadUri;
private String filename;
}
}

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.validator;
* 数据增加的验证分组。通常用于数据新增场景。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public interface AddGroup {
}

View File

@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
* 定义在Model对象中标注字段值引用自指定的常量字典和ConstDictRefValidator对象配合完成数据验证。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -10,7 +10,7 @@ import java.lang.reflect.Method;
* 数据字段自定义验证用于验证Model中字符串字段的最大长度和最小长度。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class ConstDictValidator implements ConstraintValidator<ConstDictRef, Object> {

View File

@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
* 定义在Model或Dto对象中UTF-8编码的字符串字段长度的上限和下限和TextLengthValidator对象配合完成数据验证。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -9,7 +9,7 @@ import javax.validation.ConstraintValidatorContext;
* 数据字段自定义验证用于验证Model中UTF-8编码的字符串字段的最大长度和最小长度。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public class TextLengthValidator implements ConstraintValidator<TextLength, String> {

View File

@@ -4,7 +4,7 @@ package com.orange.demo.common.core.validator;
* 数据修改的验证分组。通常用于数据更新的场景。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
public interface UpdateGroup {

View File

@@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
<facet type="web" name="Web">
<configuration>
<webroots />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: io.minio:minio:7.0.2" level="project" />
<orderEntry type="library" name="Maven: org.simpleframework:simple-xml:2.7.1" level="project" />
<orderEntry type="library" name="Maven: stax:stax-api:1.0.1" level="project" />
<orderEntry type="library" name="Maven: stax:stax:1.2.0" level="project" />
<orderEntry type="library" name="Maven: xpp3:xpp3:1.1.3.3" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:28.2-android" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
<orderEntry type="library" name="Maven: org.checkerframework:checker-compat-qual:2.5.5" level="project" />
<orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.3.4" level="project" />
<orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
<orderEntry type="library" name="Maven: com.squareup.okhttp3:okhttp:3.12.0" level="project" />
<orderEntry type="library" name="Maven: com.squareup.okio:okio:1.17.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.github.spotbugs:spotbugs-annotations:4.0.0" level="project" />
<orderEntry type="library" name="Maven: net.jcip:jcip-annotations:1.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
<orderEntry type="module" module-name="common-core" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.2" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.31" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.31" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.31" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.0.18.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.9" level="project" />
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.13" level="project" />
<orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
<orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.3.3" level="project" />
<orderEntry type="library" name="Maven: joda-time:joda-time:2.10.5" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-collections4:4.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-csv:1.8" level="project" />
<orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.1.5" level="project" />
<orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.70" level="project" />
<orderEntry type="library" name="Maven: com.github.ben-manes.caffeine:caffeine:2.8.1" level="project" />
<orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.1.0" level="project" />
<orderEntry type="library" name="Maven: cn.jimmyshi:bean-query:1.1.5" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-all:1.3" level="project" />
<orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.9.3" level="project" />
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.2" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jcl-over-slf4j:1.7.30" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml:3.17" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi:3.17" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:3.17" level="project" />
<orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:2.6.0" level="project" />
<orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.04" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: mysql:mysql-connector-java:8.0.19" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:druid-spring-boot-starter:1.1.22" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:druid:1.1.22" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring-boot-starter:2.1.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.2" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.mybatis:mybatis:3.4.6" level="project" />
<orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:1.3.2" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-core:1.1.5" level="project" />
<orderEntry type="library" name="Maven: javax.persistence:persistence-api:1.0" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-base:1.1.5" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-weekend:1.1.5" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring:1.1.5" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-extra:1.1.5" level="project" />
<orderEntry type="library" name="Maven: tk.mybatis:mapper-spring-boot-autoconfigure:2.1.5" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.2.13" level="project" />
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1" level="project" />
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:2.1.1" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-autoconfigure:1.2.13" level="project" />
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper:5.1.11" level="project" />
<orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:2.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.25" level="project" />
<orderEntry type="library" name="Maven: org.freemarker:freemarker:2.3.29" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-log4j2:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-slf4j-impl:2.12.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.12.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-core:2.12.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-jul:2.12.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-cache:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-configuration-processor:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.2.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.2" level="project" />
<orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.3.5" level="project" />
<orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.11" level="project" />
<orderEntry type="library" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
<orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-starter-client:2.2.3" level="project" />
<orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-client:2.2.3" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2.2.1.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.spring:spring-context-support:1.0.6" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-client:1.2.1" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-common:1.2.1" level="project" />
<orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-api:1.2.1" level="project" />
<orderEntry type="library" name="Maven: io.prometheus:simpleclient:0.5.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-core:10.7.4" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:10.7.4" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-hystrix:10.7.4" level="project" />
<orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.6" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-httpclient:10.7.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.11" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.13" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-hystrix:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-hystrix:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-archaius:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
<orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
<orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-serialization:1.5.18" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: com.fasterxml.jackson.module:jackson-module-afterburner:2.10.2" level="project" />
<orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-metrics-event-stream:1.5.18" level="project" />
<orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-javanica:1.5.18" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
<orderEntry type="library" name="Maven: io.reactivex:rxjava-reactive-streams:1.2.1" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
<orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.3.1.Final" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.mapstruct:mapstruct-processor:1.3.1.Final" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.12" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-recipes:4.3.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-framework:4.0.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-client:4.0.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.zookeeper:zookeeper:3.5.3-beta" level="project" />
<orderEntry type="library" name="Maven: commons-cli:commons-cli:1.4" level="project" />
<orderEntry type="library" name="Maven: org.apache.kafka:kafka-clients:2.4.0" level="project" />
<orderEntry type="library" name="Maven: com.github.luben:zstd-jni:1.4.3-1" level="project" />
<orderEntry type="library" name="Maven: org.lz4:lz4-java:1.6.0" level="project" />
<orderEntry type="library" name="Maven: org.xerial.snappy:snappy-java:1.1.7.3" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
<orderEntry type="library" name="Maven: org.scala-lang:scala-library:2.12.10" level="project" />
<orderEntry type="library" name="Maven: com.lmax:disruptor:3.4.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-starter-test:2.2.5.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test:2.2.5.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test-autoconfigure:2.2.5.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.jayway.jsonpath:json-path:2.4.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:json-smart:2.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:accessors-smart:1.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-commons:1.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-params:5.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.vintage:junit-vintage-engine:5.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-engine:1.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-junit-jupiter:3.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:3.13.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest:2.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:3.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy:1.10.8" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.10.8" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:2.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.skyscreamer:jsonassert:1.5.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.4.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.4.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.2.4.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.6.3" level="project" />
</component>
</module>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common</artifactId>
<groupId>com.orange.demo</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-minio</artifactId>
<version>1.0.0</version>
<name>common-minio</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
<dependency>
<groupId>com.orange.demo</groupId>
<artifactId>common-core</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,48 @@
package com.orange.demo.common.minio.config;
import com.orange.demo.common.minio.wrapper.MinioTemplate;
import io.minio.MinioClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* common-minio模块的自动配置引导类。仅当配置项minio.enabled为true的时候加载。
*
* @author Jerry
* @date 2020-10-19
*/
@EnableConfigurationProperties(MinioProperties.class)
@ConditionalOnProperty(prefix = "minio", name = "enabled")
public class MinioAutoConfiguration {
/**
* 将minio原生的客户端类封装成bean对象便于集成同时也可以灵活使用客户端的所有功能。
*
* @param p 属性配置对象。
* @return minio的原生客户端对象。
*/
@Bean
@ConditionalOnMissingBean
public MinioClient minioClient(MinioProperties p) throws Exception {
MinioClient client = new MinioClient(p.getEndpoint(), p.getAccessKey(), p.getSecretKey());
if (!client.bucketExists(p.getBucketName())) {
client.makeBucket(p.getBucketName());
}
return client;
}
/**
* 封装的minio模板类。
*
* @param p 属性配置对象。
* @param c minio的原生客户端bean对象。
* @return minio模板的bean对象。
*/
@Bean
@ConditionalOnMissingBean
public MinioTemplate minioTemplate(MinioProperties p, MinioClient c) {
return new MinioTemplate(p, c);
}
}

View File

@@ -0,0 +1,32 @@
package com.orange.demo.common.minio.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* common-minio模块的配置类。
*
* @author Jerry
* @date 2020-10-19
*/
@Data
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
/**
* 访问入口地址。
*/
private String endpoint;
/**
* 访问安全的key。
*/
private String accessKey;
/**
* 访问安全的密钥。
*/
private String secretKey;
/**
* 缺省桶名称。
*/
private String bucketName;
}

View File

@@ -0,0 +1,100 @@
package com.orange.demo.common.minio.util;
import cn.hutool.core.io.IoUtil;
import com.orange.demo.common.core.constant.ErrorCodeEnum;
import com.orange.demo.common.core.upload.UpDownloaderFactory;
import com.orange.demo.common.core.upload.UploadResponseInfo;
import com.orange.demo.common.core.upload.BaseUpDownloader;
import com.orange.demo.common.core.upload.UploadStoreTypeEnum;
import com.orange.demo.common.minio.wrapper.MinioTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Objects;
/**
* 基于Minio上传和下载文件操作的工具类。
*
* @author Jerry
* @date 2020-10-19
*/
@Slf4j
@Component
@ConditionalOnProperty(prefix = "minio", name = "enabled")
public class MinioUpDownloader extends BaseUpDownloader {
@Autowired
private MinioTemplate minioTemplate;
@Autowired
private UpDownloaderFactory factory;
@PostConstruct
public void doRegister() {
factory.registerUpDownloader(UploadStoreTypeEnum.MINIO_SYSTEM, this);
}
/**
* 执行文件上传操作将文件数据存入Minio。
*
* @param serviceContextPath 微服务的上下文路径,如: /admin/upms。
* @param rootBaseDir 存放上传文件的根目录。(minio中忽略该值因为使用了bucket)
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param uploadFile Http请求中上传的文件对象。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @return 上传应答信息对象。该对象始终不为null。
* @throws Exception minio抛出的异常。
*/
@Override
public UploadResponseInfo doUpload(
String serviceContextPath,
String rootBaseDir,
String modelName,
String fieldName,
Boolean asImage,
MultipartFile uploadFile) throws Exception {
UploadResponseInfo responseInfo = new UploadResponseInfo();
if (Objects.isNull(uploadFile) || uploadFile.isEmpty()) {
responseInfo.setUploadFailed(true);
responseInfo.setErrorMessage(ErrorCodeEnum.INVALID_UPLOAD_FILE_ARGUMENT.getErrorMessage());
return responseInfo;
}
String uploadPath = super.makeFullPath(null, modelName, fieldName, asImage);
super.fillUploadResponseInfo(responseInfo, serviceContextPath, uploadFile.getOriginalFilename());
minioTemplate.putObject(uploadPath + "/" + responseInfo.getFilename(), uploadFile.getInputStream());
return responseInfo;
}
/**
* 执行下载操作从Minio读取数据。并将读取的文件数据直接写入到HttpServletResponse应答对象。
*
* @param rootBaseDir 文件下载的根目录。(minio中忽略该值因为使用了bucket)
* @param modelName 所在数据表的实体对象名。
* @param fieldName 关联字段的实体对象属性名。
* @param fileName 文件名。
* @param asImage 是否为图片对象。图片是无需权限验证的,因此和附件存放在不同的子目录。
* @param response Http 应答对象。
*/
@Override
public void doDownload(
String rootBaseDir,
String modelName,
String fieldName,
String fileName,
Boolean asImage,
HttpServletResponse response) throws Exception {
String uploadPath = this.makeFullPath(null, modelName, fieldName, asImage);
String fullFileanme = uploadPath + "/" + fileName;
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
InputStream in = minioTemplate.getStream(fullFileanme);
IoUtil.copy(in, response.getOutputStream());
}
}

View File

@@ -0,0 +1,209 @@
package com.orange.demo.common.minio.wrapper;
import com.orange.demo.common.minio.config.MinioProperties;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.InputStream;
/**
* 封装的minio客户端模板类。
*
* @author Jerry
* @date 2020-10-19
*/
@Slf4j
public class MinioTemplate {
private static final String TMP_DIR = System.getProperty("java.io.tmpdir") + File.separator;
private final MinioProperties properties;
private final MinioClient client;
public MinioTemplate(MinioProperties properties, MinioClient client) {
super();
this.properties = properties;
this.client = client;
}
/**
* 判断bucket是否存在。
*
* @param bucketName 桶名称。
* @return 存在返回true否则false。
*/
public boolean bucketExists(String bucketName) {
try {
return client.bucketExists(bucketName);
} catch (Exception e) {
log.error("", e);
}
return false;
}
/**
* 创建桶。
*
* @param bucketName 桶名称。
*/
public void makeBucket(String bucketName) throws Exception {
if (!client.bucketExists(bucketName)) {
client.makeBucket(bucketName);
}
}
/**
* 存放对象。
*
* @param bucketName 桶名称。
* @param objectName 对象名称。
* @param filename 本地上传的文件名称。
*/
public void putObject(String bucketName, String objectName, String filename) throws Exception {
client.putObject(bucketName, objectName, filename, new PutObjectOptions(-1, -1));
}
/**
* 存放对象。桶名称为配置中的桶名称。
*
* @param objectName 对象名称。
* @param filename 本地上传的文件名称。
*/
public void putObject(String objectName, String filename) throws Exception {
this.putObject(properties.getBucketName(), objectName, filename);
}
/**
* 读取输入流并存放。
*
* @param bucketName 桶名称。
* @param objectName 对象名称。
* @param stream 读取后上传的文件流。
*/
public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
client.putObject(bucketName, objectName, stream, new PutObjectOptions(stream.available(), -1));
}
/**
* 读取输入流并存放。
*
* @param objectName 对象名称。
* @param stream 读取后上传的文件流。
*/
public void putObject(String objectName, InputStream stream) throws Exception {
this.putObject(properties.getBucketName(), objectName, stream);
}
/**
* 移除对象。
*
* @param bucketName 桶名称。
* @param objectName 对象名称。
*/
public void removeObject(String bucketName, String objectName) throws Exception {
client.removeObject(bucketName, objectName);
}
/**
* 移除对象。桶名称为配置中的桶名称。
*
* @param objectName 对象名称。
*/
public void removeObject(String objectName) throws Exception {
this.removeObject(properties.getBucketName(), objectName);
}
/**
* 获取文件输入流。
*
* @param bucket 桶名称。
* @param objectName 对象名称。
* @return 文件的输入流。
*/
public InputStream getStream(String bucket, String objectName) throws Exception {
return client.getObject(bucket, objectName);
}
/**
* 获取文件输入流。
*
* @param objectName 对象名称。
* @return 文件的输入流。
*/
public InputStream getStream(String objectName) throws Exception {
return this.getStream(properties.getBucketName(), objectName);
}
/**
* 获取存储的文件对象。
*
* @param bucket 桶名称。
* @param objectName 对象名称。
* @return 读取后存储到文件的文件对象。
*/
public File getFile(String bucket, String objectName) throws Exception {
InputStream in = getStream(bucket, objectName);
File dir = new File(TMP_DIR);
if (!dir.exists() || dir.isFile()) {
dir.mkdirs();
}
File file = new File(TMP_DIR + objectName);
FileUtils.copyInputStreamToFile(in, file);
return file;
}
/**
* 获取存储的文件对象。桶名称为配置中的桶名称。
*
* @param objectName 对象名称。
* @return 读取后存储到文件的文件对象。
*/
public File getFile(String objectName) throws Exception {
return this.getFile(properties.getBucketName(), objectName);
}
/**
* 获取指定文件的URL。
*
* @param bucketName 桶名称。
* @param objectName 对象名称。
* @return 指定文件的URL。
*/
public String getObjectUrl(String bucketName, String objectName) throws Exception {
return client.getObjectUrl(bucketName, objectName);
}
/**
* 获取指定文件的URL。桶名称为配置中的桶名称。
*
* @param objectName 对象名称。
* @return 指定文件的URL。
*/
public String getObjectUrl(String objectName) throws Exception {
return this.getObjectUrl(properties.getBucketName(), objectName);
}
/**
* 获取对象状态信息。
*
* @param bucketName 桶名称。
* @param objectName 对象名称。
* @return 对象状态和meta信息。
*/
public ObjectStat statObject(String bucketName, String objectName) throws Exception {
return client.statObject(bucketName, objectName, null);
}
/**
* 获取对象状态信息。桶名称为配置中的桶名称。
*
* @param objectName 对象名称。
* @return 对象状态和meta信息。
*/
public ObjectStat statObject(String objectName) throws Exception {
return client.statObject(properties.getBucketName(), objectName, null);
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.orange.demo.common.minio.config.MinioAutoConfiguration

View File

@@ -21,7 +21,7 @@ import java.util.stream.Collectors;
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public class RedisDictionaryCache<K, V> implements DictionaryCache<K, V> {

View File

@@ -21,7 +21,7 @@ import java.util.stream.Collectors;
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Slf4j
public class RedisTreeDictionaryCache<K, V> extends RedisDictionaryCache<K, V> {

View File

@@ -15,7 +15,7 @@ import java.util.Map;
* 使用Redisson作为Redis的分布式缓存库。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Configuration
@EnableCaching

View File

@@ -13,7 +13,7 @@ import java.util.Set;
* Session数据缓存辅助类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@SuppressWarnings("unchecked")
@Component

View File

@@ -11,7 +11,7 @@ import redis.clients.jedis.JedisPoolConfig;
* Redis配置类。
*
* @author Jerry
* @date 2020-09-27
* @date 2020-10-19
*/
@Configuration
@ConditionalOnProperty(name = "redis.jedis.enabled", havingValue = "true")

Some files were not shown because too many files have changed in this diff Show More