mirror of
https://gitee.com/orangeform/orange-admin.git
synced 2026-01-17 18:46:36 +08:00
commit:集成Minio
This commit is contained in:
@@ -12,7 +12,7 @@ import java.util.Date;
|
||||
* Controller的环绕拦截类。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class MyControllerAdvice {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.lang.annotation.*;
|
||||
* 主要用于标记逻辑删除字段。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.lang.annotation.*;
|
||||
* 主要用于标记更新字段。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.List;
|
||||
* 所有的项目拦截器、参数解析器、消息对象转换器都在这里集中配置。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Configuration
|
||||
public class CommonWebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.List;
|
||||
* RestTemplate连接池配置对象。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.Map;
|
||||
* 聚合计算的常量类型对象。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
public final class AggregationType {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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("当前用户尚未登录或登录已超时,请重新登录!"),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
|
||||
* 无效的类对象字段的自定义异常。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
||||
@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
|
||||
* 无效的实体对象字段的自定义异常。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
||||
@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
|
||||
* 无效的实体对象的自定义异常。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.*;
|
||||
* 2、多个对象需要封装到一个对象里才可以用@RequestBody接收。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -8,7 +8,7 @@ import lombok.Data;
|
||||
* 同时为了提升效率,减少查询次数,可以根据具体的需求,将部分验证关联对象存入data字段,以供Controller使用。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
public class CallResult {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.Set;
|
||||
* 分组聚合查询参数。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
public class MyAggregationParam {
|
||||
|
||||
@@ -7,7 +7,7 @@ import lombok.Data;
|
||||
* Mybatis Mapper.xml中所需的分组条件对象。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
|
||||
@@ -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中的分组字段显示列表。
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import lombok.Getter;
|
||||
* Controller参数中的分页请求对象
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Getter
|
||||
public class MyPageParam {
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.*;
|
||||
* 查询参数。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
public class MyQueryParam {
|
||||
|
||||
@@ -8,7 +8,7 @@ import lombok.Data;
|
||||
* BaseService中的实体对象数据组装函数,会根据该参数对象进行数据组装。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.List;
|
||||
* Where中的条件语句。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Slf4j
|
||||
@Data
|
||||
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.nio.charset.StandardCharsets;
|
||||
* 基于Jwt,用于前后端传递的令牌对象。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.util.*;
|
||||
* 导出工具类,目前支持xlsx和csv两种类型。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExportUtil {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.List;
|
||||
* Ip工具类。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Slf4j
|
||||
public class IpUtil {
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.Map;
|
||||
* 基于JWT的Token生成工具类
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Slf4j
|
||||
public class JwtUtil {
|
||||
|
||||
@@ -6,7 +6,7 @@ package com.orange.demo.common.core.util;
|
||||
* 提高代码的规范度和可维护性。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
public class LogMessageUtil {
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.UUID;
|
||||
* 脚手架中常用的基本工具方法集合,一般而言工程内部使用的方法。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
public class MyCommonUtil {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 私有构造函数,明确标识该常量类的作用。
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
* 生成带有分页信息的数据列表
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
public class MyPageUtil {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package com.orange.demo.common.core.validator;
|
||||
* 数据增加的验证分组。通常用于数据新增场景。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
public interface AddGroup {
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ package com.orange.demo.common.core.validator;
|
||||
* 数据修改的验证分组。通常用于数据更新的场景。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-27
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
public interface UpdateGroup {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user