Skip to content

Latest commit

 

History

History
4188 lines (3704 loc) · 153 KB

优秀代码实践.md

File metadata and controls

4188 lines (3704 loc) · 153 KB

项目可学代码

接口中使用配置注入
@FeignClient(name = "${self.job-center.apiclient.name}",
        path = "${self.job-center.apiclient.path}",configuration = FeignInterceptor.class)
@RequestMapping(method = RequestMethod.POST)
public interface JobCenterApiClient {
    @RequestMapping(value = "${self.job-center.apiclient.notifyJobResultUri}")
    ApiResp<Void> notifyJobResult(JobResult result);
}
线程池配置类与其常量
@Configuration
@EnableAsync
public class ThreadPoolConfig {

    /**
     * 获取当前机器的核数, 不一定准确 请根据实际场景 CPU密集 || IO 密集
     */
    public static final int cpuNum = Runtime.getRuntime().availableProcessors();
    // 核心线程池大小
    @Value("${self.pool.corePoolSize}")
    private Optional<Integer> corePoolSize;
    // 最大可创建的线程数
    @Value("${self.pool.maxPoolSize}")
    private Optional<Integer> maxPoolSize;
    // 队列最大长度
    @Value("${self.pool.queueCapacity}")
    private Optional<Integer> queueCapacity;
    // 线程池维护线程所允许的空闲时间
    @Value("${self.pool.keepAliveSeconds}")
    private Optional<Integer> keepAliveSeconds;

    @Bean(name = "selfThreadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
    {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(maxPoolSize.orElse(cpuNum * 2));
        executor.setCorePoolSize(corePoolSize.orElse(cpuNum));
        executor.setQueueCapacity(queueCapacity.orElse(500));
        executor.setKeepAliveSeconds(keepAliveSeconds.orElse(300));
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }


    @Bean(name = "constThreadPoolTaskExecutor")
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(ThreadPoolConsts.Number.TWENTY);
        // 设置最大线程数
        executor.setMaxPoolSize(ThreadPoolConsts.Number.FIFTY);
        // 设置队列容量
        executor.setQueueCapacity(ThreadPoolConsts.Number.THREE_THOUSAND);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(ThreadPoolConsts.Number.SIX_HUNDRED);
        // 设置默认线程名称
        executor.setThreadNamePrefix(ThreadPoolConsts.Namefix.Thread_Name_Prefix);
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

public interface ThreadPoolConsts {
    interface Number {
        // 代表无,不存在等
        int MINUS_ONE = -1;
        int MINUS_TWO = -2;
        int ZERO = 0;
        int ONE = 1;
        int TWO = 2;
        int THREE = 3;
        int FOUR = 4;
        int FIVE = 5;
        int SIX = 6;
        int SEVEN = 7;
        int EIGHT = 8;
        int NINE = 9;
        int TEN = 10;
        int TWENTY = 20;
        int FIFTY = 50;
        int ONE_HUNDRED = 100;
        int TWO_HUNDRED = 200;
        int SIX_HUNDRED = 600;
        int THREE_THOUSAND = 3000;
    }

    interface Namefix {
        String Thread_Name_Prefix = "async-task-";
    }

}
返回体封装
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseResult {
    @ApiModelProperty(value = "提示信息")
    private String msg;
    @ApiModelProperty(value = "错误码(200为成功)",example ="200")
    private int code;
    @ApiModelProperty(value = "是否成功")
    public boolean isSuccess() {
        return code == ResponseCode.SUCCESS.getCode();
    }
}
@Data
@ApiModel(value="统一结果对象", description="")
public class Result<T> extends BaseResult{
    private final static Result<?> EMPTY = new Result<>();
    @ApiModelProperty(value = "响应数据对象")
    private T data;

    private Result() {
        this.data = null;
    }
    private Result(T data, int code) {
        super(null,code);
        this.data = data;
    }
    private Result(String msg, int code) {
        super(msg,code);
    }

    /**
     *
     * 功能描述: 创建一个空Result类
     */
    public static <T> Result<T> empty() {
        @SuppressWarnings("unchecked")
        Result<T> t = (Result<T>) EMPTY;
        return t;
    }

    /**
     *
     * 功能描述: 生成一个成功状态Result类
     * @param: data
     * @return: Result<T>
     */
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>(data, ResponseCode.SUCCESS.getCode());
        result.setMsg(ResponseCode.SUCCESS.getMsg());
        return result;
    }
  
    public static <T> Result<T> success() {
        return new Result<>(null, ResponseCode.SUCCESS.getCode());
    }

    public static <T> Result<T> fail(String message) {
        return (Result<T>)new Result<>(message, ResponseCode.FAIL.getCode());
    }

    public static <T> Result<T> fail(ResultCode resultCode) {
        return (Result<T>)new Result<>(resultCode.getMsg(), resultCode.getCode());
    }

    public static <T> Result<T> fail(int code,String message) {
        return (Result<T>)new Result<>(message, code);
    }

    /**
     *
     * 功能描述: 判断是否传入值是否为空,非空则返回值,为空则返回失败信息
     * @param: message 返回的错误信息
     * @return: Result<T>
     */
    public <T> Result<T> orFail(String message) {
        if (null != data) {
            return (Result<T>) this;
        } else {
            super.setMsg(getMsg());
            super.setCode(ResponseCode.FAIL.getCode());
        }
        return (Result<T>) this;
    }
}
@Getter
@AllArgsConstructor
public enum ResponseCode implements ResultCode{
	FAIL(0,"失败!"),
	SUCCESS(200,"成功"),
	;
	private final int code;
	private final String msg;
}
public interface ResultCode extends Serializable {

    /**
     * 获取消息
     */
    String getMsg();

    /**
     * 获取状态码
     */
    int getCode();

}
分页请求体与返回体
/**
* 通用分页表单
*/
@Data
@ApiModel(value = "通用分页表单",description = "通用分页表单")
public class PageRequest {
    @ApiModelProperty(value = "页码",required = true,example = "1")
    private Integer pageNo = 1;

    @ApiModelProperty(value = "页容量",required = true,example = "20")
    private Integer pageSize = 20;

    @ApiModelProperty(value = "起始记录的下标",hidden = true)
    private Integer startIndex;
    @ApiModelProperty(hidden = true,value = "最大页容量")
    private Integer maxSize;
    public Page convertPage(){
        return new Page<>(getPageNo(), getPageSize());
    }
    public Integer getPageSize(){
        if (maxSize != null && pageSize > maxSize){
            pageSize = maxSize;
        }
        return pageSize;
    }
    public int getStartIndex(){
        startIndex = (getPageNo() - 1) * getPageSize();
        return startIndex;
    }
}
@Data
public class PageResult<T> extends BaseResult{
    private final static PageResult<?> EMPTY = new PageResult<>();
    @ApiModelProperty(value = "分页对象")
    private PageData<T> data;
    private PageResult() {
        this.data = null;
    }
    private PageResult(String message, int code) {
        super(message,code);
    }
    private PageResult(ResultCode resultCode) {
        super(resultCode.getMsg(),resultCode.getCode());
    }

    private PageResult(List<T> list, int code) {
        super(null,code);
        PageData<T> data = new PageData<>(list);
        this.data = data;
    }
    private PageResult(IPage<T> page, int code) {
        super(null,code);
        PageData<T> data = new PageData<>(page);
        this.data = data;
    }

    /**
     * 功能描述: 创建一个空PageResult类
     */
    public static <T> PageResult<T> empty() {
        @SuppressWarnings("unchecked")
        PageResult<T> t = (PageResult<T>) EMPTY;
        return t;
    }

    /**
     * 功能描述: 生成一个成功状态PageResult类
     */
    public static <T> PageResult<T> success(List<T> data) {
        return new PageResult<>(data, ResponseCode.SUCCESS.getCode());
    }
    public static <T> PageResult<T> success(IPage<T> page) {
        return new PageResult<>(page, ResponseCode.SUCCESS.getCode());
    }
    /**
     * 功能描述: 生成一个失败状态PageResult类
     */
    public static <T> PageResult<T> fail(String message) {
        return new PageResult<>(message, ResponseCode.FAIL.getCode());
    }

    /**
     * 功能描述: 设置页码信息
     */
    public <T> PageResult<T> page(int pageNo, int pageSize) {
        if (ResponseCode.SUCCESS.getCode() == super.getCode()) {
            data.setPageNo(pageNo);
            data.setPageSize(pageSize);
        }
        return (PageResult<T>) this;
    }
    public <T> PageResult<T> page(PageRequest pageRequest){
        return page(pageRequest.getPageNo(),pageRequest.getPageSize());
    }

    /**
     * 功能描述: 设置总数
     */
    public <T> PageResult<T> total(int total) {
        if (data != null) {
            data.setTotal(total);
        }
        return (PageResult<T>) this;
    }
    /**
     * 功能描述: 判断是否传入值是否为空,非空则返回值,为空则返回失败信息
     */
    public <T> PageResult<T> orFail(String message) {
        if (null != data.records) {
            return (PageResult<T>) this;
        } else {
            super.setCode(ResponseCode.FAIL.getCode());
            super.setMsg(message);
//            this.msg = message;
//            this.code = "0";
        }
        return (PageResult<T>) this;
    }
    @Override
    public int hashCode() {
        return Objects.hashCode(data);
    }
    @Override
    public String toString() {
        return data != null
                ? String.format("result[%s]", data)
                : "result.empty";
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class PageData<T> {
        /**
         * 接口数据
         */
        @ApiModelProperty(value = "结果集")
        private List<T> records;
        @ApiModelProperty(value = "当前页码",example = "1")
        private long pageNo;
        @ApiModelProperty(value = "总记录数",example = "100")
        private long total;
        @ApiModelProperty(value = "页容量",example = "20")
        private long pageSize;

        public  PageData(List<T> records){
            this.records = records;
        }
        public PageData(IPage<T> page) {
            this.total = page.getTotal();
            this.records = page.getRecords();
            this.pageNo = page.getCurrent();
            this.pageSize = page.getSize();
        }
    }
}
//PageResponse使用方式

    @GetMapping("/select")
    @ApiOperation("查询模版下拉选项")
    public ResponseEntity<PageResponse<ResultTemplateSelectVO>> getTemplateListByConfig(TemplateQueryDTO templateQueryDTO) {
        templateQueryDTO.setUserId(getUserId());
        Page<ResultTemplate> page = resultTemplateService.listTemplateByServiceType(templateQueryDTO);
        List<ResultTemplateSelectVO> result = resultTemplateConvert.resultTemplateList2ResultTemplateSelectVOList(page.getRecords());
        if (page.getCurrent() == 1) {
            result.add(0, new ResultTemplateSelectVO(-1L, "千寻默认csv"));
        }
        PageResponse<ResultTemplateSelectVO> response = new PageResponse<>();
        response.setList(result);
        response.setTotal(page.getTotal());
        response.setPage(page.getCurrent());
        response.setLimit(page.getSize());
        return ResponseEntity.ok(response);
    }
全局异常处理
第一种方式
//config包下新增
/**
* 全局异常处理,处理可预见的异常,Order 排序优先级高
*/
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@RestControllerAdvice
public class CustomRestControllerAdvice {
	@ExceptionHandler(MissingServletRequestParameterException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public Result<String> handleError(MissingServletRequestParameterException e) {
		log.warn("缺少请求参数:{}", e.getMessage());
		String message = String.format("缺少必要的请求参数: %s", e.getParameterName());
		return Result.fail(message);
	}

	@ExceptionHandler(MethodArgumentTypeMismatchException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public Result<String> handleError(MethodArgumentTypeMismatchException e) {
		log.warn("请求参数格式错误:{}", e.getMessage());
		String message = String.format("请求参数格式错误: %s", e.getName());
		return Result.fail(message);
	}

	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public Result<String> handleError(MethodArgumentNotValidException e) {
		log.warn("参数验证失败:{}", e.getMessage());
		return handleError(e.getBindingResult());
	}

	@ExceptionHandler(BindException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public Result<String> handleError(BindException e) {
		log.warn("参数绑定失败:{}", e.getMessage());
		return handleError(e.getBindingResult());
	}

	private Result<String> handleError(BindingResult result) {
		FieldError error = result.getFieldError();
		String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
		return Result.fail(message);
	}

	@ExceptionHandler(ConstraintViolationException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public Result<String> handleError(ConstraintViolationException e) {
		log.warn("参数验证失败:{}", e.getMessage());
		Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
		ConstraintViolation<?> violation = violations.iterator().next();
		String path = ((PathImpl) violation.getPropertyPath()).getLeafNode().getName();
		String message = String.format("%s:%s", path, violation.getMessage());
		return Result.fail(message);
	}

	@ExceptionHandler(NoHandlerFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND)
	public Result<String> handleError(NoHandlerFoundException e) {
		log.error("404没找到请求:{}", e.getMessage());
		return Result.fail(e.getMessage());
	}

	@ExceptionHandler(HttpMessageNotReadableException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public Result<String> handleError(HttpMessageNotReadableException e) {
		log.error("消息不能读取:{}", e.getMessage());
		return Result.fail(e.getMessage());
	}

	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
	public Result<String> handleError(HttpRequestMethodNotSupportedException e) {
		log.error("不支持当前请求方法:{}", e.getMessage());
		return Result.fail(e.getMessage());
	}

	@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
	@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
	public Result<String> handleError(HttpMediaTypeNotSupportedException e) {
		log.error("不支持当前媒体类型:{}", e.getMessage());
		return Result.fail(e.getMessage());
	}

	@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
	@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
	public Result<String> handleError(HttpMediaTypeNotAcceptableException e) {
		String message = e.getMessage() + " " + StringUtils.join(e.getSupportedMediaTypes());
		log.error("不接受的媒体类型:{}", message);
		return Result.fail(message);
	}
	@ExceptionHandler(Throwable.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public Result<String> handleError(Throwable e) {
		log.error("服务器异常:{}", e);
		return Result.fail(e.getMessage());
	}

	@ExceptionHandler(value = AuthException.class)
	@ResponseStatus(HttpStatus.UNAUTHORIZED)
	public Result<String> handleAuthException(AuthException ex) {
		String temp = ex.getAuthExceptionEnum().getMessage();
		return Result.fail(temp);
	}
}
//exception包下新增
public class AuthException extends RuntimeException {
    private final AuthExceptionEnum authExceptionEnum;
    public AuthException(AuthExceptionEnum authExceptionEnum) {
        super(authExceptionEnum.getMessage());
        this.authExceptionEnum = authExceptionEnum;
    }

    public AuthExceptionEnum getAuthExceptionEnum() {
        return this.authExceptionEnum;
    }
}
public enum AuthExceptionEnum {
    /**
     * 401登陆失败,500内部错误
     */
    INVALID_REQUEST(401, "没有鉴权信息"),
    INVALID_TOKEN(401, "token失效"),
    RESET_PASSWORD(401, "首次登陆请修改密码"),
    UNAUTHORIZED(401, "无权限"),
    INVALID_AK(401, "appKey错误"),
    INVALID_SIGN(401, "请求的接口加签错误"),
    INVALID_API(401, "请求的接口不在开放API范围"),
    FAILED(401, "鉴权失败"),
    UNKNOWN(500, "未知错误");

    private final int code;
    private final String message;
    AuthExceptionEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}
第二种方式
/**
 * 全局统一异常处理
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ApiResponse handlerException(Exception e) {
        if (e instanceof NoHandlerFoundException) {
            log.error("【全局异常拦截】NoHandlerFoundException: 请求方法 {}, 请求路径 {}", ((NoHandlerFoundException) e).getRequestURL(), ((NoHandlerFoundException) e).getHttpMethod());
            return ApiResponse.ofStatus(Status.REQUEST_NOT_FOUND);
        } else if (e instanceof HttpRequestMethodNotSupportedException) {
            log.error("【全局异常拦截】HttpRequestMethodNotSupportedException: 当前请求方式 {}, 支持请求方式 {}", ((HttpRequestMethodNotSupportedException) e).getMethod(), JSONUtil.toJsonStr(((HttpRequestMethodNotSupportedException) e).getSupportedHttpMethods()));
            return ApiResponse.ofStatus(Status.HTTP_BAD_METHOD);
        } else if (e instanceof MethodArgumentNotValidException) {
            log.error("【全局异常拦截】MethodArgumentNotValidException", e);
            return ApiResponse.of(Status.BAD_REQUEST.getCode(), ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors().get(0).getDefaultMessage(), null);
        } else if (e instanceof ConstraintViolationException) {
            log.error("【全局异常拦截】ConstraintViolationException", e);
            return ApiResponse.of(Status.BAD_REQUEST.getCode(), CollUtil.getFirst(((ConstraintViolationException) e).getConstraintViolations()).getMessage(), null);
        } else if (e instanceof MethodArgumentTypeMismatchException) {
            log.error("【全局异常拦截】MethodArgumentTypeMismatchException: 参数名 {}, 异常信息 {}", ((MethodArgumentTypeMismatchException) e).getName(), ((MethodArgumentTypeMismatchException) e).getMessage());
            return ApiResponse.ofStatus(Status.PARAM_NOT_MATCH);
        } else if (e instanceof HttpMessageNotReadableException) {
            log.error("【全局异常拦截】HttpMessageNotReadableException: 错误信息 {}", ((HttpMessageNotReadableException) e).getMessage());
            return ApiResponse.ofStatus(Status.PARAM_NOT_NULL);
        } else if (e instanceof BadCredentialsException) {
            log.error("【全局异常拦截】BadCredentialsException: 错误信息 {}", e.getMessage());
            return ApiResponse.ofStatus(Status.USERNAME_PASSWORD_ERROR);
        } else if (e instanceof DisabledException) {
            log.error("【全局异常拦截】BadCredentialsException: 错误信息 {}", e.getMessage());
            return ApiResponse.ofStatus(Status.USER_DISABLED);
        } else if (e instanceof BaseException) {
            log.error("【全局异常拦截】DataManagerException: 状态码 {}, 异常信息 {}", ((BaseException) e).getCode(), e.getMessage());
            return ApiResponse.ofException((BaseException) e);
        }

        log.error("【全局异常拦截】: 异常信息 {} ", e.getMessage());
        return ApiResponse.ofStatus(Status.ERROR);
    }
}

@Data
public class ApiResponse implements Serializable {
    private static final long serialVersionUID = 8993485788201922830L;

    private Integer code;

    private String message;

    private Object data;

    private ApiResponse() {

    }

    private ApiResponse(Integer code, String message, Object data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static ApiResponse of(Integer code, String message, Object data) {
        return new ApiResponse(code, message, data);
    }

    public static ApiResponse ofSuccess() {
        return ofSuccess(null);
    }


    public static ApiResponse ofSuccess(Object data) {
        return ofStatus(Status.SUCCESS, data);
    }

    public static ApiResponse ofMessage(String message) {
        return of(Status.SUCCESS.getCode(), message, null);
    }

    public static ApiResponse ofStatus(Status status) {
        return ofStatus(status, null);
    }

    public static ApiResponse ofStatus(IStatus status, Object data) {
        return of(status.getCode(), status.getMessage(), data);
    }

    public static <T extends BaseException> ApiResponse ofException(T t) {
        return of(t.getCode(), t.getMessage(), t.getData());
    }
}

public interface IStatus {
    Integer getCode();

    String getMessage();
}

@Getter
public enum Status implements IStatus {
    /**
     * 操作成功!
     */
    SUCCESS(200, "操作成功!"),

    /**
     * 操作异常!
     */
    ERROR(500, "操作异常!"),

    /**
     * 退出成功!
     */
    LOGOUT(200, "退出成功!"),

    /**
     * 请先登录!
     */
    UNAUTHORIZED(401, "请先登录!"),

    /**
     * 暂无权限访问!
     */
    ACCESS_DENIED(403, "权限不足!"),

    /**
     * 请求不存在!
     */
    REQUEST_NOT_FOUND(404, "请求不存在!"),

    /**
     * 请求方式不支持!
     */
    HTTP_BAD_METHOD(405, "请求方式不支持!"),

    /**
     * 请求异常!
     */
    BAD_REQUEST(400, "请求异常!"),

    /**
     * 参数不匹配!
     */
    PARAM_NOT_MATCH(400, "参数不匹配!"),

    /**
     * 参数不能为空!
     */
    PARAM_NOT_NULL(400, "参数不能为空!"),

    /**
     * 当前用户已被锁定,请联系管理员解锁!
     */
    USER_DISABLED(403, "当前用户已被锁定,请联系管理员解锁!"),

    /**
     * 用户名或密码错误!
     */
    USERNAME_PASSWORD_ERROR(5001, "用户名或密码错误!"),

    /**
     * token 已过期,请重新登录!
     */
    TOKEN_EXPIRED(5002, "token 已过期,请重新登录!"),

    /**
     * token 解析失败,请尝试重新登录!
     */
    TOKEN_PARSE_ERROR(5002, "token 解析失败,请尝试重新登录!"),

    /**
     * 当前用户已在别处登录,请尝试更改密码或重新登录!
     */
    TOKEN_OUT_OF_CTRL(5003, "当前用户已在别处登录,请尝试更改密码或重新登录!"),

    /**
     * 无法手动踢出自己,请尝试退出登录操作!
     */
    KICKOUT_SELF(5004, "无法手动踢出自己,请尝试退出登录操作!");

    /**
     * 状态码
     */
    private Integer code;

    /**
     * 返回信息
     */
    private String message;

    Status(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public static Status fromCode(Integer code) {
        Status[] statuses = Status.values();
        for (Status status : statuses) {
            if (status.getCode().equals(code)) {
                return status;
            }
        }
        return SUCCESS;
    }

    @Override
    public String toString() {
        return String.format(" Status:{code=%s, message=%s} ", getCode(), getMessage());
    }
}
通用日期处理
//DateUtil用法
String date = DateUtil.formatDate(localDate);
String[] dateTime = DateUtil.getDateTime(localDate,0);
String startTime = dateTime[0];
String endTime = dateTime[1];
public class DateUtil {
    public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
    public static final String PATTERN_DATETIME_MINI = "yyyyMMddHHmmss";
    public static final String PATTERN_DATE = "yyyy-MM-dd";
    public static final String PATTERN_TIME = "HH:mm:ss";

    public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME);
    public static final DateTimeFormatter DATETIME_MINI_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME_MINI);
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE);
    public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);

    /**
     * 获取当前日期
     */
    public static Date now() {
        return new Date();
    }

    /**
     * 添加年
     */
    public static Date plusYears(Date date, int yearsToAdd) {
        return DateUtil.set(date, Calendar.YEAR, yearsToAdd);
    }

    /**
     * 添加月
     */
    public static Date plusMonths(Date date, int monthsToAdd) {
        return DateUtil.set(date, Calendar.MONTH, monthsToAdd);
    }

    /**
     * 添加周
     */
    public static Date plusWeeks(Date date, int weeksToAdd) {
        return DateUtil.plus(date, Period.ofWeeks(weeksToAdd));
    }

    /**
     * 添加天
     */
    public static Date plusDays(Date date, long daysToAdd) {
        return DateUtil.plus(date, Duration.ofDays(daysToAdd));
    }

    /**
     * 添加小时
     */
    public static Date plusHours(Date date, long hoursToAdd) {
        return DateUtil.plus(date, Duration.ofHours(hoursToAdd));
    }

    /**
     * 添加分钟
     */
    public static Date plusMinutes(Date date, long minutesToAdd) {
        return DateUtil.plus(date, Duration.ofMinutes(minutesToAdd));
    }

    /**
     * 添加秒
     */
    public static Date plusSeconds(Date date, long secondsToAdd) {
        return DateUtil.plus(date, Duration.ofSeconds(secondsToAdd));
    }

    /**
     * 添加毫秒
     */
    public static Date plusMillis(Date date, long millisToAdd) {
        return DateUtil.plus(date, Duration.ofMillis(millisToAdd));
    }

    /**
     * 添加纳秒
     */
    public static Date plusNanos(Date date, long nanosToAdd) {
        return DateUtil.plus(date, Duration.ofNanos(nanosToAdd));
    }

    /**
     * 日期添加时间量
     */
    public static Date plus(Date date, TemporalAmount amount) {
        Instant instant = date.toInstant();
        return Date.from(instant.plus(amount));
    }

    /**
     * 减少年
     */
    public static Date minusYears(Date date, int years) {
        return DateUtil.set(date, Calendar.YEAR, -years);
    }

    /**
     * 减少月
     */
    public static Date minusMonths(Date date, int months) {
        return DateUtil.set(date, Calendar.MONTH, -months);
    }

    /**
     * 减少周
     */
    public static Date minusWeeks(Date date, int weeks) {
        return DateUtil.minus(date, Period.ofWeeks(weeks));
    }

    /**
     * 减少天
     */
    public static Date minusDays(Date date, long days) {
        return DateUtil.minus(date, Duration.ofDays(days));
    }

    /**
     * 减少小时
     */
    public static Date minusHours(Date date, long hours) {
        return DateUtil.minus(date, Duration.ofHours(hours));
    }

    /**
     * 减少分钟
     */
    public static Date minusMinutes(Date date, long minutes) {
        return DateUtil.minus(date, Duration.ofMinutes(minutes));
    }

    /**
     * 减少秒
     */
    public static Date minusSeconds(Date date, long seconds) {
        return DateUtil.minus(date, Duration.ofSeconds(seconds));
    }

    /**
     * 减少毫秒
     */
    public static Date minusMillis(Date date, long millis) {
        return DateUtil.minus(date, Duration.ofMillis(millis));
    }

    /**
     * 减少纳秒
     */
    public static Date minusNanos(Date date, long nanos) {
        return DateUtil.minus(date, Duration.ofNanos(nanos));
    }

    /**
     * 日期减少时间量
     */
    public static Date minus(Date date, TemporalAmount amount) {
        Instant instant = date.toInstant();
        return Date.from(instant.minus(amount));
    }

    /**
     * 设置日期属性
     */
    private static Date set(Date date, int calendarField, int amount) {
        Assert.notNull(date, "The date must not be null");
        Calendar c = Calendar.getInstance();
        c.setLenient(false);
        c.setTime(date);
        c.add(calendarField, amount);
        return c.getTime();
    }


    /**
     * java8 日期时间格式化
     */
    public static String formatDateTime(TemporalAccessor temporal) {
        return DATETIME_FORMATTER.format(temporal);
    }

    /**
     * java8 日期时间格式化
     */
    public static String formatDateTimeMini(TemporalAccessor temporal) {
        return DATETIME_MINI_FORMATTER.format(temporal);
    }

    /**
     * java8 日期时间格式化
     */
    public static String formatDate(TemporalAccessor temporal) {
        return DATE_FORMATTER.format(temporal);
    }

    /**
     * java8 时间格式化
     */
    public static String formatTime(TemporalAccessor temporal) {
        return TIME_FORMATTER.format(temporal);
    }

    /**
     * java8 日期格式化
     */
    public static String format(TemporalAccessor temporal, String pattern) {
        return DateTimeFormatter.ofPattern(pattern).format(temporal);
    }
    /**
     * 将字符串转换为时间
     */
    public static <T> T parse(String dateStr, String pattern, TemporalQuery<T> query) {
        return DateTimeFormatter.ofPattern(pattern).parse(dateStr, query);
    }

    /**
     * 时间转 Instant
     */
    public static Instant toInstant(LocalDateTime dateTime) {
        return dateTime.atZone(ZoneId.systemDefault()).toInstant();
    }

    /**
     * Instant 转 时间
     */
    public static LocalDateTime toDateTime(Instant instant) {
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    /**
     * 转换成 date
     */
    public static Date toDate(LocalDateTime dateTime) {
        return Date.from(DateUtil.toInstant(dateTime));
    }

    /**
     * 转换成 date
     */
    public static Date toDate(final LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * Converts local date time to Calendar.
     */
    public static Calendar toCalendar(final LocalDateTime localDateTime) {
        return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
    }

    /**
     * localDateTime 转换成毫秒数
     */
    public static long toMilliseconds(final LocalDateTime localDateTime) {
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * localDate 转换成毫秒数
     */
    public static long toMilliseconds(LocalDate localDate) {
        return toMilliseconds(localDate.atStartOfDay());
    }

    /**
     * 转换成java8 时间
     */
    public static LocalDateTime fromCalendar(final Calendar calendar) {
        TimeZone tz = calendar.getTimeZone();
        ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId();
        return LocalDateTime.ofInstant(calendar.toInstant(), zid);
    }

    /**
     * 转换成java8 时间
     */
    public static LocalDateTime fromInstant(final Instant instant) {
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    /**
     * 转换成java8 时间
     */
    public static LocalDateTime fromDate(final Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * 转换成java8 时间
     */
    public static LocalDateTime fromMilliseconds(final long milliseconds) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault());
    }

    /**
     * 比较2个时间差,跨度比较小
     */
    public static Duration between(Temporal startInclusive, Temporal endExclusive) {
        return Duration.between(startInclusive, endExclusive);
    }

    /**
     * 比较2个时间差,跨度比较大,年月日为单位
     */
    public static Period between(LocalDate startDate, LocalDate endDate) {
        return Period.between(startDate, endDate);
    }

    /**
     * 比较2个 时间差
     */
    public static Duration between(Date startDate, Date endDate) {
        return Duration.between(startDate.toInstant(), endDate.toInstant());
    }


    public static String getDate(LocalDateTime dateTime ,String format){
        if(dateTime == null){
            dateTime = LocalDateTime.now();
        }
        return dateTime.format(DateTimeFormatter.ofPattern(format));
    }
  
    public static String[] getDateTime(LocalDate dateTime,int addDay){
        LocalDate plusDate = dateTime.plusDays(addDay);
        String[] dates = new String[2];
        if(addDay>=0){
            dates[0] = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
            dates[1] = plusDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd 23:59:59"));
        }else{
            dates[0] = plusDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
            dates[1] = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd 23:59:59"));
        }

        return dates;
    }
    public static void main(String[] args) {
        System.out.println(getDateTime(LocalDate.now(), -6)[0]);
        System.out.println(getDateTime(LocalDate.now(), -6)[1]);
    }
}

//DateTimeUtil用法
LocalDate localDate = StringUtils.isBlank(monitorItemRequest.getDate())?LocalDate.now(): DateTimeUtil.parseDate(monitorItemRequest.getDate());
/**
 * DateTime 工具类
 **/
public class DateTimeUtil {
	public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME);
	public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE);
	public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);

	/**
	 * 日期时间格式化
	 */
	public static String formatDateTime(TemporalAccessor temporal) {
		return DATETIME_FORMAT.format(temporal);
	}

	/**
	 * 日期时间格式化
	 */
	public static String formatDate(TemporalAccessor temporal) {
		return DATE_FORMAT.format(temporal);
	}

	/**
	 * 时间格式化
	 */
	public static String formatTime(TemporalAccessor temporal) {
		return TIME_FORMAT.format(temporal);
	}

	/**
	 * 日期格式化
	 */
	public static String format(TemporalAccessor temporal, String pattern) {
		return DateTimeFormatter.ofPattern(pattern).format(temporal);
	}

	/**
	 * 将字符串转换为时间
	 */
	public static LocalDateTime parseDateTime(String dateStr, String pattern) {
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
		return DateTimeUtil.parseDateTime(dateStr, formatter);
	}

	/**
	 * 将字符串转换为时间
	 */
	public static LocalDateTime parseDateTime(String dateStr, DateTimeFormatter formatter) {
		return LocalDateTime.parse(dateStr, formatter);
	}

	/**
	 * 将字符串转换为时间
	 */
	public static LocalDateTime parseDateTime(String dateStr) {
		return DateTimeUtil.parseDateTime(dateStr, DateTimeUtil.DATETIME_FORMAT);
	}

	/**
	 * 将字符串转换为时间
	 */
	public static LocalDate parseDate(String dateStr, String pattern) {
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
		return DateTimeUtil.parseDate(dateStr, formatter);
	}

	/**
	 * 将字符串转换为时间
	 */
	public static LocalDate parseDate(String dateStr, DateTimeFormatter formatter) {
		return LocalDate.parse(dateStr, formatter);
	}

	/**
	 * 将字符串转换为日期
	 */
	public static LocalDate parseDate(String dateStr) {
		return DateTimeUtil.parseDate(dateStr, DateTimeUtil.DATE_FORMAT);
	}


	/**
	 * 将字符串转换为时间
	 */
	public static LocalTime parseTime(String dateStr, String pattern) {
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
		return DateTimeUtil.parseTime(dateStr, formatter);
	}

	/**
	 * 将字符串转换为时间
	 */
	public static LocalTime parseTime(String dateStr, DateTimeFormatter formatter) {
		return LocalTime.parse(dateStr, formatter);
	}

	/**
	 * 将字符串转换为时间
	 */
	public static LocalTime parseTime(String dateStr) {
		return DateTimeUtil.parseTime(dateStr, DateTimeUtil.TIME_FORMAT);
	}

	/**
	 * 时间转 Instant
	 */
	public static Instant toInstant(LocalDateTime dateTime) {
		return dateTime.atZone(ZoneId.systemDefault()).toInstant();
	}

	/**
	 * Instant 转 时间
	 */
	public static LocalDateTime toDateTime(Instant instant) {
		return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
	}

	/**
	 * 转换成 date
	 */
	public static Date toDate(LocalDateTime dateTime) {
		return Date.from(DateTimeUtil.toInstant(dateTime));
	}

	/**
	 * 比较2个时间差,跨度比较小
	 */
	public static Duration between(Temporal startInclusive, Temporal endExclusive) {
		return Duration.between(startInclusive, endExclusive);
	}

	/**
	 * 比较2个时间差,跨度比较大,年月日为单位
	 */
	public static Period between(LocalDate startDate, LocalDate endDate) {
		return Period.between(startDate, endDate);
	}
}

//DateConverterUtil用法
Date todayStart = DateConverterUtil.getTodayStart();
Date todayEnd = DateConverterUtil.getTodayEnd();
Date weekStart = DateConverterUtil.getWeekStart();
Date weekEnd = DateConverterUtil.getWeekEnd();
Date monthStart = DateConverterUtil.getMonthStart();
Date monthEnd = DateConverterUtil.getMonthEnd();
@Component
@Slf4j
public class DateConverterUtil {
    private final static String startStr = " 00:00:00";
    private final static String endStr = " 23:59:59";
    private final static String yyyyMMdd = "yyyy-MM-dd";
    private final static String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss";
    private static final List<String> formarts = new ArrayList<>(4);

    static {
        formarts.add("yyyy-MM");
        formarts.add("yyyy-MM-dd");
        formarts.add("yyyy-MM-dd HH:mm");
        formarts.add("yyyy-MM-dd HH:mm:ss");
    }

    public Date convert(String source) {

        try {
            if (StringUtils.isBlank(source)) {
                return null;
            }
            if (source.matches("^\\d{4}-\\d{1,2}$")) {
                return parseDate(source, formarts.get(0));
            } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
                return parseDate(source, formarts.get(1));
            } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
                return parseDate(source, formarts.get(2));
            } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
                return parseDate(source, formarts.get(3));
            } else {
                return null;
            }
        } catch (Exception e) {
            log.error("convert error",e);
            return null;
        }
    }

    /**
     * 格式化日期
     */
    private Date parseDate(String dateStr, String format) {
        Date date = null;
        try {
            DateFormat dateFormat = new SimpleDateFormat(format);
            date = dateFormat.parse(dateStr);
        } catch (Exception e) {

        }
        return date;
    }

    public static Date long2Date(Long time) {
        try {
            if (time == null) {
                return null;
            }
            return new Date(time);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取本月开始日期
     **/
    public static Date getMonthStart() throws ParseException {
        Calendar cal=Calendar.getInstance();
        cal.add(Calendar.MONTH, 0);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        Date time=cal.getTime();
        SimpleDateFormat yyyyMMddHHmmssFormat = new SimpleDateFormat(yyyyMMddHHmmss);
        return yyyyMMddHHmmssFormat.parse(new SimpleDateFormat(yyyyMMdd).format(time)+startStr);
    }

    /**
     * 获取本月最后一天
     **/
    public static Date getMonthEnd() throws ParseException {
        Calendar cal=Calendar.getInstance();
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        Date time=cal.getTime();
        SimpleDateFormat yyyyMMddHHmmssFormat = new SimpleDateFormat(yyyyMMddHHmmss);
        return yyyyMMddHHmmssFormat.parse(new SimpleDateFormat(yyyyMMdd).format(time)+endStr);
    }

    /**
     * 获取本周的第一天
     **/
    public static Date getWeekStart() throws ParseException {
        Calendar cal=Calendar.getInstance();
        cal.add(Calendar.WEEK_OF_MONTH, 0);
        cal.set(Calendar.DAY_OF_WEEK, 2);
        Date time=cal.getTime();
        SimpleDateFormat yyyyMMddHHmmssFormat = new SimpleDateFormat(yyyyMMddHHmmss);
        return yyyyMMddHHmmssFormat.parse(new SimpleDateFormat(yyyyMMdd).format(time)+startStr);
    }

    /**
     * 获取本周的最后一天
     **/
    public static Date getWeekEnd() throws ParseException {
        Calendar cal=Calendar.getInstance();
        cal.set(Calendar.DAY_OF_WEEK, cal.getActualMaximum(Calendar.DAY_OF_WEEK));
        cal.add(Calendar.DAY_OF_WEEK, 1);
        Date time=cal.getTime();
        SimpleDateFormat yyyyMMddHHmmssFormat = new SimpleDateFormat(yyyyMMddHHmmss);
        return yyyyMMddHHmmssFormat.parse(new SimpleDateFormat(yyyyMMdd).format(time)+endStr);
    }

    /**
     * 获取今天开始时间
     **/
    public static Date getTodayStart() throws ParseException {

        SimpleDateFormat yyyyMMddFormat = new SimpleDateFormat(yyyyMMdd);
        SimpleDateFormat yyyyMMddHHmmssFormat = new SimpleDateFormat(yyyyMMddHHmmss);
        Date currentDate = new Date();
        return yyyyMMddHHmmssFormat.parse(yyyyMMddFormat.format(currentDate)+startStr);
    }

    /**
     * 获取今天结束时间
     **/
    public static Date getTodayEnd() throws ParseException {
        SimpleDateFormat yyyyMMddFormat = new SimpleDateFormat(yyyyMMdd);
        SimpleDateFormat yyyyMMddHHmmssFormat = new SimpleDateFormat(yyyyMMddHHmmss);
        Date currentDate = new Date();
        return yyyyMMddHHmmssFormat.parse(yyyyMMddFormat.format(currentDate)+endStr);
    }
    /**
     * 获取年月日
     **/
    public static String getFormatDate(Date date) {
        String strDateFormat = "YYYY年MM月dd日";
        SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
        return sdf.format(date);
    }


    /**
     * 获取年月
     **/
    public static String getFormatDateMonth(Date date) {
        String strDateFormat = "yyyy-MM";
        SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
        return sdf.format(date);
    }

    /**
     * 获取年月
     **/
    public static String getFormatDateWeek(Date date) {
        String strDateFormat = "MM月dd日";
        SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
        return sdf.format(date);
    }
}
用map对关系进行映射避免for循环里面操作sql
    @Override
    public List<MonitorItemVO> queryDay7AlarmDetail(NoticeAlarmRecordStatusRequest noticeAlarmRecordStatusRequest) {
        NoticeMonitorPointQuery query = new NoticeMonitorPointQuery();
        query.setTypes(Arrays.asList(WarningTypeEnum.DZZH.getCode(),WarningTypeEnum.STSK.getCode()));
        query.setAreaCode(StringUtils.defaultIfBlank(noticeAlarmRecordStatusRequest.getAreaCode(),areaCode));
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = noticeMonitorObjectService.listNoticeMonitorPoint(query);
        List<Long> pointIds = noticeMonitorObjectDTOList.stream().flatMap(obj -> obj.getNoticeMonitorPoints().stream().map(mp -> mp.getId())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(pointIds)){
            return Collections.emptyList();
        }
        String startTime = "";
        String endTime = "";
        LocalDate localDate = StringUtils.isBlank(noticeAlarmRecordStatusRequest.getDate())?LocalDate.now(): DateTimeUtil.parseDate(noticeAlarmRecordStatusRequest.getDate());
        String[] dateTime = DateUtil.getDateTime(localDate,-6);
        startTime = dateTime[0];
        endTime = dateTime[1];
        LambdaQueryWrapper<NoticeAlarmRecord> noticeAlarmRecordLambdaQueryWrapper = Wrappers.lambdaQuery(NoticeAlarmRecord.class)
                .in(NoticeAlarmRecord::getPointId, pointIds)
                .eq(StringUtils.isNotBlank(noticeAlarmRecordStatusRequest.getStatus()),NoticeAlarmRecord::getStatus,noticeAlarmRecordStatusRequest.getStatus())
                .between(NoticeAlarmRecord::getTriggerTime, startTime, endTime)
                .orderByDesc(NoticeAlarmRecord::getTriggerTime);
        List<NoticeAlarmRecord> list = noticeAlarmRecordService.list(noticeAlarmRecordLambdaQueryWrapper);
        List<Long> recordPointIds = list.stream().map(rc->rc.getPointId()).collect(Collectors.toList());
        List<String> types = list.stream().map(rc -> rc.getItemCode()).collect(Collectors.toList());
        List<MonitorItemVO> results = new ArrayList<>();
        Map<Long, NoticeMonitorPointDTO> noticeMonitorPointDTOMap = noticeMonitorPointService.mapNoticeMonitorPoint(query);
        List<NoticeMonitorItem> noticeMonitorItemList = noticeMonitorItemService.countByPointIdAndType(recordPointIds,types);
        Map<String, NoticeMonitorItem> noticeMonitorItemMap = noticeMonitorItemList.stream().collect(Collectors.toMap(NoticeMonitorItem::getType, Function.identity(), (key1, key2) -> key1));
        for (NoticeAlarmRecord record : list) {
            MonitorItemVO monitorItem = new MonitorItemVO();
            monitorItem.setRecordId(record.getId());
            monitorItem.setAlarmLevel(record.getAlarmLevel());
            monitorItem.setStatus(record.getStatus());
            monitorItem.setType(record.getItemCode());
            monitorItem.setItemCode(record.getItemCode());
            NoticeMonitorItem noticeMonitorItem = noticeMonitorItemMap.get(record.getItemCode());
            if(noticeMonitorItem!=null){
                monitorItem.setItemCodeName(noticeMonitorItem.getName());
            }
            monitorItem.setTriggerTime(record.getTriggerTime());
            monitorItem.setAlarmDesc(record.getAlarmDesc());
            NoticeMonitorPointDTO noticeMonitorPointDTO = noticeMonitorPointDTOMap.get(record.getPointId());
            if(noticeMonitorPointDTO != null){
                NoticeMonitorObject noticeMonitorObject = noticeMonitorPointDTO.getNoticeMonitorObject();
                if(noticeMonitorObject != null){
                    monitorItem.setAreaDetail(noticeMonitorObject.getAreaDetail());
                }
                monitorItem.setAltitude(noticeMonitorPointDTO.getAltitude());
                monitorItem.setLongitude(noticeMonitorPointDTO.getLongitude());
                monitorItem.setLatitude(noticeMonitorPointDTO.getLatitude());
                monitorItem.setName(noticeMonitorPointDTO.getName());
            }
            results.add(monitorItem);
        }
        return results;
    }
}
用Ipage进行分页
    @Override
    public List<MonitorItemVO> queryMonitorItemWarnRecord(MonitorItemRequest monitorItemRequest, PageRequest pageRequest) {
        NoticeMonitorPointQuery query = new NoticeMonitorPointQuery();
        query.setTypes(Arrays.asList(WarningTypeEnum.DZZH.getCode(),WarningTypeEnum.STSK.getCode()));
        query.setAreaCode(StringUtils.defaultIfBlank(monitorItemRequest.getAreaCode(),areaCode));
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = noticeMonitorObjectService.listNoticeMonitorPoint(query);
        List<Long> pointIds = noticeMonitorObjectDTOList.stream().flatMap(obj -> obj.getNoticeMonitorPoints().stream().map(mp -> mp.getId())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(pointIds)){
            return Collections.emptyList();
        }
        LocalDate localDate = StringUtils.isBlank(monitorItemRequest.getDate())?LocalDate.now(): DateTimeUtil.parseDate(monitorItemRequest.getDate());
        String[] dateTime = DateUtil.getDateTime(localDate,0);
        String startTime = dateTime[0];
        String endTime = dateTime[1];

        LambdaQueryWrapper<NoticeAlarmRecord> noticeAlarmRecordLambdaQueryWrapper = Wrappers.lambdaQuery(NoticeAlarmRecord.class)
                .in(NoticeAlarmRecord::getPointId, pointIds)
                .eq(NoticeAlarmRecord::getItemCode, monitorItemRequest.getType())
                .between(NoticeAlarmRecord::getTriggerTime,startTime,endTime)
                .orderByDesc(NoticeAlarmRecord::getTriggerTime);
        IPage<NoticeAlarmRecord> page = noticeAlarmRecordService.listByPage(pageRequest.convertPage(), noticeAlarmRecordLambdaQueryWrapper);
        List<NoticeAlarmRecord> records = page.getRecords();
        List<MonitorItemVO> monitorItemVOS = new ArrayList<>();
        if(records == null){
            return monitorItemVOS;
        }
        List<Long> recordPointIds = records.stream().map(rc->rc.getPointId()).collect(Collectors.toList());
        List<String> types = records.stream().map(rc -> rc.getItemCode()).collect(Collectors.toList());
        Map<Long, NoticeMonitorPointDTO> noticeMonitorPointDTOMap = noticeMonitorPointService.mapNoticeMonitorPoint(query);
        List<NoticeMonitorItem> noticeMonitorItemList = noticeMonitorItemService.countByPointIdAndType(recordPointIds,types);
        Map<String, NoticeMonitorItem> noticeMonitorItemMap = noticeMonitorItemList.stream().collect(Collectors.toMap(NoticeMonitorItem::getType, Function.identity(), (key1, key2) -> key1));
        for (NoticeAlarmRecord record : records) {
            MonitorItemVO monitorItem = new MonitorItemVO();
            monitorItem.setAlarmLevel(record.getAlarmLevel());
            monitorItem.setStatus(record.getStatus());
            monitorItem.setItemCode(record.getItemCode());
            NoticeMonitorItem noticeMonitorItem = noticeMonitorItemMap.get(record.getItemCode());
            if(noticeMonitorItem!=null){
                monitorItem.setItemCodeName(noticeMonitorItem.getName());
            }
            monitorItem.setType(record.getItemCode());
            monitorItem.setTriggerTime(record.getTriggerTime());
            monitorItem.setAlarmDesc(record.getAlarmDesc());
            NoticeMonitorPointDTO noticeMonitorPointDTO = noticeMonitorPointDTOMap.get(record.getPointId());
            if(noticeMonitorPointDTO != null){
                NoticeMonitorObject noticeMonitorObject = noticeMonitorPointDTO.getNoticeMonitorObject();
                if(noticeMonitorObject != null){
                    monitorItem.setAreaDetail(noticeMonitorObject.getAreaDetail());
                }
                monitorItem.setAltitude(noticeMonitorPointDTO.getAltitude());
                monitorItem.setLongitude(noticeMonitorPointDTO.getLongitude());
                monitorItem.setLatitude(noticeMonitorPointDTO.getLatitude());
                monitorItem.setName(noticeMonitorPointDTO.getName());
            }
            monitorItemVOS.add(monitorItem);
        }

        return monitorItemVOS;
    }
serviceImpl中也可以写查询wrapper
    /**
     * 告警处理详情接口
     */
    @Override
    public AlarmDetailVO queryAlarmDetail(AlarmDetailRequest alarmDetailRequest) {
        NoticeAlarmRecord noticeAlarmRecord = noticeAlarmRecordService.getById(alarmDetailRequest.getRecordId());
        AlarmDetailVO alarmDetai = new AlarmDetailVO();
        if(noticeAlarmRecord == null){
            return alarmDetai;
        }
        LambdaQueryWrapper<NoticeAlarmProcessRecord> noticeAlarmProcessRecordLambdaQueryWrapper = Wrappers.lambdaQuery(NoticeAlarmProcessRecord.class)
                .eq(NoticeAlarmProcessRecord::getRecordId, alarmDetailRequest.getRecordId())
                .orderByDesc(NoticeAlarmProcessRecord::getGmtCreate)
                .last("limit 1");
        NoticeAlarmProcessRecord noticeAlarmProcessRecord = noticeAlarmProcessRecordService.getOne(noticeAlarmProcessRecordLambdaQueryWrapper);
        NoticeMonitorPoint noticeMonitorPoint = noticeMonitorPointService.getById(noticeAlarmRecord.getPointId());
        alarmDetai.setAlarmDesc(noticeAlarmRecord.getAlarmDesc());
        alarmDetai.setAlarmLevel(noticeAlarmRecord.getAlarmLevel());
        LambdaQueryWrapper<NoticeMonitorItem> noticeMonitorItemLambdaQueryWrapper = Wrappers.lambdaQuery(NoticeMonitorItem.class)
                .eq(NoticeMonitorItem::getType, noticeAlarmRecord.getItemCode())
                .last("limit 1");
        NoticeMonitorItem noticeMonitorItem = noticeMonitorItemService.getOne(noticeMonitorItemLambdaQueryWrapper);
        alarmDetai.setItemCodeName(noticeMonitorItem.getName());
        alarmDetai.setItemCode(noticeAlarmRecord.getItemCode());
        alarmDetai.setStatus(noticeAlarmRecord.getStatus());
        alarmDetai.setTriggerTime(noticeAlarmRecord.getTriggerTime());
        alarmDetai.setType(noticeAlarmRecord.getItemCode());
        if(noticeAlarmProcessRecord != null){
            alarmDetai.setProcessUser(noticeAlarmProcessRecord.getProcessUser());
            alarmDetai.setFalsePositive(noticeAlarmProcessRecord.getFalsePositive());
            alarmDetai.setOpinions(noticeAlarmProcessRecord.getOpinions());
            alarmDetai.setGmtCreate(noticeAlarmProcessRecord.getGmtCreate());
        }
        if(noticeMonitorPoint != null){
            alarmDetai.setName(noticeMonitorPoint.getName());
        }
        return alarmDetai;
    }
Optional的使用与不同日期范围的处理
    @Override
    public List<MonitorInspectionStatisticsVO> queryInspectionStatistics(BaseWarningRequest baseWarningRequest) {
        baseWarningRequest.setAreaCode(StringUtils.defaultIfBlank(baseWarningRequest.getAreaCode(),areaCode));
        List<NoticeInspectionDetailDTO> noticeInspectionDetailDTOList = noticeMonitorObjectService.listNoticeInspectionDetail(baseWarningRequest);
        List<Long> objectIdList = noticeInspectionDetailDTOList.stream().flatMap(obj -> obj.getNoticeInspectionDetails().stream().map(mp -> mp.getObjectId())).collect(Collectors.toList());
        List<MonitorInspectionStatisticsVO> responseList= new ArrayList<>();
        if (CollectionUtils.isEmpty(objectIdList)) {
            // 没有数据权限
            responseList.add(MonitorInspectionStatisticsVO.builder()
                    .timeScale("today")
                    .normalCount(0L)
                    .inspectionCount(0L)
                    .normalRate(BigDecimal.ZERO)
                    .build());
            responseList.add(MonitorInspectionStatisticsVO.builder()
                    .timeScale("week")
                    .normalCount(0L)
                    .inspectionCount(0L)
                    .normalRate(BigDecimal.ZERO)
                    .build());
            responseList.add(MonitorInspectionStatisticsVO.builder()
                    .timeScale("month")
                    .normalCount(0L)
                    .inspectionCount(0L)
                    .normalRate(BigDecimal.ZERO)
                    .build());
            return responseList;
        }
        try {
            // 今日
            Date todayStart = DateConverterUtil.getTodayStart();
            Date todayEnd = DateConverterUtil.getTodayEnd();
            BigDecimal todayCount = new BigDecimal(Optional.ofNullable(noticeInspectionDetailService.countAll( todayStart,todayEnd, objectIdList)).orElse(0));
            BigDecimal todayErrorCount = new BigDecimal(Optional.ofNullable(noticeInspectionDetailService.countError(todayStart,todayEnd,objectIdList)).orElse(0));
            BigDecimal todayNormalCount = todayCount.subtract(todayErrorCount);

            responseList.add(MonitorInspectionStatisticsVO.builder()
                    .timeScale("today")
                    .normalCount(todayNormalCount.longValue())
                    .inspectionCount(todayCount.longValue())
                    .normalRate(BigDecimal.ZERO.equals(todayCount) ? BigDecimal.ONE : todayNormalCount.divide(todayCount, 2, RoundingMode.HALF_UP ))
                    .build());

            // 本周
            Date weekStart = DateConverterUtil.getWeekStart();
            Date weekEnd = DateConverterUtil.getWeekEnd();
            BigDecimal weekCount = new BigDecimal(Optional.ofNullable(noticeInspectionDetailService.countAll(weekStart,weekEnd,objectIdList)).orElse(0));
            BigDecimal weekErrorCount = new BigDecimal(Optional.ofNullable(noticeInspectionDetailService.countError(weekStart,weekEnd,objectIdList)).orElse(0));
            BigDecimal weekNormalCount = weekCount.subtract(weekErrorCount);

            responseList.add(MonitorInspectionStatisticsVO.builder()
                    .timeScale("week")
                    .normalCount(weekNormalCount.longValue())
                    .inspectionCount(weekCount.longValue())
                    .normalRate(BigDecimal.ZERO.equals(weekCount) ? BigDecimal.ONE : weekNormalCount.divide(weekCount, 2, RoundingMode.HALF_UP))
                    .build());
            // 本月
            Date monthStart = DateConverterUtil.getMonthStart();
            Date monthEnd = DateConverterUtil.getMonthEnd();
            BigDecimal monthCount = new BigDecimal(Optional.ofNullable(noticeInspectionDetailService.countAll( monthStart, monthEnd,objectIdList)).orElse(0));
            BigDecimal monthErrorCount = new BigDecimal(Optional.ofNullable(noticeInspectionDetailService.countError(monthStart, monthEnd,objectIdList)).orElse(0));
            BigDecimal monthNormalCount = monthCount.subtract(monthErrorCount);

            responseList.add(MonitorInspectionStatisticsVO.builder()
                    .timeScale("month")
                    .normalCount(monthNormalCount.longValue())
                    .inspectionCount(monthCount.longValue())
                    .normalRate(BigDecimal.ZERO.equals(monthCount) ? BigDecimal.ONE : monthNormalCount.divide(monthCount, 2, RoundingMode.HALF_UP))
                    .build());

        } catch (ParseException e) {
            log.error("日期转换异常", e);
            throw new ApiException("日期转换异常", e);
        }
        return responseList;
    }
利用Page进行分页
 @Override
    public List<AlarmInfoVO> queryAlarmInfo(MonitorItemObjectRequest monitorItemRequest,PageRequest pageRequest){
        List<AlarmInfoVO> alarmInfoVOList = new ArrayList<>();
        LocalDate localDate = StringUtils.isBlank(monitorItemRequest.getDate())?LocalDate.now(): DateTimeUtil.parseDate(monitorItemRequest.getDate());
        String date = DateUtil.formatDate(localDate);
        NoticeMonitorPointQuery query = new NoticeMonitorPointQuery();
        query.setTypes(Arrays.asList(WarningTypeEnum.DZZH.getCode(),WarningTypeEnum.STSK.getCode()));
        query.setAreaCode(StringUtils.defaultIfBlank(monitorItemRequest.getAreaCode(),areaCode));
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = noticeMonitorObjectService.listNoticeMonitorPoint(query);
        Map<Long, NoticeMonitorPointDTO> mapNoticeMonitorPoint = noticeMonitorPointService.mapNoticeMonitorPoint(query);
        if(CollectionUtils.isEmpty(noticeMonitorObjectDTOList)){
            return new ArrayList<>();
        }
        String[] dateTime = DateUtil.getDateTime(localDate,0);
        String startTime = dateTime[0];
        String endTime = dateTime[1];

        List<Long> pointIds = noticeMonitorObjectDTOList.stream().filter(nmo -> nmo.getId().equals(monitorItemRequest.getObjectId())).flatMap(obj -> obj.getNoticeMonitorPoints().stream().map(mp -> mp.getId())).collect(Collectors.toList());

        LambdaQueryWrapper<NoticeAlarmRecord> noticeAlarmRecordLambdaQueryWrapper = Wrappers.lambdaQuery(NoticeAlarmRecord.class)
                .in(NoticeAlarmRecord::getPointId, pointIds)
                .between(NoticeAlarmRecord::getTriggerTime, startTime, endTime)
                .orderByDesc(NoticeAlarmRecord::getTriggerTime);
        List<NoticeAlarmRecord> alarmRecordList = new ArrayList<>();
        try{
            Page page = noticeAlarmRecordService.page(pageRequest.convertPage(), noticeAlarmRecordLambdaQueryWrapper);
            // List<NoticeAlarmRecord> alarmRecordList = noticeAlarmRecordService.getAlarmRecord(pointIds, startTime, endTime,monitorItemRequest.getType());
            alarmRecordList = page.getRecords();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        List<Long> recordIdList = alarmRecordList.stream().map(mp -> mp.getId()).collect(Collectors.toList());
        List<Long> recordPointIds = alarmRecordList.stream().map(rc->rc.getPointId()).collect(Collectors.toList());
        List<String> types = alarmRecordList.stream().map(rc -> rc.getItemCode()).collect(Collectors.toList());
        List<NoticeAlarmProcessRecord> processRecordList = noticeAlarmProcessRecordService.getProcessRecord(recordIdList);

        Map<Long, NoticeAlarmProcessRecord> noticeAlarmProcessRecordMap = processRecordList.stream().collect(Collectors.toMap(NoticeAlarmProcessRecord::getRecordId, Function.identity(), (key1, key2) -> key1));
        List<NoticeMonitorItem> noticeMonitorItemList = noticeMonitorItemService.countByPointIdAndType(recordPointIds,types);
        Map<String, NoticeMonitorItem> noticeMonitorItemMap = noticeMonitorItemList.stream().collect(Collectors.toMap(NoticeMonitorItem::getType, Function.identity(), (key1, key2) -> key1));
        for (NoticeAlarmRecord noticeAlarmRecord : alarmRecordList) {
            AlarmInfoVO alarmInfoVO = new AlarmInfoVO();
            alarmInfoVO.setDate(date);
            NoticeMonitorPointDTO noticeMonitorPointDTO = getNoticeMonitorObjectDTO(noticeAlarmRecord,mapNoticeMonitorPoint);
            NoticeMonitorObject noticeMonitorObject = noticeMonitorPointDTO == null ? new NoticeMonitorObjectDTO() : noticeMonitorPointDTO.getNoticeMonitorObject();
            alarmInfoVO.setRecordId(noticeAlarmRecord.getId());
            alarmInfoVO.setAreaDetail(noticeMonitorObject.getAreaDetail());
            alarmInfoVO.setAltitude(noticeMonitorPointDTO.getAltitude());
            alarmInfoVO.setLatitude(noticeMonitorPointDTO.getLatitude());
            alarmInfoVO.setLongitude(noticeMonitorPointDTO.getLongitude());
            alarmInfoVO.setName(noticeMonitorPointDTO.getName());
            NoticeMonitorItem noticeMonitorItem = noticeMonitorItemMap.get(noticeAlarmRecord.getItemCode());
            if(noticeMonitorItem!=null){
                alarmInfoVO.setItemCodeName(noticeMonitorItem.getName());
            }
            alarmInfoVO.setItemCode(noticeAlarmRecord.getItemCode());
            alarmInfoVO.setAlarmLevel(noticeAlarmRecord.getAlarmLevel());
            alarmInfoVO.setAlarmDesc(noticeAlarmRecord.getAlarmDesc());
            alarmInfoVO.setStatus(noticeAlarmRecord.getStatus());
            alarmInfoVO.setAlarmDate(noticeAlarmRecord.getTriggerTime());
            NoticeAlarmProcessRecord noticeAlarmProcessRecord = noticeAlarmProcessRecordMap.get(noticeAlarmRecord.getId());
            if(noticeAlarmProcessRecord != null){
                alarmInfoVO.setProcessUser(noticeAlarmProcessRecord.getProcessUser());
                alarmInfoVO.setProcessDate(noticeAlarmProcessRecord.getGmtCreate());
            }
            alarmInfoVOList.add(alarmInfoVO);
        }

        return alarmInfoVOList;
    }
流式编程过滤相关参数
    /**
    * 返回监测点ID
    */
    public List<Long> listPointId(List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList,String type){
        Stream<NoticeMonitorObjectDTO> stream = noticeMonitorObjectDTOList.stream();
        if(StringUtils.isNotBlank(type)){
            stream = stream.filter(obj -> StringUtils.equalsIgnoreCase(obj.getType(), type));
        }
        return stream.flatMap(obj -> obj.getNoticeMonitorPoints().stream().map(mp -> mp.getId())).collect(Collectors.toList());
    }
对近七天数据进行保存并且过滤
    @Override
    public List<WarningVO> queryDay7warning(WarningRequest warningRequest) {
        List<WarningVO> list = new ArrayList<>();
        LocalDate localDate = StringUtils.isBlank(warningRequest.getDate())?LocalDate.now(): DateTimeUtil.parseDate(warningRequest.getDate());
        NoticeMonitorPointQuery query = new NoticeMonitorPointQuery();
        query.setTypes(Arrays.asList(WarningTypeEnum.DZZH.getCode(),WarningTypeEnum.STSK.getCode()));
        query.setAreaCode(StringUtils.defaultIfBlank(warningRequest.getAreaCode(),areaCode));
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = noticeMonitorObjectService.listNoticeMonitorPoint(query);
        List<Long> dzzhPointIds = listPointId(noticeMonitorObjectDTOList, WarningTypeEnum.DZZH.getCode());
        List<Long> stskPointIds = listPointId(noticeMonitorObjectDTOList, WarningTypeEnum.STSK.getCode());
        List<WarningSumVO> dzzhList = listWarnType(warningRequest,dzzhPointIds);
        List<WarningSumVO> stskList = listWarnType(warningRequest,stskPointIds);
        int n = 0;
        while(n < 7){
            String[] dateTime = DateUtil.getDateTime(localDate, -n);
            WarningVO warning = new WarningVO();
            warning.setDate(dateTime[0].substring(0,dateTime[0].length()-9));
            warning.setDzzhSum(0L);
            warning.setStskSum(0L);
            list.add(warning);
            n++;
        }
        if(dzzhList.isEmpty()&&stskList.isEmpty()){
            return list;
        }
        list.forEach(warningVO -> {
           dzzhList.forEach(dzzh->{
               if(StringUtils.equals(dzzh.getDate(),warningVO.getDate())){
                   warningVO.setDzzhSum(dzzh.getWarningSum());
               }
           });
           stskList.forEach(stsk->{
               if(StringUtils.equals(stsk.getDate(),warningVO.getDate())){
                   warningVO.setStskSum(stsk.getWarningSum());
               }
           });
        });
        return list;
    }

    public List<WarningSumVO> listWarnType(WarningRequest warningRequest,List<Long> points){
        try {
            LocalDate localDate = StringUtils.isBlank(warningRequest.getDate())?LocalDate.now(): DateTimeUtil.parseDate(warningRequest.getDate());
            String startTime = "";
            String endTime = "";
            String[] dateTime = DateUtil.getDateTime(localDate,-6);
            startTime = dateTime[0];
            endTime = dateTime[1];
            NoticeRecordQuery noticeRecordQuery = new NoticeRecordQuery();
            noticeRecordQuery.setPointIds(points);
            noticeRecordQuery.setStartDate(startTime);
            noticeRecordQuery.setEndDate(endTime);
            List<WarningSumVO> list = noticeAlarmRecordService.countWarnType(noticeRecordQuery);
            return list;
        }catch (Exception e){
            log.error("areacode异常", e);
            return new ArrayList<>();
        }
    }

trms_service(测试报告管理系统)

整合OSS实现文件上传下载与预览
搭建项目

IDEA连接数据库,使用MybatisPlus生成所有表的entity,service,mapper和controller,再配置swagger和全局异常处理以及通用返回体,引入依赖如下:

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.10.2</version>
</dependency>

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-extension</artifactId>
    <version>3.4.2</version>
</dependency>
<!--Mybatis-Plus-Generator-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
    <exclusions>
        <exclusion>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--Velocity-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>
application.yml添加配置
swagger:
  base-packages: com.example.trmsfile.controller
  description: 文件项目接口文档
  title: 文件项目接口文档
server:
  port: 9090
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://172.16.218.82/trms_data?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: MyPass123@
  servlet:
    multipart:
      enabled: true
      max-file-size: -1
      max-request-size: -1

#mybatis-plus配置
mybatis-plus:
  mapper-locations: classpath*:mapper/*Mapper.xml
  #实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.example.**.entity
  #typeEnumsPackage: com.qxwz.enums
  global-config:
    # 关闭MP3.0自带的banner
    banner: true
    db-config:
      #主键类型  0:"数据库ID自增", 1:"不操作", 2:"用户输入ID",3:"数字型snowflake", 4:"全局唯一ID UUID", 5:"字符串型snowflake";
      id-type: assign_id
      #字段策略
      insert-strategy: not_null
      update-strategy: not_null
      select-strategy: not_empty
      #驼峰下划线转换
      table-underline: true
      # 逻辑删除配置
      # 逻辑删除全局值(1表示已删除,这也是Mybatis Plus的默认配置)
      logic-delete-value: 1
      # 逻辑未删除全局值(0表示未删除,这也是Mybatis Plus的默认配置)
      logic-not-delete-value: 0
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
file:
  path: "/Users/zuoping.liu/Documents/"
编写OssService与OssServiceImpl
/**
 * oss 业务接口
 *
 */
public interface OssService {

    String uploadFile (MultipartFile file, String fileName);

    void preview (Long rid, HttpServletResponse response);

    void downloadFile (Long rid, HttpServletResponse response) throws IOException;

    void deleteOneFile (String objectName);

    void deleteFileList (List<String> keys);
}

@Service
@Slf4j
public class OssServiceImpl implements OssService {
    @Autowired
    ReportMapper reportMapper;

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
    private String baseFolder;

    @PostConstruct
    public void init () {
        this.endpoint = "https://oss-cn-shanghai.aliyuncs.com";
        //this.victoriaConfig.configs().getProperty("OSS_ENDPOINT");
        this.accessKeyId = "LTAI5t8Ld9BBLiZC4Pw3R11N";
        //this.victoriaConfig.configs().getProperty("OSS_KEY_ID");
        this.accessKeySecret = "Dc2k4l8Et4Vot0HaR9yzRnqkNZHJPQ";
        this.bucketName = "dragon-wu-2";
        this.baseFolder = "trms-file";
    }

    @Override
    public String uploadFile (MultipartFile file, String fileName) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        InputStream inputStream = null;
        String objectName = "";
        try {
            inputStream = file.getInputStream();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM");
            String date = simpleDateFormat.format(new Date());
            objectName = this.baseFolder + "/" + date + "/" + UUIDUtil.getUUID() + "_" + fileName;
            ossClient.putObject(bucketName, objectName, inputStream);
        } catch (Exception e) {
            log.error("上传文件失败", e);
        }
        ossClient.shutdown();
        return objectName;
    }

    @Override
    public void downloadFile (Long rid, HttpServletResponse response) throws IOException {
        Report report = this.reportMapper.selectById(rid);
        String objectName = report.getPath();
        String title = report.getTitle();
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        OSSObject ossObject = ossClient.getObject(bucketName, objectName);
        BufferedInputStream bis = new BufferedInputStream(ossObject.getObjectContent());
        response.setHeader("Content-Disposition",
                "attachment;filename="
                        + new String(title.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
        response.setHeader("Content-Type",
                "application/octet-stream");
        BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
        this.write(bis, bos);
        ossClient.shutdown();
    }

    @Override
    public void preview (Long rid, HttpServletResponse response) {
        Report report = this.reportMapper.selectById(rid);
        String objectName = report.getPath();
        String title = report.getTitle();
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        OSSObject ossObject = ossClient.getObject(bucketName, objectName);
        InputStream fileStream = ossObject.getObjectContent();
        HttpUtil.addCommonResponseHeader(title, response);
        try {
            BufferedInputStream bis = new BufferedInputStream(fileStream);
            BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
            this.write(bis, bos);
        } catch (Exception e) {
            log.error("预览文件失败", e);
        }
        ossClient.shutdown();
    }

    @Override
    public void deleteOneFile (String objectName) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.deleteObject(bucketName, objectName);
        ossClient.shutdown();
    }

    @Override
    public void deleteFileList (List<String> keys) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(keys));
        ossClient.shutdown();
    }

    private void write(BufferedInputStream bis, BufferedOutputStream bos) throws IOException {
        int len;
        byte[] b = new byte[1024];
        while ((len = bis.read(b)) != -1) {
            bos.write(b, 0, len);
        }
        bos.close();
        bis.close();
    }
}
ReportService与ReportServiceImpl
public interface ReportService extends IService<Report> {
        UploadFileResponse uploadFile1(MultipartFile file);
}

@Service
@Slf4j
public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> implements ReportService {
    @Autowired
    ReportMapper reportMapper;
    @Autowired
    private OssService ossService;
    @Override
    public UploadFileResponse uploadFile1(MultipartFile file) {
        String fileName = org.springframework.util.StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
        if (fileName.contains("..")) {
            log.error("File name contains invalid path sequence");
        }
        String realName = this.ossService.uploadFile(file, fileName);
        Report report = new Report();
        report.setUid("zuoping.liu");
        report.setVersionLock(0);
        report.setTitle(fileName);
        report.setPath(realName);
        report.setSize(FileUtil.getFileSizeDesc(file.getSize()));
        LocalDateTime localDateTime = LocalDateTime.now();
        report.setCreateTime(localDateTime);
        report.setLastUpdateTime(localDateTime);
        report.setDelFlag(false);
        report.setCid0(191L);
        // report表新增
        int result = reportMapper.insert(report);
        if (result <= 0) {
            log.error("新增报告失败, sql返回结果: {}", result);
            return null;
        }
        return new UploadFileResponse(fileName, file.getContentType(), FileUtil.getFileSizeDesc(file.getSize()), realName);
    }
}
ReportController
@RestController
@RequestMapping("api/trms/report")
@Api(tags = "文件项目接口")
@Slf4j
public class ReportController {

    @Autowired
    ReportService reportService;

    @Autowired
    private OssService ossService;
    @ApiOperation(value = "1、上传文件",
            notes = "上传文件")
    @ApiOperationSupport(order = 1)
    @PostMapping(value = "uploadFile1")
    public Result<UploadFileResponse> uploadFile(HttpServletRequest request,
                                                 @RequestParam("file1") MultipartFile file) {
        UploadFileResponse data = reportService.uploadFile1(file);
        return Result.success(data);
    }

    @ApiOperation(value = "1、下载文件",
            notes = "下载文件")
    @ApiOperationSupport(order = 1)
    @GetMapping("downloadFile/{rid}")
    public ResponseEntity<Void> downloadFile(@PathVariable("rid") Long rid,
                                             HttpServletResponse response,
                                             HttpServletRequest request) throws IOException {
  
        this.ossService.downloadFile(rid, response);
        return ResponseEntity.ok().build();
    }

    @ApiOperation(value = "2、预览文件",
            notes = "预览文件")
    @ApiOperationSupport(order = 2)
    @GetMapping("preview/{rid}")
    public ResponseEntity<Void> preview(@PathVariable("rid") Long rid,
                                        HttpServletResponse response,
                                        HttpServletRequest request) {
        this.ossService.preview(rid, response);
        return ResponseEntity.ok().build();
    }
相关中间类
//constant包下
public class CommonConstant {

    public static final String NULL_STR = "null";
    public static final String SLASH_STR = "/";
    public static final String UTF_8_STR = "UTF-8";
    public static final String COMMON_SEPARATOR = "__,__";
    public static final String EMPTY_STR = "";
    public static final String POINT_STR = ".";
    public static final String HYPHEN_STR = "-";
    public static final Long ZERO_LONG = 0L;
    public static final Long TWO_LONG = 2L;
    public static final String ZERO_STR = "0";
    public static final Integer ZERO_INT = 0;
    public static final Integer ONE_INT = 1;
    public static final Integer TWO_INT = 2;
    public static final Integer MINUS_ONE_INT = -1;
    public static final String TOP_STR = "TOP";
    public static final String TRUE_STR = "true";

    public static final Long ONE_DAY_LONG = 24L * 60L * 60L * 1000L;
    public static final Long FIVE_MINUTES_LONG = 5L * 60L * 1000L;
    public static final Long ONE_HOUR_LONG = 60L * 60L * 1000L;

    public static final String CONTENT_TYPE_STR = "content-type";
    /**
     * 登录用户ID的key
     */
    public static final String LOGIN_USER_ID = "LOGIN_USER_ID";

    /**
     * 分享ID
     */
    public static final String SHARE_ID = "SHARE_ID";

    /**
     * 用户登录后存储redis的key前缀
     */
    public static final String USER_LOGIN_PREFIX = "USER_LOGIN_";

    /**
     * 用户校验分享码通过之后存储redis的key前缀
     */
    public static final String SHARE_CODE_PREFIX = "SHARE_CODE_";

    /**
     * 跨域设置枚举类
     */
    public enum CorsConfigEnum {
        /**
         * 允许所有远程访问
         */
        CORS_ORIGIN("Access-Control-Allow-Origin", "*"),
        /**
         * 允许认证
         */
        CORS_CREDENTIALS("Access-Control-Allow-Credentials", "true"),
        /**
         * 允许远程调用的请求类型
         */
        CORS_METHODS("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT"),
        /**
         * 指定本次预检请求的有效期,单位是秒
         */
        CORS_MAX_AGE("Access-Control-Max-Age", "3600"),
        /**
         * 允许所有请求头
         */
        CORS_HEADERS("Access-Control-Allow-Headers", "*");

        private String key;
        private String value;

        CorsConfigEnum(String key, String value) {
            this.key = key;
            this.value = value;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}
//exception包下
public class FileStorageException extends RuntimeException {
    public FileStorageException(String message) {
        super(message);
    }

    public FileStorageException(String message, Throwable cause) {
        super(message, cause);
    }
}
//resp包下
@Data
@AllArgsConstructor
public class UploadFileResponse {

    private String fileName;

    private String fileType;

    private String fileSize;

    private String realName;

}
//util包下
public class FileUtil {
    private static final String KB_STR = "K";
    private static final String MB_STR = "M";
    private static final String GB_STR = "G";
    private static final Integer UNIT = 1024;
    private static final String FILE_SIZE_DESC_FORMAT = "%.2f";
    /**
     * 获取文件大小字符串
     */
    public static String getFileSizeDesc(long size) {
        double fileSize = (double) size;
        String fileSizeSuffix = KB_STR;
        fileSize = fileSize / UNIT;
        if (fileSize > UNIT) {
            fileSize = fileSize / UNIT;
            fileSizeSuffix = MB_STR;
        }
        if (fileSize > UNIT) {
            fileSize = fileSize / UNIT;
            fileSizeSuffix = GB_STR;
        }
        return String.format(FILE_SIZE_DESC_FORMAT, fileSize) + fileSizeSuffix;
    }

    /**
     * 通过文件名获取文件后缀
     */
    public static String getFileSuffix(String fileName) {
        if (StringUtils.isBlank(fileName) || (fileName.indexOf(CommonConstant.POINT_STR) == CommonConstant.MINUS_ONE_INT)) {
            return CommonConstant.EMPTY_STR;
        }
        return fileName.substring(fileName.lastIndexOf(CommonConstant.POINT_STR)).toLowerCase();
    }
}

public class HttpUtil {
    /**
     * 添加跨域的响应头
     *
     * @param response
     */
    public static void addCorsResponseHeader(HttpServletResponse response) {
        response.setHeader(CommonConstant.CorsConfigEnum.CORS_ORIGIN.getKey(), CommonConstant.CorsConfigEnum.CORS_ORIGIN.getValue());
        response.setHeader(CommonConstant.CorsConfigEnum.CORS_CREDENTIALS.getKey(), CommonConstant.CorsConfigEnum.CORS_CREDENTIALS.getValue());
        response.setHeader(CommonConstant.CorsConfigEnum.CORS_METHODS.getKey(), CommonConstant.CorsConfigEnum.CORS_METHODS.getValue());
        response.setHeader(CommonConstant.CorsConfigEnum.CORS_MAX_AGE.getKey(), CommonConstant.CorsConfigEnum.CORS_MAX_AGE.getValue());
        response.setHeader(CommonConstant.CorsConfigEnum.CORS_HEADERS.getKey(), CommonConstant.CorsConfigEnum.CORS_HEADERS.getValue());
    }

    public static void addCommonResponseHeader(String title, HttpServletResponse response) {
        response.reset();
        HttpUtil.addCorsResponseHeader(response);
        if (title.substring(title.length()-4, title.length()).equals(".pdf")) {
            response.setHeader(CommonConstant.CONTENT_TYPE_STR, "application/pdf");
            response.setContentType("application/pdf");
        } else if (title.substring(title.length()-4, title.length()).equals(".doc")) {
            response.setHeader(CommonConstant.CONTENT_TYPE_STR, "application/msword");
            response.setContentType("application/msword");
        } else {
            response.setHeader(CommonConstant.CONTENT_TYPE_STR, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        }
    }
}

public class UUIDUtil {
    public static String getUUID() {
        return UUID.randomUUID().toString().replace("-","").toUpperCase();
    }
}
本地实现文件上传下载与预览
ReportService与ReportServiceImpl
public interface ReportService extends IService<Report> {
        void preview(Long id, HttpServletResponse response);

        String storeFile(MultipartFile file);

        Resource loadFileAsResource(String fileName) throws MalformedURLException;

        UploadFileResponse uploadFile2(MultipartFile file);
}

@Service
@Slf4j
public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> implements ReportService {
    @Autowired
    ReportMapper reportMapper;
    @Autowired
    private OssService ossService;
    @Value("${file.path}")
    private String fileStorageLocation;

    @Override
    public UploadFileResponse uploadFile2(MultipartFile file) {
        Report report = new Report();
        report.setUid("zuoping.liu");
        report.setVersionLock(0);

        // 处理展示的重复文件名
        List<String> titleList = reportMapper.selectTitleList("zuoping.liu");
        String filename = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
        String realName = storeFile(file);
        String suffix = FileUtil.getFileSuffix(filename);
        StringBuilder sb = new StringBuilder();
        if (!org.apache.commons.lang3.StringUtils.isBlank(filename) && titleList.contains(filename)){
            List<String> filter = titleList.stream().filter(title -> title.contains(filename)
                    || title.contains(filename.substring(0, filename.lastIndexOf(".")) + "(")).collect(Collectors.toList());
            sb.append(filename, 0, filename.lastIndexOf("."))
                    .append("(")
                    .append(filter.size())
                    .append(")")
                    .append(FileUtil.getFileSuffix(suffix));
        }else {
            sb.append(filename);
        }
        report.setTitle(sb.toString());
        report.setPath(realName);
        report.setSize(FileUtil.getFileSizeDesc(file.getSize()));
        LocalDateTime localDateTime = LocalDateTime.now();
        report.setCreateTime(localDateTime);
        report.setLastUpdateTime(localDateTime);
        report.setDelFlag(false);
        report.setCid0(191L);
        // report表新增
        int result = reportMapper.insert(report);
        if (result <= 0) {
            log.error("新增报告失败, sql返回结果: {}", result);
            return null;
        }
        return new UploadFileResponse(filename,file.getContentType(),FileUtil.getFileSizeDesc(file.getSize()), realName);
    }

    @Override
    public void preview(Long id, HttpServletResponse response) {
        String path = fileStorageLocation+"/"+reportMapper.selectReportPathById(id);
        if (Objects.isNull(path)) {
            throw new FileStorageException("文件不存在");
        }
        preview(new File(path),id, response);
    }
    /**
     * 文件预览
     */
    private void preview(File file,Long id, HttpServletResponse response) {
        addCommonResponseHeader(id,response);
        writeFile2Response(file, response);
    }
    /**
     * 添加公用的响应头
     */
    private void addCommonResponseHeader(Long id,HttpServletResponse response) {
        response.reset();
        HttpUtil.addCorsResponseHeader(response);
        String title = reportMapper.selectReportTitleById(id);
        if(title.substring(title.length()-4,title.length()).equals(".pdf")){
            response.setHeader(CommonConstant.CONTENT_TYPE_STR, "application/pdf");
            response.setContentType("application/pdf");
        }else if(title.substring(title.length()-4,title.length()).equals(".doc")){
            response.setHeader(CommonConstant.CONTENT_TYPE_STR, "application/msword");
            response.setContentType("application/msword");
        }else{
            response.setHeader(CommonConstant.CONTENT_TYPE_STR, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        }
    }
    /**
     * 单位长度
     */
    private static final Integer UNIT_INT = 1024;
    /**
     * 文件写入响应实体
     */
    private void writeFile2Response(File file, HttpServletResponse response) {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
            byte[] buffer = new byte[UNIT_INT];
            OutputStream os = response.getOutputStream();
            int i = bis.read(buffer);
            while (i != CommonConstant.MINUS_ONE_INT) {
                os.write(buffer, CommonConstant.ZERO_INT, i);
                i = bis.read(buffer);
            }
        } catch (Exception e) {
            //log.error("文件写入响应实体失败", e);
        }
    }

    @Override
    public String storeFile(MultipartFile file) {
        String filename = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
        String suffix = FileUtil.getFileSuffix(filename);
        // 处理展示的重复文件名
        List<String> titleList = reportMapper.selectTitleList("zuoping.liu");
        StringBuilder sb = new StringBuilder();
        if (!org.apache.commons.lang3.StringUtils.isBlank(filename) && titleList.contains(filename)){
            List<String> filter = titleList.stream().filter(title -> title.contains(filename)
                    || title.contains(filename.substring(0, filename.lastIndexOf(".")) + "(")).collect(Collectors.toList());
            sb.append(filename, 0, filename.lastIndexOf("."))
                    .append("(")
                    .append(filter.size())
                    .append(")")
                    .append(FileUtil.getFileSuffix(suffix));
        }else {
            sb.append(filename);
        }
        try {
            if (filename.contains("..")) {
                throw new FileStorageException("File name contains invalid path sequence");
            }
            String realName = sb.toString();
            String targetLocation = this.fileStorageLocation + realName;
            File dest = new File(targetLocation);
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            file.transferTo(dest);
            return realName;
        } catch (IOException ex) {
            throw new FileStorageException("Could not store file " + filename + ". Please try again later!", ex);
        }
    }

    @Override
    public Resource loadFileAsResource(String fileName) throws MalformedURLException {
        Path path = Paths.get(fileStorageLocation);
        Path targetPath = path.resolve(fileName).normalize();
        Resource resource = new UrlResource(targetPath.toUri());
        return resource;
    }
}
ReportController
@RestController
@RequestMapping("api/trms/report")
@Api(tags = "文件项目接口")
@Slf4j
public class ReportController {

    @Autowired
    ReportService reportService;

    @Autowired
    private OssService ossService;

    @ApiOperation(value = "2、上传文件2",
            notes = "上传文件2")
    @ApiOperationSupport(order = 2)
    @PostMapping(value = "/uploadFile2")
    public Result<UploadFileResponse> uploadFile2(HttpServletRequest request,
                               @RequestParam("file2") MultipartFile file){
        UploadFileResponse data = reportService.uploadFile2(file);
        return Result.success(data);
    }

    @ApiOperation(value = "3、下载文件2",
            notes = "下载文件")
    @ApiOperationSupport(order = 3)
    @GetMapping("/downloadFile")
    public ResponseEntity<Resource> downloadFile2(@RequestParam("fileName") String fileName, HttpServletRequest request) throws MalformedURLException {
        Resource resource = reportService.loadFileAsResource(fileName);
        String contentType = null;
        try{
            contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
        }catch (IOException ex){
            log.info("Could not determine file type");
        }
        if (contentType == null){
            contentType = "application/octet-stream";
        }
        return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType))
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    }

    @ApiOperation(value = "4、预览文件2",
            notes = "预览文件")
    @ApiOperationSupport(order = 4)
    @GetMapping("preview")
    public void preview2(@RequestParam("id") Long id,
                        HttpServletResponse response){
        reportService.preview(id,response);
    }
}
处理展示的重复文件名
        // 处理展示的重复文件名
        List<String> titleList = reportMapper.selectTitleList("zuoping.liu");
        String filename = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
        String realName = storeFile(file);
        String suffix = FileUtil.getFileSuffix(filename);
        StringBuilder sb = new StringBuilder();
        if (!org.apache.commons.lang3.StringUtils.isBlank(filename) && titleList.contains(filename)){
            List<String> filter = titleList.stream().filter(title -> title.contains(filename)
                    || title.contains(filename.substring(0, filename.lastIndexOf(".")) + "		  (")).collect(Collectors.toList());
            sb.append(filename, 0, filename.lastIndexOf("."))
                    .append("(")
                    .append(filter.size())
                    .append(")")
                    .append(FileUtil.getFileSuffix(suffix));
        }else {
            sb.append(filename);
        }
        report.setTitle(sb.toString());
Java HashMap compute() 方法:

compute() 方法对 hashMap 中指定 key 的值进行重新计算。

@Select("select uid, count(*) from report where YEARWEEK( date_format(  create_time,'%Y-%m-%d' ) ) = YEARWEEK( now() ) group by uid order by count(*) desc")
List<Map<Object, Object>> selectWeekDetail();
List<Map<Object, Object>> maps1 = this.reportMapper.selectWeekDetail();
Map<String, Object> weekData = new HashMap<>();
for (Map<Object, Object> map : maps1) {
    String uid = (String) map.get("uid");
    String fullName = commonServiceImpl.getStaffNameByDomain(uid);
    weekData.put(fullName, map.get("count(*)"));
}
            weekData.compute(key, (k, v) -> {
                if (v == null) {
                    item.setWeekUpload(0l);
                    return 0;
                } else {
                    item.setWeekUpload((Long)v);
                    return v;
                }
            });
Java ArrayList sort() 方法:
List<ItemAnalysis> itemAnalyses = new ArrayList<>();
itemAnalyses.sort((r1, r2) -> {
    Long t1 = r1.getTotalUpload();
    Long t2 = r2.getTotalUpload();
    return t2.compareTo(t1);//倒序排序;t1.compareTo(t2):正序排序
});
查询本周与上周数据
//本周
//detail
List<Map<Object, Object>> maps1 = this.reportMapper.selectWeekDetail();
//报告总数
Long weekUpload = this.reportMapper.selectWeekUpload();
//上周报告总数
Long lastWeekUpload = this.reportMapper.selectLastWeekUpload();

    @Select("select count(*) from report where YEARWEEK( date_format(  create_time,'%Y-%m-%d' ) ) = YEARWEEK( now() )")
    Long selectWeekUpload();

    @Select("select count(*) from report where YEARWEEK(date_format(create_time,'%Y-%m-%d')) = YEARWEEK(now())-1")
    Long selectLastWeekUpload();
通用mapper中多表关联查询自定义mapper.xml
@Table(name = "category")
@Data
public class Category implements Serializable {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private Long parentId;
    private Integer access;
    private Boolean isParent;
    private String name;
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    private Date lastUpdateTime;
    private transient List<Map<String, Long>> referList;
}
@Data
@Table(name = "sys_group")
public class Group implements Serializable {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private String name;
    private String description;
    private Boolean isExclusive;
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    private Date lastUpdateTime;
    private transient List<Map<String, String>> memberList;
    private transient List<Map<String, Long>> referList;
}
    <resultMap id="categoryMap" type="com.qxwz.qa.trms.pojo.Category">
        <result property="id" jdbcType="BIGINT" column="id" javaType="Long"/>
        <result property="parentId" jdbcType="BIGINT" column="parent_id" javaType="Long"/>
        <result property="name" jdbcType="VARCHAR" column="name"/>
        <result property="access" jdbcType="INTEGER" column="access"/>
        <result property="lastUpdateTime" jdbcType="TIMESTAMP" column="last_update_time"/>
        <collection property="referList" resultMap="referMap"/>
    </resultMap>
    <resultMap id="referMap" type="Map">
        <result property="key" jdbcType="BIGINT" column="rid" javaType="Long"/>
    </resultMap>
    <select id="selectAllWithRefer" resultMap="categoryMap">
        SELECT c.*, r.id rid
        FROM category c
        left join report r on r.cid = c.id or r.cid0 = c.id
        order by c.id
    </select>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.qxwz.qa.trms.mapper.GroupMapper">
    <resultMap id="groupMap" type="com.qxwz.qa.trms.pojo.Group">
        <result property="id" jdbcType="BIGINT" column="id" javaType="Long"/>
        <result property="name" jdbcType="VARCHAR" column="name"/>
        <result property="description" jdbcType="VARCHAR" column="description"/>
        <result property="lastUpdateTime" jdbcType="TIMESTAMP" column="last_update_time"/>
        <collection property="memberList" resultMap="memberMap"/>
        <collection property="referList" resultMap="referMap"/>
    </resultMap>
    <resultMap id="memberMap" type="Map">
        <result property="key" jdbcType="VARCHAR" column="user_key" javaType="String"/>
    </resultMap>
    <resultMap id="referMap" type="Map">
        <result property="key" jdbcType="BIGINT" column="group_id" javaType="Long"/>
    </resultMap>
    <select id="selectAllWithMemberAndRefer" resultMap="groupMap">
        SELECT sg.*, gu.user_key, rg.group_id
        FROM sys_group sg
        left join group_user gu on gu.group_id = sg.id
        left join report_group_access rg on rg.group_id = sg.id
        where sg.is_exclusive = 0
        order by sg.id
    </select>
</mapper>
tkmapper单表操作自定义SQL
    @Override
    public List<Category> queryCategoryListByIds(List<Long> cids) {
        return this.categoryMapper.selectByIds(cids);
    }
    @Select("<script>" +
            "select * from category where id in " +
            "<foreach collection='list' open='(' item='id' separator=',' close=')'> #{id}" +
            "</foreach>" +
            "</script>")
    List<Category> selectByIds(List<Long> cids);
流式编程stream
/**
 * 题目要求: 一分钟内完成此题:只能用一行代码实现!
 * 现有5个用户!筛选:
 * 1. ID必须是偶数
 * 2. 年龄必须大于23岁
 * 3. 用户名转为大写字母
 * 4. 用户名字母倒着排序
 * 5. 只输出一个用户!
 */
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 21);
        User u2 = new User(2, "b", 22);
        User u3 = new User(3, "c", 23);
        User u4 = new User(4, "d", 24);
        User u5 = new User(6, "e", 25);
        // 集合就是存储
        List<User> users = Arrays.asList(u1, u2, u3, u4, u5);
        users.stream()
                .filter(u -> u.getId() % 2 == 0)
                .filter(u -> u.getAge() > 23)
                .map(u -> u.getName().toUpperCase())
                .sorted(Comparator.reverseOrder())
                .limit(1)
                .forEach(System.out::println);
    }
}
        List<String> fullNameList = staffList.stream()
                .map(item -> commonService.getStaffNameByDomain(item))
                .filter(item -> !item.isEmpty())
                .collect(Collectors.toList());
        List<Group> groups = this.queryAllWithMemberAndRefer();
        List<String> lists = new ArrayList<>();
        for (Group group : groups) {
            List<String> staffNames = group.getMemberList()
                    .stream().map(item -> item.get("key"))
                    .collect(Collectors.toList());
            StringBuilder sb = new StringBuilder();
            for (String member : staffNames) {
                String fullName = commonService.getStaffNameByDomain(member);
                if (StringUtils.isNotEmpty(fullName)) {
                    sb.append(fullName);
                    if (staffNames.indexOf(member) != staffNames.size()-1) {
                        sb.append(", ");
                    }
                }
            }
            lists.add(sb.toString());
        }
        return lists;
自定义异常与通用异常处理
//exception包下新增
public class BusinessException extends RuntimeException{
    private int code;
    private String msg;

    public BusinessException(int code, String msg){
        super(msg);
        this.code = code;
        this.msg = msg;
    }

    public  BusinessException(){}

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

public class FileStorageException extends RuntimeException {
    public FileStorageException(String message) {
        super(message);
    }
    public FileStorageException(String message, Throwable cause) {
        super(message, cause);
    }
}


@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger LOGGER =  LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(value = BusinessException.class)
    public JsonData handleBusinessException(BusinessException e, HttpServletRequest request){
        LOGGER.error("url {}, code {}, msg {}",  request.getRequestURL(), e.getCode(), e.getMsg());
        return new JsonData(e.getCode(), e.getMsg());
    }

    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public JsonData<Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request){
        LOGGER.error("url {}, msg {}", request.getRequestURL(), e.getMessage());
        return new JsonData<>(StateType.BAD_REQUEST.getCode(), "缺少必需的请求参数");
    }

    @ExceptionHandler(value = Exception.class)
    public JsonData<Object>  handleException(Exception e, HttpServletRequest request){
        LOGGER.error("url {}, msg {}", request.getRequestURL(), e.getMessage());
        return new JsonData<>(StateType.INTERNAL_SERVER_ERROR.getCode(),StateType.INTERNAL_SERVER_ERROR.value());
    }
}
包装类的使用
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
public Long create(AlarmConfigCreateRequest request, String createUser) {
    request.validate();

    Long id = alarmConfigDao.save(alarmConfigConvertor.request2Entity(request, createUser));

    saveRule(request.getFieldConfig(), id);
    return id;
}

@Slf4j
@Component
public class AlarmConfigConvertor {

    public NoticeAlarmConfigEntity request2Entity(AlarmConfigCreateRequest request, String createUser) {
        return Optional.ofNullable(request)
                .map(s -> new NoticeAlarmConfigEntity()
                        .setEnv(AppEnv.getDeployEnv())
                        .setItemType(s.getItemCode())
                        .setName(s.getName())
                        .setObjectId(s.getObjectId())
                        .setSendFrequency(s.getSendFrequency())
                        .setConfigStatus(AlarmConfigStatusConstant.ENABLE)
                        .setPoints(JSON.toJSONString(s.getPointIdList()))
                        .setIsDeleted(false)
                        .setCreateUser(createUser)
                        .setModifyUser(createUser))
                .orElse(null);
    }
  
    public List<FieldSummary> indexConfig2FieldSummaryList(List<NoticeIndexConfigEntity> source) {
        return Optional.ofNullable(source).map(entities ->
                entities.stream().map(x  -> indexConfig2FieldSummary(x)).collect(Collectors.toList())
        ).orElse(new ArrayList<>());
    }

    public FieldSummary indexConfig2FieldSummary(NoticeIndexConfigEntity source) {
        return Optional.ofNullable(source).map(x -> new FieldSummary()
                .setKey(x.getSensorIndex())
                .setName(x.getSensorIndexName())
                .setUnitCode(x.getUnit())
                .setUnitName(x.getUnitName())
                .setDesc(x.getRemark())).orElse(null);
    }
}
Optional
@Component
public class NoticeItemAuth implements BaseAuthData{

    @Autowired
    private MonitorObjectDao monitorObjectDao;

    @Autowired
    private MonitorObjectMemberRefDao monitorObjectMemberRefDao;

    @Autowired
    private MonitorObjectPointRefDao monitorObjectPointRefDao;

    @Autowired
    private MonitorPointDao monitorPointDao;

    @Autowired
    private MonitorItemDao monitorItemDao;

    @Override
    public List<NoticeItemAuthModel> getAuthList(SessionInfoUser user) {
        // 获取监测对象信息
        List<Long> objectIds = Lists.newArrayList();
        if (LoginMemberTypeEnum.SUB_USER.getValue().equals(user.getLoginMemberType())) {
            objectIds = monitorObjectMemberRefDao.queryObjectIdListByUserId(user.getSessionInfoSubUser().getSubMemberId());
            if (CollectionUtils.isNotEmpty(objectIds)) {
                objectIds.addAll(Optional.ofNullable(monitorObjectMemberRefDao.queryObjectIdListByUserId(user.getSessionInfoSubUser().getSubMemberId()))
                        .orElseGet(ArrayList::new));
            }
        } else {
            objectIds.addAll(Optional.ofNullable(monitorObjectDao.queryList(new NoticeMonitorObjectEntity().setCreateUser(String.valueOf(user.getMemberId())), Lists.newArrayList()))
                    .orElseGet(ArrayList::new)
                    .stream()
                    .map(NoticeMonitorObjectEntity::getId)
                    .collect(Collectors.toList()));

        }

        List<Long> pointIds = Lists.newArrayList();
        // 获取监测点信息
        if (CollectionUtils.isNotEmpty(objectIds)) {
            List<String> pointCodeList = monitorObjectPointRefDao.queryByMonitorIds(objectIds);
            if (CollectionUtils.isNotEmpty(pointCodeList)) {
                pointIds.addAll(Optional.ofNullable(monitorPointDao.queryIdByCode(pointCodeList)).orElseGet(ArrayList::new));
            }
        }

        // 获取检测项信息
        if (CollectionUtils.isNotEmpty(pointIds)) {
            return Optional.ofNullable(monitorItemDao.queryListByPointIds(pointIds))
                    .orElseGet(ArrayList::new)
                    .stream()
                    .map(item -> {
                        NoticeItemAuthModel model = new NoticeItemAuthModel();
                        model.setId(item.getId());
                        return model;
                    })
                    .collect(Collectors.toList());
        }

        return Lists.newArrayList();
    }
}
LinkedHashMap、Stream、switch
@Data
@Accessors(chain = true)
public class CommonQueryResult implements Serializable {

    private static final long serialVersionUID = -7168106982749055931L;

    /**
    * 度量
    */
    private String metric;

    /**
     * 标签 map
     */
    private Map<String, String> tags;

    /**
     * 聚合tag列表
     */
    private List<String> aggregateTags;

    /**
     * 值 map
     */
    private LinkedHashMap<Long, Object> dps = new LinkedHashMap<>();

}

@Slf4j
@Component
public class AliTsdbConvertor {

    @Autowired
    private IndexConfigService indexConfigService;

    public List<CommonQueryResult> convertResultList(List<QueryResult> source) {
        return Optional.ofNullable(source)
                .map(result -> result.stream().map(x -> convertResult(x)).collect(Collectors.toList()))
                .orElse(null);
    }

    public CommonQueryResult convertResult( QueryResult source) {
        return Optional.ofNullable(source).map(result -> new CommonQueryResult()
                .setAggregateTags(result.getAggregateTags())
                .setDps(result.getDps())
                .setMetric(result.getMetric())
                .setTags(result.getTags())
                .setAggregateTags(result.getAggregateTags()))
                .orElse(null);
    }

    /**
    * 将tsdb度量转成测值key,如 L1_JS-gX 转为 gX
    * @param metric 度量
    * @return 测值
    */
    private NoticeIndexConfigEntity getIndexByMetric(String metric) {
        return indexConfigService.queryByMetric(metric);
    }

    public List<ItemDataResponse> convertResponseList(List<CommonQueryResult> source, DataQuery dataQuery) {
        if (CollectionUtils.isEmpty(source)) {
            return null;
        }

        LinkedHashMap<Long, List<ItemIndex>> dataMap = new LinkedHashMap<>(source.size() + 1);

        source.stream().forEach(data -> {
            NoticeIndexConfigEntity indexConfig = indexConfigService.queryByMetric(data.getMetric());
            Optional.ofNullable(data.getDps()).ifPresent(x -> {
                x.entrySet().stream().forEachOrdered(entity -> {
                    List<ItemIndex> indexList = Lists.newArrayList();
                    Long timeStamp = entity.getKey();
                    if (dataMap.containsKey(timeStamp)) {
                        indexList = dataMap.get(timeStamp);
                    }
                    DataUnit dataUnit = null ;
                    String indexKey = null;
                    Double value = null == entity.getValue()? null : (Double) entity.getValue();
                    if (Objects.nonNull(dataQuery) && Boolean.TRUE.equals(dataQuery.getRate())) {

                        if (null != indexConfig) {
                            indexKey = indexConfig.getSensorIndex();
                            dataUnit = new DataUnit().setCode(this.unitFormate(indexConfig, dataQuery)).setName(this.unitNameFormate(indexConfig, dataQuery));
                        }
                        indexList.add(new ItemIndex().setKey(indexKey)
                                .setValue(this.rateFormate(value,dataQuery))
                                .setUnit(dataUnit));
                    } else {

                        if (null != indexConfig) {
                            indexKey = indexConfig.getSensorIndex();
                            dataUnit = new DataUnit().setCode(indexConfig.getUnit()).setName(indexConfig.getUnitName());
                        }
                        indexList.add(new ItemIndex().setKey(indexKey)
                                .setValue(value)
                                .setUnit(dataUnit));
                    }
                    dataMap.put(entity.getKey(), indexList);
                });
            });
        });

        return dataMap.entrySet()
                .stream()
                .map(entry -> new ItemDataResponse().setTimeStamp(entry.getKey()).setDataList(entry.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * 格式化变化率
     * @param rate
     * @param dataQuery
     * @return
     */
    private Double rateFormate(Double rate, DataQuery dataQuery) {

        if (Objects.isNull(rate)) {
            return rate;
        }
        if (StringUtils.isBlank(dataQuery.getDownsample())) {
            return rate;
        }
        BigDecimal formateRate = new BigDecimal(rate);
        switch(dataQuery.getDownsample()) {
            case "1h-avg" :
                formateRate = formateRate.multiply(new BigDecimal(60 * 60));
                break;
            case "1dc-avg" :
                formateRate = formateRate.multiply(new BigDecimal(60 * 60 * 24));
                break;
            case "1wc-avg" :
                formateRate = formateRate.multiply(new BigDecimal(60 * 60 * 24 * 7));
                break;
            default:

        }

        return formateRate.doubleValue();
    }

    /**
     * 变化率英文单位格式化
     * @param indexConfig
     * @param dataQuery
     * @return
     */
    private String unitFormate(NoticeIndexConfigEntity indexConfig, DataQuery dataQuery) {

        String unitCode = indexConfig.getUnit();
        switch(dataQuery.getDownsample()) {
            case "1h-avg" :
                unitCode = String.format("%s/%s",indexConfig.getUnit(), "hour");
                break;
            case "1dc-avg" :
                unitCode = String.format("%s/%s",indexConfig.getUnit(), "day");
                break;
            case "1wc-avg" :
                unitCode = String.format("%s/%s",indexConfig.getUnit(), "week");
                break;
            default:
                unitCode = String.format("%s/%s",indexConfig.getUnit(), "s");
                break;
        }
        return unitCode;
    }

    /**
     * 变化率中文单位格式化
     * @param indexConfig
     * @param dataQuery
     * @return
     */
    private String unitNameFormate(NoticeIndexConfigEntity indexConfig, DataQuery dataQuery) {


        String unitName = indexConfig.getUnit();
        switch(dataQuery.getDownsample()) {
            case "1h-avg" :
                unitName = String.format("%s/%s",indexConfig.getUnitName(), "小时");
                break;
            case "1dc-avg" :
                unitName = String.format("%s/%s",indexConfig.getUnitName(), "天");
                break;
            case "1wc-avg" :
                unitName = String.format("%s/%s",indexConfig.getUnitName(), "周");
                break;
            default:
                unitName = String.format("%s/%s",indexConfig.getUnitName(), "秒");
                break;
        }
        return unitName;
    }
}
Stream
    @Override
    public List<RainfallCalendarResponse> queryRainfallCalendar(Long pointId, Long startTime, Long endTime) {

        Map<ItemEnum, List<String>> rainfallConfigs = Maps.newHashMap();
        rainfallConfigs.put(ItemEnum.L3_YL, Arrays.asList(totalValueIndex));
        ThemeAnalysisUtilsBo checkRainfall = themeAnalysisUtils.check(pointId, rainfallConfigs);

        List<ItemDataResponse> rainfallResp = themeAnalysisUtils.queryTsdb(Lists.newArrayList(checkRainfall), startTime, endTime, TimeScaleEnum.last_day);
        return rainfallResp.stream().map(response ->
            new RainfallCalendarResponse()
                    .setTimeStamp(response.getTimeStamp())
                    .setDailyRainfallAccumulation(new BigDecimal(response.getDataList()
                            .stream()
                            .filter(itemIndex -> totalValueIndex.equals(itemIndex.getKey()))
                            .findFirst()
                            .orElseGet(() -> new ItemIndex().setValue(0D))
                            .getValue())
                            .setScale(2, RoundingMode.HALF_UP)))
                .collect(Collectors.toList());
    }

Resttemplate

/**
 * RestTemplate 远程调用工具类
 *
 * @author jiang.peng
 * @createDate 2021-12-10
 *
 */
public class RestTemplateUtils {

    private static final RestTemplate restTemplate = new RestTemplate();

    // ----------------------------------GET-------------------------------------------------------

    /**
     * GET请求调用方式
     *
     * @param url 请求URL
     * @param responseType 返回对象类型
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> get(String url, Class<T> responseType) {
        return restTemplate.getForEntity(url, responseType);
    }

    /**
     * GET请求调用方式
     *
     * @param url 请求URL
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> get(String url, Class<T> responseType, Object... uriVariables) {
        return restTemplate.getForEntity(url, responseType, uriVariables);
    }

    /**
     * GET请求调用方式
     *
     * @param url 请求URL
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> get(String url, Class<T> responseType, Map<String, ?> uriVariables) {
        return restTemplate.getForEntity(url, responseType, uriVariables);
    }

    /**
     * 带请求头的GET请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> get(String url, Map<String, String> headers, Class<T> responseType, Object... uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return get(url, httpHeaders, responseType, uriVariables);
    }

    /**
     * 带请求头的GET请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> get(String url, HttpHeaders headers, Class<T> responseType, Object... uriVariables) {
        HttpEntity<?> requestEntity = new HttpEntity<>(headers);
        return exchange(url, HttpMethod.GET, requestEntity, responseType, uriVariables);
    }

    /**
     * 带请求头的GET请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> get(String url, Map<String, String> headers, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return get(url, httpHeaders, responseType, uriVariables);
    }

    /**
     * 带请求头的GET请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> get(String url, HttpHeaders headers, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpEntity<?> requestEntity = new HttpEntity<>(headers);
        return exchange(url, HttpMethod.GET, requestEntity, responseType, uriVariables);
    }

    // ----------------------------------POST-------------------------------------------------------

    /**
     * POST请求调用方式
     *
     * @param url 请求URL
     * @param responseType 返回对象类型
     * @return
     */
    public static <T> ResponseEntity<T> post(String url, Class<T> responseType) {
        return restTemplate.postForEntity(url, HttpEntity.EMPTY, responseType);
    }

    /**
     * POST请求调用方式
     *
     * @param url 请求URL
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, Object requestBody, Class<T> responseType) {
        return restTemplate.postForEntity(url, requestBody, responseType);
    }

    /**
     * POST请求调用方式
     *
     * @param url 请求URL
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, Object requestBody, Class<T> responseType, Object... uriVariables) {
        return restTemplate.postForEntity(url, requestBody, responseType, uriVariables);
    }

    /**
     * POST请求调用方式
     *
     * @param url 请求URL
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        return restTemplate.postForEntity(url, requestBody, responseType, uriVariables);
    }

    /**
     * 带请求头的POST请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, Map<String, String> headers, Object requestBody, Class<T> responseType, Object... uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return post(url, httpHeaders, requestBody, responseType, uriVariables);
    }

    /**
     * 带请求头的POST请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, HttpHeaders headers, Object requestBody, Class<T> responseType, Object... uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody, headers);
        return post(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 带请求头的POST请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, Map<String, String> headers, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return post(url, httpHeaders, requestBody, responseType, uriVariables);
    }

    /**
     * 带请求头的POST请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, HttpHeaders headers, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody, headers);
        return post(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 自定义请求头和请求体的POST请求调用方式
     *
     * @param url 请求URL
     * @param requestEntity 请求头和请求体封装对象
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) {
        return restTemplate.exchange(url, HttpMethod.POST, requestEntity, responseType, uriVariables);
    }

    /**
     * 自定义请求头和请求体的POST请求调用方式
     *
     * @param url 请求URL
     * @param requestEntity 请求头和请求体封装对象
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> post(String url, HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) {
        return restTemplate.exchange(url, HttpMethod.POST, requestEntity, responseType, uriVariables);
    }

    // ----------------------------------PUT-------------------------------------------------------

    /**
     * PUT请求调用方式
     *
     * @param url 请求URL
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, Class<T> responseType, Object... uriVariables) {
        return put(url, HttpEntity.EMPTY, responseType, uriVariables);
    }

    /**
     * PUT请求调用方式
     *
     * @param url 请求URL
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, Object requestBody, Class<T> responseType, Object... uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody);
        return put(url, requestEntity, responseType, uriVariables);
    }

    /**
     * PUT请求调用方式
     *
     * @param url 请求URL
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody);
        return put(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 带请求头的PUT请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, Map<String, String> headers, Object requestBody, Class<T> responseType, Object... uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return put(url, httpHeaders, requestBody, responseType, uriVariables);
    }

    /**
     * 带请求头的PUT请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, HttpHeaders headers, Object requestBody, Class<T> responseType, Object... uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody, headers);
        return put(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 带请求头的PUT请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, Map<String, String> headers, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return put(url, httpHeaders, requestBody, responseType, uriVariables);
    }

    /**
     * 带请求头的PUT请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, HttpHeaders headers, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody, headers);
        return put(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 自定义请求头和请求体的PUT请求调用方式
     *
     * @param url 请求URL
     * @param requestEntity 请求头和请求体封装对象
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) {
        return restTemplate.exchange(url, HttpMethod.PUT, requestEntity, responseType, uriVariables);
    }

    /**
     * 自定义请求头和请求体的PUT请求调用方式
     *
     * @param url 请求URL
     * @param requestEntity 请求头和请求体封装对象
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> put(String url, HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) {
        return restTemplate.exchange(url, HttpMethod.PUT, requestEntity, responseType, uriVariables);
    }

    // ----------------------------------DELETE-------------------------------------------------------

    /**
     * DELETE请求调用方式
     *
     * @param url 请求URL
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, Class<T> responseType, Object... uriVariables) {
        return delete(url, HttpEntity.EMPTY, responseType, uriVariables);
    }

    /**
     * DELETE请求调用方式
     *
     * @param url 请求URL
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, Class<T> responseType, Map<String, ?> uriVariables) {
        return delete(url, HttpEntity.EMPTY, responseType, uriVariables);
    }

    /**
     * DELETE请求调用方式
     *
     * @param url 请求URL
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, Object requestBody, Class<T> responseType, Object... uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody);
        return delete(url, requestEntity, responseType, uriVariables);
    }

    /**
     * DELETE请求调用方式
     *
     * @param url 请求URL
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody);
        return delete(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 带请求头的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, Map<String, String> headers, Class<T> responseType, Object... uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return delete(url, httpHeaders, responseType, uriVariables);
    }

    /**
     * 带请求头的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, HttpHeaders headers, Class<T> responseType, Object... uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(headers);
        return delete(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 带请求头的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, Map<String, String> headers, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return delete(url, httpHeaders, responseType, uriVariables);
    }

    /**
     * 带请求头的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, HttpHeaders headers, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(headers);
        return delete(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 带请求头的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, Map<String, String> headers, Object requestBody, Class<T> responseType, Object... uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return delete(url, httpHeaders, requestBody, responseType, uriVariables);
    }

    /**
     * 带请求头的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, HttpHeaders headers, Object requestBody, Class<T> responseType, Object... uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody, headers);
        return delete(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 带请求头的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, Map<String, String> headers, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAll(headers);
        return delete(url, httpHeaders, requestBody, responseType, uriVariables);
    }

    /**
     * 带请求头的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param headers 请求头参数
     * @param requestBody 请求参数体
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, HttpHeaders headers, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity<Object>(requestBody, headers);
        return delete(url, requestEntity, responseType, uriVariables);
    }

    /**
     * 自定义请求头和请求体的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param requestEntity 请求头和请求体封装对象
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) {
        return restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, responseType, uriVariables);
    }

    /**
     * 自定义请求头和请求体的DELETE请求调用方式
     *
     * @param url 请求URL
     * @param requestEntity 请求头和请求体封装对象
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> delete(String url, HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) {
        return restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, responseType, uriVariables);
    }

    // ----------------------------------通用方法-------------------------------------------------------

    /**
     * 通用调用方式
     *
     * @param url 请求URL
     * @param method 请求方法类型
     * @param requestEntity 请求头和请求体封装对象
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,按顺序依次对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) {
        return restTemplate.exchange(url, method, requestEntity, responseType, uriVariables);
    }

    /**
     * 通用调用方式
     *
     * @param url 请求URL
     * @param method 请求方法类型
     * @param requestEntity 请求头和请求体封装对象
     * @param responseType 返回对象类型
     * @param uriVariables URL中的变量,与Map中的key对应
     * @return ResponseEntity 响应对象封装类
     */
    public static <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) {
        return restTemplate.exchange(url, method, requestEntity, responseType, uriVariables);
    }

    /**
     * 获取RestTemplate实例对象,可自由调用其方法
     *
     * @return RestTemplate实例对象
     */
    public static RestTemplate getRestTemplate() {
        return restTemplate;
    }
}
使用方式
@Component("ysy")
@Slf4j
@ConditionalOnProperty(name = "video.service.provider", havingValue = "ysy")
public class YsyVideoServiceImpl implements VideoService {
    private static final String CODE_SUCCESS = "200";
    /**
     * 通道号
     */
    private static final int VIDEO_CHANNEL_NO = 1;

    /**
     * 视频播放速度,0-慢,1-适中,2-快,海康设备参数不可为0
     */
    private static final int VIDEO_SPEED = 1;


    @Autowired
    private DeviceServiceSide deviceServiceSide;

    @Autowired
    private ThreadPoolTaskExecutor executor;

    @Value("${ysy.service.host:https://open.ys7.com}")
    private String serviceHost;

    @Value("${ysy.service.url.live_address:/api/lapp/v2/live/address/get}")
    private String liveAddressUrl;

    @Value("${ysy.service.url.ptz:/api/lapp/device/ptz/start}")
    private String ptzUrl;

    @Value("${ysy.service.url.ptz:/api/lapp/device/ptz/stop}")
    private String stopPtzUrl;

    /**
     * 设备操作单次时间
     */
    @Value("${ysy.service.ptz.ms:1000}")
    private int ptzTime;

    /**
     * 上一次的视频操作
     */
    private ThreadLocal<Integer> currentOperation;

    @Override
    public DeviceVideoStreamResponse getDeviceStreamUrl(Long deviceId) {

        //获取设备信息
        YsyDevice ysyDevice = deviceServiceSide.getVideoDeviceDetail(deviceId);
        if (null == ysyDevice) {
            throw new ApiException(ErrorCode.INTERNAL_ERROR, "获取不到视频设备信息");
        }
        log.info("getVideoDeviceDetail:{}", ysyDevice.toString());

        String accessToken = ysyDevice.getAccessToken();
        String requestUrl = serviceHost + liveAddressUrl;
        Map<String, Object> params = new HashMap<>();
        params.put("accessToken", accessToken);
        params.put("deviceSerial", ysyDevice.getDeviceSerial());

        ResponseEntity<String> response = RestTemplateUtils.post(buildRealUrl(requestUrl, params), HttpEntity.EMPTY, String.class, params);

        String body = response.getBody();
        if (StringUtils.isNotBlank(body)) {
            JSONObject jb = JSON.parseObject(body);
            String code = jb.getString("code");
            if (CODE_SUCCESS.equalsIgnoreCase(code)) {
                Map dataMap = jb.getObject("data", Map.class);
                String videoUrl = (String) dataMap.get("url");
                return new DeviceVideoStreamResponse().setUrl(videoUrl).setAccessToken(accessToken);
            } else {
                log.info("[{}]获取视频流地址失败,错误码:{}", deviceId, code);
                throw new ApiException(ErrorCode.INTERNAL_ERROR, "获取视频流地址失败!");
            }
        } else {
            log.info("获取视频流地址失败,body is empty");
            throw new ApiException(ErrorCode.INTERNAL_ERROR, "获取视频流地址失败!");
        }
    }

    @Override
    public void deviceOperation(Long deviceId, int operation) {

        //获取设备信息
        YsyDevice ysyDevice = deviceServiceSide.getVideoDeviceDetail(deviceId);

        log.info("getVideoDeviceDetail:{}", ysyDevice.toString());

        String requestUrl = serviceHost + ptzUrl;

        Map<String, Object> params = new HashMap<>();
        params.put("accessToken", ysyDevice.getAccessToken());
        params.put("deviceSerial", ysyDevice.getDeviceSerial());
        params.put("channelNo", VIDEO_CHANNEL_NO);
        params.put("direction", operation);
        params.put("speed", VIDEO_SPEED);
        ResponseEntity<String> response = RestTemplateUtils.post(buildRealUrl(requestUrl, params), HttpEntity.EMPTY, String.class, params);

        String body = response.getBody();
        if (StringUtils.isNotBlank(body)) {
            JSONObject jb = JSON.parseObject(body);
            String code = jb.getString("code");
            if (!CODE_SUCCESS.equalsIgnoreCase(code)) {
                log.info("[{}]视频操作失败,错误码:{}", deviceId, code);
                throw new ApiException(ErrorCode.INTERNAL_ERROR, jb.getString("msg"));
            } else {
                //成功后需要停止,否则会一直转动
                executor.execute(() -> {
                    try {
                        Thread.sleep(ptzTime);
                        stopOperation(ysyDevice);
                    } catch (Exception e) {
                        log.error("e:{}", e);
                    }
                });
            }
        } else {
            log.info("视频操作失败,body is empty");
            throw new ApiException(ErrorCode.INTERNAL_ERROR, "操作失败!");
        }
    }

    /**
     * 停止视频操作
     * @param device 设备信息
     * @return void
     */
    private void stopOperation(YsyDevice device) {

        String requestUrl = serviceHost + stopPtzUrl;

        Map<String, Object> params = new HashMap<>();
        params.put("accessToken", device.getAccessToken());
        params.put("deviceSerial", device.getDeviceSerial());
        params.put("channelNo", VIDEO_CHANNEL_NO);
//        Integer operation = currentOperation.get();
//        if (null == operation) {
//            params.put("direction", operation);
//        }

        ResponseEntity<String> response = RestTemplateUtils.post(buildRealUrl(requestUrl, params), HttpEntity.EMPTY, String.class, params);

        String body = response.getBody();
        if (StringUtils.isNotBlank(body)) {
            JSONObject jb = JSON.parseObject(body);
            String code = jb.getString("code");
            if (!CODE_SUCCESS.equalsIgnoreCase(code)) {
                log.info("[{}]视频停止操作失败,错误码:{}", device, code);
            }
        } else {
            log.info("获取视频流地址失败,body is empty");
        }
    }

    /**
     * 构建实际的请求地址url
     * @param url 请求host
     * @param params 参数列表
     * @return
     */
    private String buildRealUrl(String url, Map<String, Object> params) {
        if (null == params || params.isEmpty()) {
            return url;
        }

        StringBuilder sb = new StringBuilder().append(url).append("?");
        params.keySet().forEach(key -> sb.append(key).append("={").append(key).append("}&"));

        //去掉最后一个&
        sb.deleteCharAt(sb.length() - 1);

        return sb.toString();
    }
}
可学方法
        // 查询日累计雨量
        List<RainfallCalendarResponse> rainfallCalendarResponseList = this.queryRainfallCalendar(pointId, startTime, endTime);

        // 计算总累计雨量
        Map<Long, BigDecimal> sumMap = Maps.newHashMap();
        List<BigDecimal> values = rainfallCalendarResponseList.stream()
                .map(RainfallCalendarResponse::getDailyRainfallAccumulation)
                .collect(Collectors.toList());
        for (int i=0; i<rainfallCalendarResponseList.size(); i++) {
            sumMap.put(rainfallCalendarResponseList.get(i).getTimeStamp(),
                    values.subList(0, i+1).stream().reduce(BigDecimal.ZERO, BigDecimal::add));
        }

        return rainfallCalendarResponseList.stream().map(item -> new RainfallAnalysisResponse()
                .setTimeStamp(item.getTimeStamp())
                .setDailyRainfallAccumulation(item.getDailyRainfallAccumulation())
                .setSingleRainfallAccumulation(singleMap.get(item.getTimeStamp()))
                .setTotalRainfallAccumulation(sumMap.get(item.getTimeStamp())))
                .collect(Collectors.toList());

        List<ItemDataResponse> rainfallResp = themeAnalysisUtils.queryTsdb(Lists.newArrayList(checkRainfall), startTime, endTime, TimeScaleEnum.last_day);
        return rainfallResp.stream().map(response ->
            new RainfallCalendarResponse()
                    .setTimeStamp(response.getTimeStamp())
                    .setDailyRainfallAccumulation(new BigDecimal(response.getDataList()
                            .stream()
                            .filter(itemIndex -> totalValueIndex.equals(itemIndex.getKey()))
                            .findFirst()
                            .orElseGet(() -> new ItemIndex().setValue(0D))
                            .getValue())
                            .setScale(2, RoundingMode.HALF_UP)))
                .collect(Collectors.toList());


        // 数据运算
        return resp.stream().map(itemDataResponse -> {
            AnalysisReservoirWaterLevelResponse response = new AnalysisReservoirWaterLevelResponse();
            BigDecimal relativeWaterLevel = BigDecimal.ZERO;
            BigDecimal absolutelyWaterLevel = BigDecimal.ZERO;

            BigDecimal value = BigDecimal.ZERO;
            /**
             * 计算方式
             * 相对库水位:压力式水位计:基准水位+(当前测值value-基准测值),雷达(超声波)水位计:基准水位-(当前测值value-基准测值)。
             * 绝对库水位:压力式水位计:设备海拔+当前测值value,雷达(超声波)水位计:设备海拔-当前测值value
             * 若value≤0,则弹出提示“监测数据异常”,展示页面不显示曲线;。
             */
            for (ItemIndex itemIndex : itemDataResponse.getDataList()) {
                if (sensorIndex.equals(itemIndex.getKey())) {
                    Double doubleValue = itemIndex.getValue();
                    if (doubleValue <= 0D) {
                        log.error("监测数据异常");
                        throw new ApiException(ErrorCode.INTERNAL_ERROR, "监测数据异常");
                    }
                    value = new BigDecimal(doubleValue);
                    break;
                }
            }
            BigDecimal temp = value.subtract(referenceValue);
            if (observationMethod.equals(ObservationMethodEnum.pressure.getCode())) {
                relativeWaterLevel = referenceWaterLevel.add(temp);
                absolutelyWaterLevel = deviceAltitude.add(value);

            } else if (observationMethod.equals(ObservationMethodEnum.radar.getCode())) {
                relativeWaterLevel = referenceWaterLevel.subtract(temp);
                absolutelyWaterLevel = deviceAltitude.subtract(value);
            }
            return response.setTimestamp(itemDataResponse.getTimeStamp())
                    .setAbsolutelyWaterLevel(absolutelyWaterLevel.setScale(2, RoundingMode.HALF_UP))
                    .setRelativeWaterLevel(relativeWaterLevel.setScale(2, RoundingMode.HALF_UP));
        }).collect(Collectors.toList());

       // 4、库水位
        List<AnalysisReservoirWaterLevelResponse> reservoirWaterLevelResp = analysisReservoirWaterLevelService.queryReservoirWaterLevel(waterLevelId,dataType,startTime,endTime);

        // 5、组装数据
        return new AnalysisSeepageFlowResponse().setSeepageFlows(seepageFlowResp
                .stream()
                .map(item -> {

                    // 渗流量
                    BigDecimal value = BigDecimal.ZERO;
                    for (ItemIndex itemIndex : item.getDataList()) {
                        if (sensorIndex.equals(itemIndex.getKey())) {
                            value = new BigDecimal(itemIndex.getValue());
                        }
                    }
                    return new SeepageFlowModel().setTimestamp(item.getTimeStamp())
                            .setSeepageFlowValue(value.setScale(2, RoundingMode.HALF_UP));
                }).collect(Collectors.toList()))
                .setRainfallValues(rainfallResp.stream().map(item -> {

                    // 雨量
                    BigDecimal value = BigDecimal.ZERO;
                    for (ItemIndex itemIndex : item.getDataList()) {
                        if (sensorIndex.equals(itemIndex.getKey())) {
                            value = new BigDecimal(itemIndex.getValue());
                        }
                    }
                    return new RainfallValuesModel().setTimestamp(item.getTimeStamp())
                            .setRainfallValue(value.setScale(2, RoundingMode.HALF_UP));
                }).collect(Collectors.toList()))
                .setReservoirWaterLevels(reservoirWaterLevelResp);
respList.add(MonitorItemChartsResponse.builder()
        .itemType(entry.getKey())      .itemTypeName(Optional.ofNullable(ItemEnum.getNameByCode(entry.getKey())).orElse(""))
        .count(entry.getValue())
        .rate(BigDecimal.ONE.subtract(cumulativeRate))
        .build());
return respList.stream()             .sorted(Comparator.comparing(MonitorItemChartsResponse::getRate).reversed()).collect(Collectors.toList());
@Getter
public enum ItemEnum {
    L1_QJ(ItemTypeConstant.L1_QJ, "倾角"),
    L1_LF(ItemTypeConstant.L1_LF, "裂缝"),
    L1_GP(ItemTypeConstant.L1_GP, "地表位移"),
    L1_SW(ItemTypeConstant.L1_SW, "深部位移"),
    L1_JS(ItemTypeConstant.L1_JS, "加速度"),
    L1_ZD(ItemTypeConstant.L1_ZD, "振动"),
    L1_BT(ItemTypeConstant.L1_BT, "崩塌"),
    L1_JZ(ItemTypeConstant.L1_JZ, "地表位移基站"),

    private String code;
    private String name;

    ItemEnum(String code, String name) {
        this.code = code;
        this.name = name;
    }

    /**
     * 根据code获取枚举
     * @param code
     * @return
     */
    public static ItemEnum getEnumByCode(String code) {

        return Arrays.stream(ItemEnum.values())
                .filter(ItemEnum -> code.equals(ItemEnum.getCode()))
                .findFirst()
                .orElse(null);
    }

    /**
     * 根据code获取名称
     * @param code
     * @return
     */
    public static String getNameByCode(String code) {

        return Arrays.stream(ItemEnum.values())
                .filter(ItemEnum -> code.equals(ItemEnum.getCode()))
                .findFirst()
                .map(ItemEnum::getName)
                .orElse(null);
    }

}
/**
     * 预警设备
     */
private static final Set<String> ALARM_ITEM_SET = Collections
            .unmodifiableSet(new HashSet<>(Arrays.asList("L4_LB","L4_SP")));
List<String> filterList = typeList.stream().filter(type -> ! ALARM_ITEM_SET.contains(type)).collect(Collectors.toList());
Map<String, Long> typeMap = filterList
                .stream()
                .collect(Collectors.groupingBy(type -> type, Collectors.counting()));