Skip to content

Latest commit

 

History

History
5289 lines (4645 loc) · 228 KB

框架学习笔记.md

File metadata and controls

5289 lines (4645 loc) · 228 KB

框架学习笔记

Hibernate Validator 参数校验

在RESTful 的接口服务中,会有各种各样的入参,我们不可能完全不做任何校验就直接进入到业务处理的环节,通常我们会有一个基础的数据验证的机制,待这些验证过程完毕,结果无误后,参数才会进入到正式的业务处理中。而数据验证又分为两种,一种是无业务关联的规则性验证,一种是根据现有数据进行的联动性数据验证(简单来说,参数的合理性,需要查数据库)。而Hibernate-Validator则适合做无业务关联的规则性验证,而这类验证的代码大多是可复用的。

        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>

如果是Spring Boot项目,那么spring-boot-starter-web中就已经依赖hibernate-validator

使用方式:https://blog.csdn.net/qq_32258777/article/details/86743416?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_paycolumn_v3&utm_relevant_index=1

https://blog.csdn.net/java_collect/article/details/85534054

Hibernate Validator视频代码:https://gitee.com/phui/share-early-works/tree/master

我们在平时的学习与工作中,都需要对参数进行校验,比如在注册时,用户名密码不能为空,用户名长度必须小于10等等。虽然有些校验在前端页面会进行验证,但是后端为了增加健壮性也需要对这些参数进行判断(比如绕过前端页面而直接调用了接口,参数的合法性未知),可能就会在controller或者service中就会有如下代码的出现

首先我们看controller类最上方,我们标注了@Validataed,该注解的含义是:这个类要启用参数校验。在save方法的参数中标注了@Valid,含义为我们要对紧跟的实体进行校验,而具体校验的内容,为实体类中的我们的定义的约束 以Ability类举例,在name字段上方标记了@NotBlank,意为定义了该字段不允许为空的约束,如果name为空,校验就不通过,就会返回我们之前碰到的400异常。而type字段也标注了@NotNull,也定义了该字段不允许为空的约束

使用方式:https://blog.csdn.net/csdn_mrsongyang/article/details/106115243

Swagger

现项目中我们常常直接用Swagger来让项目展示接口文档体验,随着项目后续的发展,大家对Swagger对原生的界面不是很满意,于是就出现了一个新的Swagger扩展项目,叫swagger-bootstrap-ui,swagger-bootstrap-ui是springfox-swagger的增强UI实现,为Java开发者在使用Swagger的时候,能拥有一份简洁、强大的接口文档体验。 作者对swagger-bootstrap-ui不断的迭代,在1.9.6的版本时,作者把swagger-bootstrap-ui项目重命名为Knife4j,目标是希望它能像一把匕首一样小巧,轻量,并且功能强悍,更名也是希望把她做成一个为Swagger接口文档服务的通用性解决方案,不仅仅只是专注于前端Ui前端。 文档可参考: 说明文档: https://doc.xiaominfo.com 快速开始:https://doc.xiaominfo.com/knife4j/documentation/get_start.html Gitee:https://gitee.com/xiaoym/knife4j GitHub:https://github.com/xiaoymin/swagger-bootstrap-ui 示例:https://gitee.com/xiaoym/swagger-bootstrap-ui-demo

使用方式:

https://doc.xiaominfo.com/knife4j/documentation/get_start.html

1、导入依赖

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.2</version>
        </dependency>

2、添加配置

#application.yml
swagger:
  base-packages: com.example.mpservice.controller
  description: 大屏项目接口文档
  title: 大屏项目接口文档
#配置swagger配置
knife4j:
  production: false #默认是false ,屏蔽所有Swagger的相关资源
  enable: true #是否开启swagger
spring:
  mvc:
    pathmatch:
      matching-strategy: ANT_PATH_MATCHER
#swagger公共信息
#swagger.yml
swagger:
  title: 接口文档系统
  description: 接口文档系统
  license:
  license-url: https://www.lemonyliu.com
  terms-of-service-url: https://www.lemonyliu.com
  contact:
    name: zuoping.liu
    email: [email protected]
    url:

3、添加配置文件

//swagger包下
@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {
    /**
     * swagger会解析的包路径
     **/
    private List<String> basePackages = new ArrayList<>();
    /**
     * swagger会解析的url规则
     **/
    private List<String> basePath = new ArrayList<>();
    /**
     * 在basePath基础上需要排除的url规则
     **/
    private List<String> excludePath = new ArrayList<>();
    /**
     * 标题
     **/
    private String title = "接口文档系统";
    /**
     * 描述
     **/
    private String description = "接口文档系统";
    /**
     * 版本
     **/
    private String version = "1.0.0";
    /**
     * 许可证
     **/
    private String license = "";
    /**
     * 许可证URL
     **/
    private String licenseUrl = "";
    /**
     * 服务条款URL
     **/
    private String termsOfServiceUrl = "";

    /**
     * host信息
     **/
    private String host = "";
    /**
     * 联系人信息
     */
    private Contact contact = new Contact();

    @Data
    @NoArgsConstructor
    public static class Contact {
        /**
         * 联系人
         **/
        private String name = "zuoping.liu";
        /**
         * 联系人url
         **/
        private String url = "";
        /**
         * 联系人email
         **/
        private String email = "";
    }
}
/**
 * Swagger工具类
 */
public class SwaggerUtil {
    /**
     * 获取包集合
     */
    public static Predicate<RequestHandler> basePackages(final List<String> basePackages) {
        return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true);
    }

    private static Function<Class<?>, Boolean> handlerPackage(final List<String> basePackages) {
        return input -> {
            // 循环判断匹配
            for (String strPackage : basePackages) {
                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                if (isMatch) {
                    return true;
                }
            }
            return false;
        };
    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
    }
}
/**
 * @description: swagger资源配置
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SwaggerProperties.class)
@Import(SwaggerAutoConfiguration.class)
public class SwaggerWebConfiguration implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}
/**
 * @description: swagger配置
 */
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@Import(BeanValidatorPluginsConfiguration.class)
@ConditionalOnExpression("${knife4j.enable}")
@EnableOpenApi
public class SwaggerAutoConfiguration {
    private static final String DEFAULT_BASE_PATH = "/**";
    private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");
    @Bean
    public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                    customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
                }
                return bean;
            }

            private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
                List<T> copy = mappings.stream()
                        .filter(mapping -> mapping.getPatternParser() == null)
                        .collect(Collectors.toList());
                mappings.clear();
                mappings.addAll(copy);
            }

            private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
                try {
                    Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                    field.setAccessible(true);
                    return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new IllegalStateException(e);
                }
            }
        };
    }

    @Bean
    @ConditionalOnMissingBean
    public SwaggerProperties swaggerProperties() {
        return new SwaggerProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    public Docket api(SwaggerProperties swaggerProperties) {
        if (swaggerProperties.getBasePath().size() == 0) {
            swaggerProperties.getBasePath().add(DEFAULT_BASE_PATH);
        }
        if (swaggerProperties.getExcludePath().size() == 0) {
            swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
        }
        ApiSelectorBuilder apis = new Docket(DocumentationType.SWAGGER_2)
                .host(swaggerProperties.getHost())
                .apiInfo(apiInfo(swaggerProperties)).select()
                .apis(SwaggerUtil.basePackages(swaggerProperties.getBasePackages()));
        swaggerProperties.getBasePath().forEach(p -> apis.paths(PathSelectors.ant(p)));
        swaggerProperties.getExcludePath().forEach(p -> apis.paths(PathSelectors.ant(p).negate()));
        return apis.build();
    }

    /**
     * 配置基本信息
     */
    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .license(swaggerProperties.getLicense())
                .licenseUrl(swaggerProperties.getLicenseUrl())
                .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
                .contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
                .version(swaggerProperties.getVersion())
                .build();
    }
}

本地缓存Caffine

按 Caffeine Github 文档描述,Caffeine 是基于 JAVA 8 的高性能缓存库。并且在 spring5 (springboot 2.x) 后,spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。

引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>2.9.3</version>
        </dependency>

配置类编写

  • 方法一
@Configuration
public class CacheConfig {
    /**
     * 配置缓存管理器
     *
     * @return 缓存管理器
     */
    @Bean("caffeineCacheManager")
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                // 设置最后一次写入或访问后经过固定时间过期
                .expireAfterAccess(60, TimeUnit.SECONDS)
                // 初始的缓存空间大小
                .initialCapacity(100)
                // 缓存的最大条数
                .maximumSize(1000));
        return cacheManager;
    }

}

service实现类实现对caffeine的运用

@Slf4j
@Service
@CacheConfig(cacheNames = "caffeineCacheManager")
public class UserInfoServiceImpl implements UserInfoService {
    /**
     * 模拟数据库存储数据
     */
    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();
    @Override
    @CachePut(key = "#userInfo.id")
    public void addUserInfo(UserInfo userInfo) {
        log.info("create");
        userInfoMap.put(userInfo.getId(), userInfo);
    }

    @Override
    @Cacheable(key = "#id")
    public UserInfo getByName(Integer id) {
        log.info("get");
        return userInfoMap.get(id);
    }

    @Override
    @CachePut(key = "#userInfo.id")
    public UserInfo updateUserInfo(UserInfo userInfo) {
        log.info("update");
        if (!userInfoMap.containsKey(userInfo.getId())) {
            return null;
        }
        // 取旧的值
        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
        // 替换内容
        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
            oldUserInfo.setAge(userInfo.getAge());
        }
        // 将新的对象存储,更新旧对象信息
        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
        // 返回新对象信息
        return oldUserInfo;
    }

    @Override
    @CacheEvict(key = "#id")
    public void deleteById(Integer id) {
        log.info("delete");
        userInfoMap.remove(id);
    }

}
  • 方法二
@Configuration(proxyBeanMethods = false)
//Spring 5.2.0+的版本,建议你的配置类均采用Lite模式去做,即显示设置proxyBeanMethods = false。Spring Boot在2.2.0版本
//(依赖于Spring 5.2.0)起就把它的所有的自动配置类的此属性改为了false,即@Configuration(proxyBeanMethods = false),
// 提高Spring启动速度
public class CacheConfig extends CachingConfigurerSupport {
    @Override
    @Bean(name = "cacheManager")
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        // 方案一(常用):定制化缓存Cache
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .expireAfterWrite(2, TimeUnit.MINUTES)
                .initialCapacity(100)
                .maximumSize(10_000));

        return cacheManager;
    }
}

service实现类实现对caffeine的运用

@Service
@Slf4j
public class NoticeMonitorObjectServiceImpl extends ServiceImpl<NoticeMonitorObjectMapper, NoticeMonitorObject> implements NoticeMonitorObjectService {
    @Value("${qxwz.monitor.warning.areaCode}")
    private String areaCode;
    /**
     * 查询监测点,优化做成缓存
     * value:缓存key的前缀。
     * key:缓存key的后缀。
     * sync:设置如果缓存过期是不是只放一个请求去请求数据库,其他请求阻塞,默认是false。
     * 用于同步的,在缓存失效(过期不存在等各种原因)的时候,如果多个线程同时访问被标注的方法
     * 则只允许一个线程通过去执行方法
     */
    @Override
    @Cacheable(value = "NoticeMonitorObjectList", key = "#query.areaCode+'_'+T(String).join(\"_\", #query.getTypes)", sync = true)
      public List<NoticeMonitorObjectDTO> listNoticeMonitorPoint(NoticeMonitorPointQuery query) {
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = baseMapper.listNoticeMonitorPoint(query);
        log.info("查询监测点信息:"+noticeMonitorObjectDTOList);
        return noticeMonitorObjectDTOList;
    }
  
@Service
@AllArgsConstructor
public class NoticeMonitorPointServiceImpl extends ServiceImpl<NoticeMonitorPointMapper, NoticeMonitorPoint> implements NoticeMonitorPointService {
    private final NoticeMonitorObjectService noticeMonitorObjectService;
    @Override
    @Cacheable(value = "NoticeMonitorPointMap", key = "#query.areaCode+'_'+T(String).join(\"_\", #query.getTypes)", sync = true)
    public Map<Long,NoticeMonitorPointDTO> mapNoticeMonitorPoint(NoticeMonitorPointQuery query) {
        List<NoticeMonitorPointDTO> noticeMonitorPoints = listNoticeMonitorPoint(query);
        if(CollectionUtils.isEmpty(noticeMonitorPoints)){
            return Collections.EMPTY_MAP;
        }
        return noticeMonitorPoints.stream().collect(Collectors.toMap(NoticeMonitorPointDTO::getId, Function.identity(), (key1, key2) -> key2));
    }
    @Override
    @Cacheable(value = "NoticeMonitorPointList", key = "#query.areaCode+'_'+T(String).join(\"_\", #query.getTypes)", sync = true)
    public List<NoticeMonitorPointDTO> listNoticeMonitorPoint(NoticeMonitorPointQuery query) {
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = noticeMonitorObjectService.listNoticeMonitorPoint(query);
        if(CollectionUtils.isEmpty(noticeMonitorObjectDTOList)){
            return Collections.emptyList();
        }
        List<NoticeMonitorPointDTO> list = new ArrayList<>();
        for (NoticeMonitorObjectDTO noticeMonitorObjectDTO : noticeMonitorObjectDTOList) {
            List<NoticeMonitorPoint> noticeMonitorPoints = noticeMonitorObjectDTO.getNoticeMonitorPoints();
            if(CollectionUtils.isEmpty(noticeMonitorPoints)){
                continue;
            }
            for (NoticeMonitorPoint noticeMonitorPoint : noticeMonitorPoints) {
                NoticeMonitorObject noticeMonitorObject = new NoticeMonitorObjectDTO();
                BeanUtils.copyProperties(noticeMonitorObjectDTO,noticeMonitorObject);
                NoticeMonitorPointDTO point = new NoticeMonitorPointDTO();
                BeanUtils.copyProperties(noticeMonitorPoint,point);
                point.setNoticeMonitorObject(noticeMonitorObject);
                list.add(point);
            }
        }
        return list;
    }
    @Override
    public List<NoticeMonitorPoint> listOfflinePoints(List<Long> ids){
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return query().select("id AS id","name AS name","longitude as longitude","latitude as latitude")
                .in("id", ids)
                .list();
    }
}

使用方式:https://zhuanlan.zhihu.com/p/109226599

Mysql

MySQL是最好的RDBMS(Relational Database Management System, 关系型数据库管理系统)应用软件之一。

是开源的数据库软件。 体积小,速度快,总体拥有成本低,招人成本低,所有人必须会~ 中小型网站或大型网站应用,还可以集群。

官网:https://www.mysql.com/ 官网下载:https://dev.mysql.com/downloads/mysql/

/*每一个表,都必须存在以下五个字段!表示一个记录存在的意义!

id      主键
`version`   乐观锁
is_delete   伪删除
gmt_create  创建时间
gmt_update  更新时间
*/

(Data Query Language:数据库查询语言)

  • 所有的查询操作都用它 Select
  • 简单的查询,复杂的查询它都能做~
  • 数据库中最核心的语言,最重要的语句
  • 使用频率最高的语句

select 语法

SELECT [ALL | DISTINCT]
{*|table.*| [table.field1[as alias1][,table.field1[as alias2][......]]}
FROM table_name [as table_alias]
    [left | right | inner join table_name2] -- 联合查询
    [WHERE ...] -- 指定结果需要满足的条件
    [GROUP BY ...] -- 指定结果按照哪几个字段来分组
    [HAVING] -- 过滤分组的记录必须满足的次要条件
    [ORDER BY ...] -- 指定查询记录按一个或多个条件排序
    [LIMIT {[offset,]row_count | row_countOFFSET offset}];
    -- 指定查询记录从哪条到哪条

Mysql狂神笔记:https://gitee.com/zhayuyao/java-notes/blob/master/md%E6%96%87%E6%A1%A3/%E7%8B%82%E7%A5%9E%E8%AF%B4java/03.MySql/MySQL%E5%9F%BA%E7%A1%80.md

Logback

项目中日志系统是必不可少的,目前比较流行的日志框架有log4j、logback等,可能大家还不知道,这两个框架的作者是同一个人,Logback旨在作为流行的log4j项目的后续版本,从而恢复log4j离开的位置。另外 slf4j(Simple Logging Facade for Java) 则是一个日志门面框架,提供了日志系统中常用的接口,logback 和 log4j 则对slf4j 进行了实现。我们本文将讲述如何在spring boot 中应用 logback+slf4j实现日志的记录。 实际开发中我们不需要直接添加依赖,你会发现spring-boot-starter其中包含了 spring-boot-starter-logging,该依赖内容就是 Spring Boot 默认的日志框架 Logback+SLF4J。而 spring-boot-starter-web 包含了spring-boot-starte,所以我们只需要引入web组件即可。

Logback与springboot整合用法:https://blog.csdn.net/white_ice/article/details/85065219

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- appender是configuration的子节点,是负责写日志的组件。 -->
    <!-- ConsoleAppender:把日志输出到控制台 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 默认情况下,每个日志事件都会立即刷新到基础输出流。 这种默认方法更安全,因为如果应用程序在没有正确关闭appender的情况下退出,则日志事件不会丢失。
         但是,为了显着增加日志记录吞吐量,您可能希望将immediateFlush属性设置为false -->
        <!--<immediateFlush>true</immediateFlush>-->
        <encoder>
            <!-- %37():如果字符没有37个字符长度,则左侧用空格补齐 -->
            <!-- %-37():如果字符没有37个字符长度,则右侧用空格补齐 -->
            <!-- %15.15():如果记录的线程字符长度小于15(第一个)则用空格在左侧补齐,如果字符长度大于15(第二个),则从开头开始截断多余的字符 -->
            <!-- %-40.40():如果记录的logger字符长度小于40(第一个)则用空格在右侧补齐,如果字符长度大于40(第二个),则从开头开始截断多余的字符 -->
            <!-- %msg:日志打印详情 -->
            <!-- %n:换行符 -->
            <!-- %highlight():转换说明符以粗体红色显示其级别为ERROR的事件,红色为WARN,BLUE为INFO,以及其他级别的默认颜色。 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) --- [%15.15(%thread)] %cyan(%-40.40(%logger{40})) : %msg%n</pattern>
            <!-- 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- info 日志-->
    <!-- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
    <!-- 以下的大概意思是:1.先按日期存日志,日期变了,将前一天的日志文件名重命名为XXX%日期%索引,新的日志仍然是project_info.log -->
    <!--             2.如果日期没有发生变化,但是当前日志的文件大小超过10MB时,对当前日志进行分割 重命名-->
    <appender name="mp-service" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志文件路径和名称-->
        <File>/Users/zuoping.liu/Documents/logs/mpservice_info.log</File>
        <!--是否追加到文件末尾,默认为true-->
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>DENY</onMatch><!-- 如果命中ERROR就禁止这条日志 -->
            <onMismatch>ACCEPT</onMismatch><!-- 如果没有命中就使用这条规则 -->
        </filter>
        <!--有两个与RollingFileAppender交互的重要子组件。 第一个RollingFileAppender子组件,即RollingPolicy:负责执行翻转所需的操作。
         RollingFileAppender的第二个子组件,即TriggeringPolicy:将确定是否以及何时发生翻转。 因此,RollingPolicy负责什么和TriggeringPolicy负责什么时候.
        作为任何用途,RollingFileAppender必须同时设置RollingPolicy和TriggeringPolicy,但是,如果其RollingPolicy也实现了TriggeringPolicy接口,则只需要显式指定前者。-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志文件的名字会根据fileNamePattern的值,每隔一段时间改变一次 -->
            <!-- 文件名:logs/project_info.2017-12-05.0.log -->
            <!-- 注意:SizeAndTimeBasedRollingPolicy中 %i和%d令牌都是强制性的,必须存在,要不会报错 -->
            <fileNamePattern>logs/project_info.%d.%i.log</fileNamePattern>
            <!-- 每产生一个日志文件,该日志文件的保存期限为30天, ps:maxHistory的单位是根据fileNamePattern中的翻转策略自动推算出来的,例如上面选用了yyyy-MM-dd,则单位为天
            如果上面选用了yyyy-MM,则单位为月,另外上面的单位默认为yyyy-MM-dd-->
            <maxHistory>30</maxHistory>
            <!-- 每个日志文件到10mb的时候开始切分,最多保留30天,但最大到20GB,哪怕没到30天也要删除多余的日志 -->
            <totalSizeCap>20GB</totalSizeCap>
            <!-- maxFileSize:这是活动文件的大小,默认值是10MB,测试时可改成5KB看效果 -->
            <maxFileSize>10MB</maxFileSize>
        </rollingPolicy>
        <!--编码器-->
        <encoder>
            <!-- pattern节点,用来设置日志的输入格式 ps:日志文件中没有设置颜色,否则颜色部分会有ESC[0:39em等乱码-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%15.15(%thread)] %-40.40(%logger{40}) : %msg%n</pattern>
            <!-- 记录日志的编码:此处设置字符集 - -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- error 日志-->
    <!-- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
    <!-- 以下的大概意思是:1.先按日期存日志,日期变了,将前一天的日志文件名重命名为XXX%日期%索引,新的日志仍然是project_error.log -->
    <!--             2.如果日期没有发生变化,但是当前日志的文件大小超过10MB时,对当前日志进行分割 重命名-->
    <appender name="mpservice_error_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志文件路径和名称-->
        <File>/Users/zuoping.liu/Documents/logs/mpservice_error.log</File>
        <!--是否追加到文件末尾,默认为true-->
        <append>true</append>
        <!-- ThresholdFilter过滤低于指定阈值的事件。 对于等于或高于阈值的事件,ThresholdFilter将在调用其decision()方法时响应NEUTRAL。 但是,将拒绝级别低于阈值的事件 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level><!-- 低于ERROR级别的日志(debug,info)将被拒绝,等于或者高于ERROR的级别将相应NEUTRAL -->
        </filter>
        <!--有两个与RollingFileAppender交互的重要子组件。 第一个RollingFileAppender子组件,即RollingPolicy:负责执行翻转所需的操作。
        RollingFileAppender的第二个子组件,即TriggeringPolicy:将确定是否以及何时发生翻转。 因此,RollingPolicy负责什么和TriggeringPolicy负责什么时候.
       作为任何用途,RollingFileAppender必须同时设置RollingPolicy和TriggeringPolicy,但是,如果其RollingPolicy也实现了TriggeringPolicy接口,则只需要显式指定前者。-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 活动文件的名字会根据fileNamePattern的值,每隔一段时间改变一次 -->
            <!-- 文件名:logs/project_error.2017-12-05.0.log -->
            <!-- 注意:SizeAndTimeBasedRollingPolicy中 %i和%d令牌都是强制性的,必须存在,要不会报错 -->
            <fileNamePattern>logs/project_error.%d.%i.log</fileNamePattern>
            <!-- 每产生一个日志文件,该日志文件的保存期限为30天, ps:maxHistory的单位是根据fileNamePattern中的翻转策略自动推算出来的,例如上面选用了yyyy-MM-dd,则单位为天
            如果上面选用了yyyy-MM,则单位为月,另外上面的单位默认为yyyy-MM-dd-->
            <maxHistory>30</maxHistory>
            <!-- 每个日志文件到10mb的时候开始切分,最多保留30天,但最大到20GB,哪怕没到30天也要删除多余的日志 -->
            <totalSizeCap>20GB</totalSizeCap>
            <!-- maxFileSize:这是活动文件的大小,默认值是10MB,测试时可改成5KB看效果 -->
            <maxFileSize>10MB</maxFileSize>
        </rollingPolicy>
        <!--编码器-->
        <encoder>
            <!-- pattern节点,用来设置日志的输入格式 ps:日志文件中没有设置颜色,否则颜色部分会有ESC[0:39em等乱码-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%15.15(%thread)] %-40.40(%logger{40}) : %msg%n</pattern>
            <!-- 记录日志的编码:此处设置字符集 - -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--给定记录器的每个启用的日志记录请求都将转发到该记录器中的所有appender以及层次结构中较高的appender(不用在意level值)。
    换句话说,appender是从记录器层次结构中附加地继承的。
    例如,如果将控制台appender添加到根记录器,则所有启用的日志记录请求将至少在控制台上打印。
    如果另外将文件追加器添加到记录器(例如L),则对L和L'子项启用的记录请求将打印在文件和控制台上。
    通过将记录器的additivity标志设置为false,可以覆盖此默认行为,以便不再添加appender累积-->
    <!-- configuration中最多允许一个root,别的logger如果没有设置级别则从父级别root继承 -->
    <root level="INFO">
        <level value="info" additivity="false"/>
        <appender-ref ref="mp-service"/>
        <appender-ref ref="console"/>
    </root>

    <!-- 指定项目中某个包,当有日志操作行为时的日志记录级别 -->
    <!-- 级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE  -->
    <logger name="com.sailing.springbootmybatis" level="INFO">
        <appender-ref ref="mp-service" />
        <appender-ref ref="mpservice_error_log" />
    </logger>

    <!-- 利用logback输入mybatis的sql日志,
    注意:如果不加 additivity="false" 则此logger会将输出转发到自身以及祖先的logger中,就会出现日志文件中sql重复打印-->
    <logger name="com.sailing.springbootmybatis.mapper" level="DEBUG" additivity="false">
        <appender-ref ref="mp-service" />
        <appender-ref ref="mpservice_error_log" />
    </logger>

    <!-- additivity=false代表禁止默认累计的行为,即com.atomikos中的日志只会记录到日志文件中,不会输出层次级别更高的任何appender-->
    <logger name="com.atomikos" level="INFO" additivity="false">
        <appender-ref ref="mp-service" />
        <appender-ref ref="mpservice_error_log" />
    </logger>
</configuration>

自定义注解

Java自定义注解一般使用场景为:自定义注解+拦截器或者AOP,使用自定义注解来自己设计框架,使得代码看起来非常优雅。 本文将先从自定义注解的基础概念说起,然后开始实战,写小段代码实现自定义注解+拦截器,自定义注解+AOP。

Java 自定义注解及使用场景:https://www.jianshu.com/p/a7bedc771204

https://blog.csdn.net/qq_37435078/article/details/90523309

//annatation包下
/**
 * 自定义资源文件读取注解,读取的优先级最低,如果已经存在相同的key,则放弃
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XwzPropertySource {
    String value();

    boolean loadActiveProfile() default true;

    int order() default Ordered.LOWEST_PRECEDENCE;
}

解析注解的Java类

//props包下
/**
* 自定义资源文件读取注解,读取的优先级最低,如果已经存在相同的key,则放弃
*/
@Slf4j
public class XwzPropertySourcePostProcessor implements BeanFactoryPostProcessor, InitializingBean, Ordered {
	private final ResourceLoader resourceLoader;
	private final List<PropertySourceLoader> propertySourceLoaders;

	public XwzPropertySourcePostProcessor() {
		this.resourceLoader = new DefaultResourceLoader();
		this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		log.info("XwzPropertySourcePostProcessor process @XwzPropertySource bean.");
		Map<String, Object> beansWithAnnotation = beanFactory.getBeansWithAnnotation(XwzPropertySource.class);
		Set<Map.Entry<String, Object>> beanEntrySet = beansWithAnnotation.entrySet();
		// 没有 @XwzPropertySource 注解,跳出
		if (beanEntrySet.isEmpty()) {
			log.warn("Not found @XwzPropertySource on spring bean class.");
			return;
		}
		// 组装资源
		List<PropertyFile> propertyFileList = new ArrayList<>();
		for (Map.Entry<String, Object> entry : beanEntrySet) {
			Class<?> beanClass = ClassUtils.getUserClass(entry.getValue());
			QxwzPropertySource propertySource = AnnotationUtils.getAnnotation(beanClass, QxwzPropertySource.class);
			if (propertySource == null) {
				continue;
			}
			int order = propertySource.order();
			boolean loadActiveProfile = propertySource.loadActiveProfile();
			String location = propertySource.value();
			propertyFileList.add(new PropertyFile(order, location, loadActiveProfile));
		}

		// 装载 PropertySourceLoader
		Map<String, PropertySourceLoader> loaderMap = new HashMap<>(16);
		for (PropertySourceLoader loader : propertySourceLoaders) {
			String[] loaderExtensions = loader.getFileExtensions();
			for (String extension : loaderExtensions) {
				loaderMap.put(extension, loader);
			}
		}
		// 去重,排序
		List<PropertyFile> sortedPropertyList = propertyFileList.stream()
			.distinct()
			.sorted()
			.collect(Collectors.toList());
		ConfigurableEnvironment environment = beanFactory.getBean(ConfigurableEnvironment.class);
		MutablePropertySources propertySources = environment.getPropertySources();

		// 只支持 activeProfiles,没有必要支持 spring.profiles.include。
		String[] activeProfiles = environment.getActiveProfiles();
		ArrayList<PropertySource> propertySourceList = new ArrayList<>();
		for (String profile : activeProfiles) {
			for (PropertyFile propertyFile : sortedPropertyList) {
				// 不加载 ActiveProfile 的配置文件
				if (!propertyFile.loadActiveProfile) {
					continue;
				}
				String extension = propertyFile.getExtension();
				PropertySourceLoader loader = loaderMap.get(extension);
				if (loader == null) {
					throw new IllegalArgumentException("Can't find PropertySourceLoader for PropertySource extension:" + extension);
				}
				String location = propertyFile.getLocation();
				String filePath = StringUtils.stripFilenameExtension(location);
				String profiledLocation = filePath + "-" + profile + "." + extension;
				Resource resource = resourceLoader.getResource(profiledLocation);
				loadPropertySource(profiledLocation, resource, loader, propertySourceList);
			}
		}
		// 本身的 Resource
		for (PropertyFile propertyFile : sortedPropertyList) {
			String extension = propertyFile.getExtension();
			PropertySourceLoader loader = loaderMap.get(extension);
			String location = propertyFile.getLocation();
			Resource resource = resourceLoader.getResource(location);
			loadPropertySource(location, resource, loader, propertySourceList);
		}
		// 转存
		for (PropertySource propertySource : propertySourceList) {
			propertySources.addLast(propertySource);
		}
	}

	private static void loadPropertySource(String location, Resource resource,
										   PropertySourceLoader loader,
										   List<PropertySource> sourceList) {
		if (resource.exists()) {
			String name = "xwzPropertySource: [" + location + "]";
			try {
				sourceList.addAll(loader.load(name, resource));
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		log.info("XwzPropertySourcePostProcessor init.");
	}

	@Override
	public int getOrder() {
		return Ordered.LOWEST_PRECEDENCE;
	}

	@Getter
	@ToString
	@EqualsAndHashCode
	private static class PropertyFile implements Comparable<PropertyFile> {
		private final int order;
		private final String location;
		private final String extension;
		private final boolean loadActiveProfile;

		PropertyFile(int order, String location, boolean loadActiveProfile) {
			this.order = order;
			this.location = location;
			this.loadActiveProfile = loadActiveProfile;
			this.extension = Objects.requireNonNull(StringUtils.getFilenameExtension(location));
		}

		@Override
		public int compareTo(PropertyFile other) {
			return Integer.compare(this.order, other.order);
		}
	}
}

将解析器注入bean容器

//config包下
@Configuration(proxyBeanMethods = false)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class XwzPropertyConfiguration {
	@Bean
	public XwzPropertySourcePostProcessor xwzPropertySourcePostProcessor() {
		return new XwzPropertySourcePostProcessor();
	}
}

使用方式

/**
 * @description: swagger资源配置
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SwaggerProperties.class)
@Import(SwaggerAutoConfiguration.class)
@XwzPropertySource(value = "classpath:/default-swagger.yml")
public class SwaggerWebConfiguration implements WebMvcConfigurer {
}
/**
 * @description: MybatisPlus配置
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AllArgsConstructor
@XwzPropertySource(value = "classpath:/default-mybatis.yml")
public class MybatisPlusConfig {
}

SpringBoot+AOP+自定义注解,优雅实现日志记录:https://blog.csdn.net/wujiangbo520/article/details/122057616

Mybatis

引入依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
  • 编写mybatis的核心配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <!--configuration核心配置文件-->
    <configuration>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    
    </configuration>
  • 接口实现类由原来的UserDaoImpl转变为一个 Mapper配置文件.

    <?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">
            <!--namespace=绑定一个对应的Dao/Mapper接口-->
    <mapper namespace="com.kuang.dao.UserDao">
    
    <!--select查询语句-->
       <select id="getUserList" resultType="com.kuang.pojo.User">
           select * from mybatis.user
       </select>
    
    </mapper>

使用注解开发

  1. 注解在接口上实现

    @Select("select * from user")
    List<User> getUsers();
    
  2. 需要再核心配置文件中绑定接口!

    <!--绑定接口-->
    <mappers>
        <mapper class="com.kuang.dao.UserMapper"/>
    </mappers>
    
  3. 测试

本质:反射机制实现

编写接口,增加注解

public interface UserMapper {
    @Select("select * from user")
    List<User> getUsers();

    // 方法存在多个参数,所有的参数前面必须加上 @Param("id")注解
    @Select("select * from user where id = #{id}")
    User getUserByID(@Param("id") int id);

    @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);
 
    @Update("update user set name=#{name},pwd=#{password} where id = #{id}")
    int updateUser(User user);

    @Delete("delete from user where id = #{uid}")
    int deleteUser(@Param("uid") int id);
}

mapper写法与官网:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html

Git地址:https://github.com/mybatis/mybatis-3

狂神Mybatis视频源码加笔记:https://github.com/lzh66666/Mybatis-kuang-

难点:多对一与一对多的实现以及动态sql的编写:https://mybatis.org/mybatis-3/zh/dynamic-sql.html

Tkmybatis

TKMybatis 是基于 Mybatis 框架开发的一个工具,内部实现了对单表的基本数据操作,只需要简单继承 TKMybatis 提供的接口,就能够实现无需编写任何 sql 即能完成单表操作。

引入依赖

<!--通用mapper起步依赖-->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>

启动类中配置扫描

@SpringBootApplication
@MapperScan(basePackages = {"com.tom.order.mapper"})
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

在实体类中,常用的注解和意义为:

@Table:描述数据库表信息,主要属性有name(表名)、schema、catalog、uniqueConstraints等。

@Id:指定表主键字段,无属性值。

@Column:描述数据库字段信息,主要属性有name(字段名)、columnDefinition、insertable、length、nullable(是否可为空)、precision、scale、table、unique、updatable等。

@ColumnType:描述数据库字段类型,可对一些特殊类型作配置,进行特殊处理,主要属性有jdbcType、column、typeHandler等。

其他注解如:@Transient、@ColumnResult、@JoinColumn、@OrderBy、@Embeddable等暂不描述

//Category
@Table(name = "category")
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;
//Group
@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;

Application.yml配置

结合pagehelper使用https://zhuanlan.zhihu.com/p/344982068

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost/trms_data?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: yourpassword
mybatis:
  configuration:
# 开启驼峰命名规范,使数据库中 user_name 可以映射实体属性 userName
    map-underscore-to-camel-case: true
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: com.qxwz.qa.trms.pojo
# PageHelper分页插件
pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  params: count=countSql

dao中使用 单表操作,只需要继承 tk.mybatis 下的 Mapper 接口即可使用

import tk.mybatis.mapper.common.Mapper;

@Repository
public interface BrandMapper extends Mapper<Brand> {
}

Dao层与service层的使用如下:

用法:https://blog.csdn.net/qq_34416331/article/details/106322596

https://blog.csdn.net/dgh112233/article/details/117372645

对于一些多表操作,仍然需要编写xml文件

<!--    Category-->
<?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.CategoryMapper">
    <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>
</mapper>
<!--   Group-->
<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>

Mybatis_Plus

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

一、导入依赖

<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>

二、编写配置文件

qxwz:
  monitor:
    warning:
#      areaCode: 331123
      areaCode: 110101
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost/notice_cqcs?autoReconnect=true&allowMultiQueries=true&characterEncoding=utf-8
    username: root
    password: yourpassword
#mybatis-plus配置
mybatis-plus:
  mapper-locations: classpath*:mapper/*Mapper.xml
  #实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.qxwz.**.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

config包下编写mybatisplus的配置类

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AllArgsConstructor
public class MybatisPlusConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    @ConditionalOnMissingBean(MybatisPlusInterceptor.class)
    public MybatisPlusInterceptor mybatisPlusInterceptor(MybatisPlusProperties mybatisPlusProperties) {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 配置分页拦截器
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
        paginationInterceptor.setMaxLimit(mybatisPlusProperties.getPageLimit());
        paginationInterceptor.setOverflow(mybatisPlusProperties.getOverflow());
        paginationInterceptor.setOptimizeJoin(mybatisPlusProperties.getOptimizeJoin());
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}

properties包下的MybatisPlusProperties.class如下

@Data
@ConfigurationProperties(prefix = "mybatis-plus")
public class MybatisPlusProperties {
    /**
     * 分页最大数
     */
    private Long pageLimit = 500L;
    /**
     * 溢出总页数后是否进行处理
     */
    protected Boolean overflow = false;
    /**
     * join优化
     */
    private Boolean optimizeJoin = false;
}

Mybatis代码生成器狂神版

// 代码自动生成器
public class MyFirstGenerator {
    public static void main(String[] args) {
        // 需要构建一个 代码自动生成器 对象
        AutoGenerator mpg = new AutoGenerator();
        // 配置策略
        // 1、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath+"/src/main/java");
        gc.setAuthor("liuzuoping");
        gc.setFileOverride(false);// 是否覆盖
        gc.setServiceName("%sService");// 去Service的I前缀
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);
        gc.setIdType(IdType.AUTO);
        gc.setOpen(true);
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/notice_cqcs?autoReconnect=true&allowMultiQueries=true&characterEncoding=utf-8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("yourpassword");
        mpg.setDataSource(dsc);
        //3、包的配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("monitor_blog");
        pc.setParent("com.example");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");
        mpg.setPackageInfo(pc);
        //4、策略配置
        StrategyConfig strategy = new StrategyConfig();
    strategy.setInclude("notice_alarm_process_record","notice_alarm_record","notice_inspection_detail","notice_monitor_item","notice_monitor_object","notice_monitor_object_point_ref","notice_monitor_point","notice_monitor_point_device_ref"); // 设置要映射的表名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);// 自动lombok;
        strategy.setLogicDeleteFieldName("deleted");   // 自动填充配置
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);
        // 乐观锁
        strategy.setVersionFieldName("version");
        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true);
        // localhost:8080/hello_id_2
        mpg.setStrategy(strategy);
        mpg.execute();
        //执行
    }
}

Mybatis生成器权哥版

public class MySecondGenerator {
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        Map<String, String> map = System.getenv();
        // 获取用户名
        String userName = map.get("USER");
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath+"/src/main/java");
        gc.setAuthor(userName);
        //实体属性 Swagger2 注解
        gc.setSwagger2(true);
        gc.setIdType(IdType.AUTO);
        gc.setOpen(true);
        // service 命名方式
        gc.setServiceName("%sService");
        // service impl 命名方式
        gc.setServiceImplName("%sServiceImpl");
        // 自定义文件命名,注意 %s 会自动填充表实体属性!
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");
        gc.setFileOverride(true);
        gc.setActiveRecord(true);
        // XML 二级缓存
        gc.setEnableCache(false);
        // XML ResultMap
        gc.setBaseResultMap(true);
        // XML columList122.51.232.48
        gc.setBaseColumnList(true);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/notice_cqcs?autoReconnect=true&allowMultiQueries=true&characterEncoding=utf-8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("yourpassword");
        mpg.setDataSource(dsc);
        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.example");
        pc.setModuleName("monitor");
        pc.setEntity("entity");
        // pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");
        pc.setServiceImpl("service.impl");
        mpg.setPackageInfo(pc);
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        // 如果模板引擎是 freemarker
        // String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        String templatePath = "/templates/mapper.xml.vm";
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }

        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 配置自定义输出模板
        //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // strategy.setSuperEntityClass(BaseEntity.class);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setFieldPrefix("f_");
        //强制实体类中映射数据库字段加上注解
        strategy.setEntityTableFieldAnnotationEnable(true);
        strategy.setTablePrefix("findtrace_","wl_","draw_line_");
        //生成 <code>@RestController</code> 控制器
        strategy.setRestControllerStyle(true);
        // 公共父类
        // strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
        // 写于父类中的公共字段
        // strategy.setSuperEntityColumns("id","gmt_created","gmt_modified","user_id");
        // 是否生成实体时,生成字段注解
        strategy.setEntityTableFieldAnnotationEnable(true);
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new VelocityTemplateEngine());
        mpg.execute();
    }
}

相比于第一个生成器,entity部分多了@TableName("notice_monitor_object")并且字段部分增加了

@ApiModelProperty(value = "监测对象编码")
@TableField("code")

并且生成的mapper.xml中多了如下部分

<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.example.monitor.entity.NoticeAlarmProcessRecord">
    <id column="id" property="id" />
    <result column="record_id" property="recordId" />
    <result column="false_positive" property="falsePositive" />
    <result column="process_user" property="processUser" />
    <result column="opinions" property="opinions" />
    <result column="is_deleted" property="isDeleted" />
    <result column="env" property="env" />
    <result column="remark" property="remark" />
    <result column="gmt_create" property="gmtCreate" />
    <result column="gmt_modified" property="gmtModified" />
</resultMap>

<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
    id, record_id, false_positive, process_user, opinions, is_deleted, env, remark, gmt_create, gmt_modified
</sql>

生成entity, service(service+Impl),controller, mapper后

在mapper中新增自己所需的方法

@Mapper
public interface NoticeAlarmRecordMapper extends BaseMapper<NoticeAlarmRecord> {
    List<WarnItemVO> countWarnItem(@Param(value = "param") NoticeAlarmRecordQuery noticeAlarmRecordQuery);

    List<WarningSumVO> countWarnType(@Param(value = "param") NoticeRecordQuery noticeRecordQuery);

    List<EventVO> countEvent(@Param(value = "param") NoticeRecordQuery noticeRecordQuery);
}
@Mapper
public interface NoticeMonitorObjectMapper extends BaseMapper<NoticeMonitorObject> {
    List<NoticeMonitorObjectDTO> listNoticeMonitorPoint(@Param(value = "param") NoticeMonitorPointQuery query);

    NoticeMonitorObjectDTO getNoticeMonitorObject(@Param(value = "param") NoticeMonitorObjectQuery query);

    List<NoticeInspectionDetailDTO> listNoticeInspectionDetail(@Param(value = "param") BaseWarningRequest query);
}

对应的xml中新增方法映射

    <!--    NoticeAlarmRecordMapper-->
    <select id="countWarnItem" resultType="com.qxwz.monitor.vo.WarnItemVO">
        select
            DATE_FORMAT(trigger_time,'%Y-%m-%d') AS date ,
            count(*) AS totalCount,
            count(case status when 'todo' then 1 end) AS todoCount,
            count(case status when 'done' then 1 end) AS doneCount
        from notice_alarm_record nar
        where point_id in
        <foreach item="item" index="index" collection="param.pointIds" open="("  close=")" separator=",">
            #{item}
        </foreach>
            <if test="param.type != null and param.type != ''">
                and item_code =#{param.type}
            </if>
        and trigger_time between #{param.startDate} and #{param.endDate}
        group by date
    </select>

    <select id="countWarnType" resultType="com.qxwz.monitor.vo.WarningSumVO" parameterType="com.qxwz.monitor.vo.NoticeRecordQuery">
        select
            DATE_FORMAT(trigger_time,'%Y-%m-%d') AS date ,
            count(*) AS warningSum
        from notice_alarm_record nar
        where point_id in
        <foreach item="item" index="index" collection="param.pointIds" open="("  close=")" separator=",">
            #{item}
        </foreach>
          and trigger_time between #{param.startDate} and #{param.endDate}
        group by date
    </select>

    <select id="countEvent" resultType="com.qxwz.monitor.vo.EventVO" parameterType="com.qxwz.monitor.vo.NoticeRecordQuery">
        select
        DATE_FORMAT(trigger_time,'%Y-%m-%d') AS date ,
        count(case status when 'todo' then 1 end) AS todoEventSum,
        count(case status when 'done' then 1 end) AS doneEventSum
        from notice_alarm_record nar
        where point_id in
        <foreach item="item" index="index" collection="param.pointIds" open="("  close=")" separator=",">
            #{item}
        </foreach>
        and trigger_time between #{param.startDate} and #{param.endDate}
        group by date
    </select>
    <!--    NoticeMonitorObjectMapper-->
    <resultMap id="listNoticeMonitorPointMP" type="com.qxwz.monitor.dto.NoticeMonitorObjectDTO">
        <id column="obj_id" property="id" />
        <result column="obj_code" property="code" />
        <result column="obj_name" property="name" />
        <result column="obj_alias_name" property="aliasName" />
        <result column="obj_type" property="type" />
        <result column="obj_area_code" property="areaCode" />
        <result column="obj_province_code" property="provinceCode" />
        <result column="obj_city_code" property="cityCode" />
        <result column="obj_area_detail" property="areaDetail" />
        <result column="obj_lalType" property="laltype" />
        <result column="obj_longitude" property="longitude" />
        <result column="obj_latitude" property="latitude" />
        <result column="obj_altitude" property="altitude" />
        <result column="obj_monitor_unit_name" property="monitorUnitName" />
        <result column="obj_monitor_unit_linkman" property="monitorUnitLinkman" />
        <result column="obj_monitor_unit_mobile" property="monitorUnitMobile" />
        <result column="obj_construction_unit_name" property="constructionUnitName" />
        <result column="obj_construction_unit_linkman" property="constructionUnitLinkman" />
        <result column="obj_construction_unit_mobile" property="constructionUnitMobile" />
        <result column="obj_op_unit_name" property="opUnitName" />
        <result column="obj_op_unit_linkman" property="opUnitLinkman" />
        <result column="obj_op_unit_mobile" property="opUnitMobile" />
        <result column="obj_object_desc" property="objectDesc" />
        <result column="obj_status" property="status" />
        <result column="obj_is_deleted" property="isDeleted" />
        <result column="obj_env" property="env" />
        <result column="obj_remark" property="remark" />
        <result column="obj_create_user" property="createUser" />
        <result column="obj_modify_user" property="modifyUser" />
        <result column="obj_gmt_create" property="gmtCreate" />
        <result column="obj_gmt_modified" property="gmtModified" />
        <collection  property="noticeMonitorPoints" ofType="com.qxwz.monitor.entity.NoticeMonitorPoint">
            <id column="nmp_id" property="id" />
            <result column="nmp_code" property="code" />
            <result column="nmp_name" property="name" />
            <result column="nmp_longitude" property="longitude" />
            <result column="nmp_latitude" property="latitude" />
            <result column="nmp_altitude" property="altitude" />
            <result column="nmp_point_desc" property="pointDesc" />
            <result column="nmp_status" property="status" />
            <result column="nmp_is_deleted" property="isDeleted" />
            <result column="nmp_env" property="env" />
            <result column="nmp_remark" property="remark" />
            <result column="nmp_gmt_create" property="gmtCreate" />
            <result column="nmp_gmt_modified" property="gmtModified" />
        </collection>
    </resultMap>
    <select id="listNoticeMonitorPoint" resultMap="listNoticeMonitorPointMP"
            parameterType="com.qxwz.monitor.vo.NoticeMonitorPointQuery">
        <include refid="listNoticeMonitorPointSql"></include>
    </select>

    <sql id="listNoticeMonitorPointSql">
        SELECT
            obj.id AS obj_id,
            obj.code AS obj_code,
            obj.name AS obj_name,
            obj.alias_name AS obj_alias_name,
            obj.type AS obj_type,
            obj.area_code AS obj_area_code,
            obj.province_code AS obj_province_code,
            obj.city_code AS obj_city_code,
            obj.area_detail AS obj_area_detail,
            obj.lalType AS obj_lalType,
            obj.longitude AS obj_longitude,
            obj.latitude AS obj_latitude,
            obj.altitude AS obj_altitude,
            obj.monitor_unit_name AS obj_monitor_unit_name,
            obj.monitor_unit_linkman AS obj_monitor_unit_linkman,
            obj.monitor_unit_mobile AS obj_monitor_unit_mobile,
            obj.construction_unit_name AS obj_construction_unit_name,
            obj.construction_unit_linkman AS obj_construction_unit_linkman,
            obj.construction_unit_mobile AS obj_construction_unit_mobile,
            obj.op_unit_name AS obj_op_unit_name,
            obj.op_unit_linkman AS obj_op_unit_linkman,
            obj.op_unit_mobile AS obj_op_unit_mobile,
            obj.object_desc AS obj_object_desc,
            obj.status AS obj_status,
            obj.is_deleted AS obj_is_deleted,
            obj.env AS obj_env,
            obj.remark AS obj_remark,
            obj.create_user AS obj_create_user,
            obj.modify_user AS obj_modify_user,
            obj.gmt_create AS obj_gmt_create,
            obj.gmt_modified AS obj_gmt_modified,

            nmp.id AS nmp_id,
            nmp.code AS nmp_code,
            nmp.name AS nmp_name,
            nmp.longitude AS nmp_longitude,
            nmp.latitude AS nmp_latitude,
            nmp.altitude AS nmp_altitude,
            nmp.point_desc AS nmp_point_desc,
            nmp.status AS nmp_status,
            nmp.is_deleted AS nmp_is_deleted,
            nmp.env AS nmp_env,
            nmp.remark AS nmp_remark,
            nmp.gmt_create AS nmp_gmt_create,
            nmp.gmt_modified AS nmp_gmt_modified

        FROM (
        SELECT *
        FROM notice_monitor_object nmo
        WHERE nmo.`type`
        IN
        <foreach item="type" index="index" collection="param.types" open="("  close=")" separator=",">
            #{type}
        </foreach>
        AND nmo.area_code = #{param.areaCode}
        AND nmo.is_deleted = 0
        ) obj
        INNER JOIN notice_monitor_object_point_ref nmopr ON obj.id = nmopr.monitor_id AND nmopr.is_deleted = 0
        INNER JOIN notice_monitor_point nmp ON nmp.code = nmopr.point_code AND nmp.is_deleted = 0
    </sql>

    <select id="getNoticeMonitorObject" resultMap="listNoticeMonitorPointMP"
            parameterType="com.qxwz.monitor.vo.NoticeMonitorObjectQuery">
        <include refid="getNoticeMonitorObjectSql"></include>
    </select>

    <sql id="getNoticeMonitorObjectSql">
        SELECT
        obj.id AS obj_id,
        obj.code AS obj_code,
        obj.name AS obj_name,
        obj.alias_name AS obj_alias_name,
        obj.type AS obj_type,
        obj.area_code AS obj_area_code,
        obj.province_code AS obj_province_code,
        obj.city_code AS obj_city_code,
        obj.area_detail AS obj_area_detail,
        obj.lalType AS obj_lalType,
        obj.longitude AS obj_longitude,
        obj.latitude AS obj_latitude,
        obj.altitude AS obj_altitude,
        obj.monitor_unit_name AS obj_monitor_unit_name,
        obj.monitor_unit_linkman AS obj_monitor_unit_linkman,
        obj.monitor_unit_mobile AS obj_monitor_unit_mobile,
        obj.construction_unit_name AS obj_construction_unit_name,
        obj.construction_unit_linkman AS obj_construction_unit_linkman,
        obj.construction_unit_mobile AS obj_construction_unit_mobile,
        obj.op_unit_name AS obj_op_unit_name,
        obj.op_unit_linkman AS obj_op_unit_linkman,
        obj.op_unit_mobile AS obj_op_unit_mobile,
        obj.object_desc AS obj_object_desc,
        obj.status AS obj_status,
        obj.is_deleted AS obj_is_deleted,
        obj.env AS obj_env,
        obj.remark AS obj_remark,
        obj.create_user AS obj_create_user,
        obj.modify_user AS obj_modify_user,
        obj.gmt_create AS obj_gmt_create,
        obj.gmt_modified AS obj_gmt_modified,

        nmp.id AS nmp_id,
        nmp.code AS nmp_code,
        nmp.name AS nmp_name,
        nmp.longitude AS nmp_longitude,
        nmp.latitude AS nmp_latitude,
        nmp.altitude AS nmp_altitude,
        nmp.point_desc AS nmp_point_desc,
        nmp.status AS nmp_status,
        nmp.is_deleted AS nmp_is_deleted,
        nmp.env AS nmp_env,
        nmp.remark AS nmp_remark,
        nmp.gmt_create AS nmp_gmt_create,
        nmp.gmt_modified AS nmp_gmt_modified

        FROM (
        SELECT *
        FROM notice_monitor_object nmo
        WHERE nmo.`type`
        IN
        <foreach item="type" index="index" collection="param.types" open="("  close=")" separator=",">
            #{type}
        </foreach>
        AND nmo.area_code = #{param.areaCode}
        AND nmo.id = #{param.objectId}
        AND nmo.is_deleted = 0
        ) obj
        LEFT JOIN notice_monitor_object_point_ref nmopr ON obj.id = nmopr.monitor_id AND nmopr.is_deleted = 0
        LEFT JOIN notice_monitor_point nmp ON nmp.code = nmopr.point_code AND nmp.is_deleted = 0
    </sql>

    <resultMap id="listNoticeInspectionDetailMP" type="com.qxwz.monitor.dto.NoticeInspectionDetailDTO">
        <id column="obj_id" property="id" />
        <result column="obj_code" property="code" />
        <result column="obj_name" property="name" />
        <result column="obj_alias_name" property="aliasName" />
        <result column="obj_type" property="type" />
        <result column="obj_area_code" property="areaCode" />
        <result column="obj_province_code" property="provinceCode" />
        <result column="obj_city_code" property="cityCode" />
        <result column="obj_area_detail" property="areaDetail" />
        <result column="obj_lalType" property="laltype" />
        <result column="obj_longitude" property="longitude" />
        <result column="obj_latitude" property="latitude" />
        <result column="obj_altitude" property="altitude" />
        <result column="obj_monitor_unit_name" property="monitorUnitName" />
        <result column="obj_monitor_unit_linkman" property="monitorUnitLinkman" />
        <result column="obj_monitor_unit_mobile" property="monitorUnitMobile" />
        <result column="obj_construction_unit_name" property="constructionUnitName" />
        <result column="obj_construction_unit_linkman" property="constructionUnitLinkman" />
        <result column="obj_construction_unit_mobile" property="constructionUnitMobile" />
        <result column="obj_op_unit_name" property="opUnitName" />
        <result column="obj_op_unit_linkman" property="opUnitLinkman" />
        <result column="obj_op_unit_mobile" property="opUnitMobile" />
        <result column="obj_object_desc" property="objectDesc" />
        <result column="obj_status" property="status" />
        <result column="obj_is_deleted" property="isDeleted" />
        <result column="obj_env" property="env" />
        <result column="obj_remark" property="remark" />
        <result column="obj_create_user" property="createUser" />
        <result column="obj_modify_user" property="modifyUser" />
        <result column="obj_gmt_create" property="gmtCreate" />
        <result column="obj_gmt_modified" property="gmtModified" />
        <collection  property="noticeInspectionDetails" ofType="com.qxwz.monitor.entity.NoticeInspectionDetail">
            <id column="nid_inspection_detail_id" property="inspectionDetailId" />
            <result column="nid_inspection_time" property="inspectionTime" />
            <result column="nid_object_id" property="objectId" />
            <result column="nid_inspector" property="inspector" />
            <result column="nid_inspection_site" property="inspectionSite" />
            <result column="nid_inspection_result" property="inspectionResult" />
            <result column="nid_resource_list" property="resourceList" />
            <result column="nid_suggest" property="suggest" />
            <result column="nid_inspection_id" property="inspectionId" />
            <result column="nid_is_deleted" property="isDeleted" />
            <result column="nid_create_user" property="createUser" />
            <result column="nid_modify_user" property="modifyUser" />
            <result column="nid_commit_status" property="commitStatus" />
            <result column="nid_gmt_create" property="gmtCreate" />
            <result column="nid_gmt_modified" property="gmtModified" />
        </collection>
    </resultMap>
    <select id="listNoticeInspectionDetail" resultMap="listNoticeInspectionDetailMP"
            parameterType="com.qxwz.monitor.vo.BaseWarningRequest">
        <include refid="listNoticeInspectionDetailSql"></include>
    </select>

    <sql id="listNoticeInspectionDetailSql">
        SELECT
        obj.id AS obj_id,
        obj.code AS obj_code,
        obj.name AS obj_name,
        obj.alias_name AS obj_alias_name,
        obj.type AS obj_type,
        obj.area_code AS obj_area_code,
        obj.province_code AS obj_province_code,
        obj.city_code AS obj_city_code,
        obj.area_detail AS obj_area_detail,
        obj.lalType AS obj_lalType,
        obj.longitude AS obj_longitude,
        obj.latitude AS obj_latitude,
        obj.altitude AS obj_altitude,
        obj.monitor_unit_name AS obj_monitor_unit_name,
        obj.monitor_unit_linkman AS obj_monitor_unit_linkman,
        obj.monitor_unit_mobile AS obj_monitor_unit_mobile,
        obj.construction_unit_name AS obj_construction_unit_name,
        obj.construction_unit_linkman AS obj_construction_unit_linkman,
        obj.construction_unit_mobile AS obj_construction_unit_mobile,
        obj.op_unit_name AS obj_op_unit_name,
        obj.op_unit_linkman AS obj_op_unit_linkman,
        obj.op_unit_mobile AS obj_op_unit_mobile,
        obj.object_desc AS obj_object_desc,
        obj.status AS obj_status,
        obj.is_deleted AS obj_is_deleted,
        obj.env AS obj_env,
        obj.remark AS obj_remark,
        obj.create_user AS obj_create_user,
        obj.modify_user AS obj_modify_user,
        obj.gmt_create AS obj_gmt_create,
        obj.gmt_modified AS obj_gmt_modified,

        nid.inspection_detail_id AS nid_inspection_detail_id,
        nid.inspection_time AS nid_inspection_time,
        nid.object_id AS nid_object_id,
        nid.inspector AS nid_inspector,
        nid.inspection_site AS nid_inspection_site,
        nid.inspection_result AS nid_inspection_result,
        nid.resource_list AS nid_resource_list,
        nid.suggest AS nid_suggest,
        nid.inspection_id AS nid_inspection_id,
        nid.is_deleted AS nid_is_deleted,
        nid.create_user AS nid_create_user,
        nid.modify_user AS nid_modify_user,
        nid.commit_status  AS nid_commit_status,
        nid.gmt_create AS nid_gmt_create,
        nid.gmt_modified AS nid_gmt_modified

        FROM (
        SELECT *
        FROM notice_monitor_object nmo
        WHERE nmo.area_code = #{param.areaCode}
        AND nmo.is_deleted = 0
        ) obj
        INNER JOIN notice_inspection_detail nid ON obj.id = nid.object_id AND nid.is_deleted = 0
    </sql>

相关中间类如下:

@Data
public class BaseWarningRequest {
    @ApiModelProperty(value = "行政区划code,默认为遂昌县的area_code")
    @Digits(integer = 20, fraction = 0,message = "区域编码必需是20位以内的数字")
    private String areaCode;
}
@Data
public class NoticeMonitorObjectQuery extends BaseWarningRequest{
    @ApiModelProperty(value = "监测类型",example = "1111")
    private List<String> types;

    @ApiModelProperty(value = "监测对象Id",example = "10")
    private Long objectId;
}
@Data
public class NoticeMonitorPointQuery extends BaseWarningRequest{
    @ApiModelProperty(value = "监测类型",example = "1111")
    private List<String> types;
}
@Data
public class EventVO {
    @ApiModelProperty(value = "事件发生日期",example = "0316")
    private String date;
    @ApiModelProperty(value = "已处理事件总数",example = "2000")
    private Long doneEventSum;
    @ApiModelProperty(value = "待处理事件总数",example = "2000")
    private Long todoEventSum;
}
public enum StatusTypeEnum {
    TODO("todo","未处理"),
    DONE("done","已处理");
    @Getter
    private String code;
    @Getter
    private String desc;

    StatusTypeEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}
@Data
public class NoticeInspectionDetailDTO extends NoticeMonitorObject {
    @ApiModelProperty(value = "监测对象对应的监测点集合")
    private List<NoticeInspectionDetail> noticeInspectionDetails;
}
@Data
@ToString
public class NoticeMonitorObjectDTO extends NoticeMonitorObject {
    @ApiModelProperty(value = "监测对象对应的监测点集合")
    private List<NoticeMonitorPoint> noticeMonitorPoints;
}
@Data
public class NoticeMonitorPointDTO extends NoticeMonitorPoint {
    @ApiModelProperty(value = "监测对象")
    private NoticeMonitorObject noticeMonitorObject;
}

service业务层方法

public interface NoticeAlarmProcessRecordService extends IService<NoticeAlarmProcessRecord> {

    List<NoticeAlarmProcessRecord> getProcessRecord(List<Long> recordIds);

    List<NoticeAlarmProcessRecord> listProcessRecord(List<Long> recordIds);

    Map<Long, NoticeAlarmProcessRecord> mapProcessRecord(List<Long> recordIds);
}
public interface NoticeAlarmRecordService extends IService<NoticeAlarmRecord> {

    int countWarning(List<Long> pointIds, String startTime, String endTime);

    List<NoticeAlarmRecord> countByItemCode(List<Long> pointIds, String startTime, String endTime, int limit);

    List<NoticeAlarmRecord> countUndoByItemCode(List<Long> pointIds, String startTime, String endTime, StatusTypeEnum status, int limit);

    List<NoticeAlarmRecord> getAlarmRecordList(List<Long> pointIds, String startTime, String endTime);

    int countTodoEvent(List<Long> pointIds,String startTime,String endTime);

    int countDoneEvent(List<Long> pointIds,String startTime,String endTime);

    List<NoticeAlarmRecord> getAlarmRecord(List<Long> pointIds,String startTime,String endTime,String type);

    List<WarnItemVO> countWarnItem(NoticeAlarmRecordQuery noticeAlarmRecordQuery);

    List<WarningSumVO> countWarnType(NoticeRecordQuery noticeRecordQuery);

    List<EventVO> countEvent(NoticeRecordQuery noticeRecordQuery);

    IPage<NoticeAlarmRecord> listByPage(IPage<NoticeAlarmRecord> page, Wrapper<NoticeAlarmRecord> queryWrapper);
}
public interface NoticeMonitorObjectService extends IService<NoticeMonitorObject> {

    List<NoticeMonitorObjectDTO> listNoticeMonitorPoint(NoticeMonitorPointQuery query);

    List<NoticeInspectionDetailDTO> listNoticeInspectionDetail(BaseWarningRequest query);

    NoticeMonitorObjectDTO getNoticeMonitorObject(NoticeMonitorObjectQuery query);

    List<MonitorObjectVO> listNoticeMonitorObject(BaseWarningRequest baseWarningRequest);
}
public interface NoticeMonitorPointService extends IService<NoticeMonitorPoint> {

    Map<Long, NoticeMonitorPointDTO> mapNoticeMonitorPoint(NoticeMonitorPointQuery query);

    List<NoticeMonitorPointDTO> listNoticeMonitorPoint(NoticeMonitorPointQuery query);

    List<NoticeMonitorPoint> listOfflinePoints(List<Long> ids);
}

serviceImpl业务层实现类方法

@Service
public class NoticeAlarmProcessRecordServiceImpl extends ServiceImpl<NoticeAlarmProcessRecordMapper, NoticeAlarmProcessRecord> implements NoticeAlarmProcessRecordService {
    @Override
    public List<NoticeAlarmProcessRecord> getProcessRecord(List<Long> recordIds){
        if (CollectionUtils.isEmpty(recordIds)) {
            return Collections.emptyList();
        }
        return lambdaQuery()
                .in(NoticeAlarmProcessRecord::getRecordId, recordIds)
                .orderByDesc(NoticeAlarmProcessRecord::getGmtCreate)
                .list();
    }

    @Override
    public List<NoticeAlarmProcessRecord> listProcessRecord(List<Long> recordIds){
        if (CollectionUtils.isEmpty(recordIds)) {
            return Collections.emptyList();
        }
        return lambdaQuery()
                .in(NoticeAlarmProcessRecord::getRecordId, recordIds)
                .orderByDesc(NoticeAlarmProcessRecord::getGmtCreate)
                .groupBy(NoticeAlarmProcessRecord::getRecordId)
                .list();
    }

    @Override
    public Map<Long, NoticeAlarmProcessRecord> mapProcessRecord(List<Long> recordIds){
        if(CollectionUtils.isEmpty(recordIds)){
            return Collections.EMPTY_MAP;
        }
        List<NoticeAlarmProcessRecord> list = listProcessRecord(recordIds);
        return list.stream().collect(Collectors.toMap(NoticeAlarmProcessRecord::getRecordId, Function.identity(), (key1, key2) -> key2));
    }
}

@Service
public class NoticeAlarmRecordServiceImpl extends ServiceImpl<NoticeAlarmRecordMapper, NoticeAlarmRecord> implements NoticeAlarmRecordService {
    @Autowired
    NoticeAlarmRecordMapper noticeAlarmRecordMapper;
    @Override
    public int countWarning(List<Long> pointIds, String startTime, String endTime) {
        if (CollectionUtils.isEmpty(pointIds)) {
            return 0;
        }
        return lambdaQuery().in(NoticeAlarmRecord::getPointId, pointIds)
                .between(NoticeAlarmRecord::getTriggerTime, startTime, endTime)
                .count();
    }

    @Override
    public List<NoticeAlarmRecord> countByItemCode(List<Long> pointIds,String startTime, String endTime,
                                                   int limit) {
        if (CollectionUtils.isEmpty(pointIds)) {
            return Collections.emptyList();
        }
        return query().select("point_id AS pointId", "count(*) AS itemCount", "item_code AS itemCode")
                .in("point_id", pointIds)
                .between("trigger_time", startTime, endTime)
                .groupBy("item_code")
                .orderByDesc("itemCount")
                .last("limit " + limit)
                .list();

    }

    @Override
    public List<NoticeAlarmRecord> countUndoByItemCode(List<Long> pointIds,String startTime,String endTime,
                                                       StatusTypeEnum status,
                                                       int limit) {
        if (CollectionUtils.isEmpty(pointIds)) {
            return Collections.emptyList();
        }
        return query().select("point_id AS pointId", "count(*) AS itemCount", "item_code AS itemCode")
                .in("point_id", pointIds)
                .eq(status != null, "status", status.getCode())
                .between("trigger_time", startTime, endTime)
                .groupBy("item_code")
                .orderByDesc("itemCount")
                .last("limit " + limit)
                .list();

    }

    @Override
    public List<NoticeAlarmRecord> getAlarmRecordList(List<Long> pointIds,String startTime,String endTime) {
        if (CollectionUtils.isEmpty(pointIds)) {
            return Collections.emptyList();
        }
        return query().select("id AS id", "point_id AS pointId", "trigger_time AS triggerTime", "alarm_level AS alarmLevel", "item_code AS itemCode", "alarm_desc AS alarmDesc", "status AS status")
                .in("point_id", pointIds)
                .between("trigger_time", startTime, endTime)
                .groupBy("point_id")
                .orderByDesc("trigger_time")
                .list();
    }

    @Override
    public int countTodoEvent(List<Long> pointIds, String startTime, String endTime) {
        if (CollectionUtils.isEmpty(pointIds)) {
            return 0;
        }
        return lambdaQuery().in(NoticeAlarmRecord::getPointId, pointIds)
                .eq(NoticeAlarmRecord::getStatus, StatusTypeEnum.TODO.getCode())
                .between(NoticeAlarmRecord::getTriggerTime, startTime, endTime)
                .count();
    }

    @Override
    public int countDoneEvent(List<Long> pointIds, String startTime, String endTime) {
        if (CollectionUtils.isEmpty(pointIds)) {
            return 0;
        }
        LambdaQueryWrapper<NoticeAlarmRecord> lambdaQuery = Wrappers.lambdaQuery(NoticeAlarmRecord.class);
        lambdaQuery.in(NoticeAlarmRecord::getPointId, pointIds);
        lambdaQuery.eq(NoticeAlarmRecord::getStatus, StatusTypeEnum.DONE.getCode());
        lambdaQuery.between(NoticeAlarmRecord::getTriggerTime, startTime, endTime);
        return count(lambdaQuery);
    }

    @Override
    public List<NoticeAlarmRecord> getAlarmRecord(List<Long> pointIds, String startTime, String endTime, String type) {
        QueryWrapper<NoticeAlarmRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("point_id", pointIds);
        queryWrapper.eq(StringUtils.isNotBlank(type), "item_code", type);
        queryWrapper.between("trigger_time", startTime, endTime);
        return noticeAlarmRecordMapper.selectList(queryWrapper);
    }

    @Override
    public List<WarnItemVO> countWarnItem(NoticeAlarmRecordQuery noticeAlarmRecordQuery) {
        return baseMapper.countWarnItem(noticeAlarmRecordQuery);
    }

    @Override
    public List<WarningSumVO> countWarnType(NoticeRecordQuery noticeRecordQuery) {
        return baseMapper.countWarnType(noticeRecordQuery);
    }

    @Override
    public List<EventVO> countEvent(NoticeRecordQuery noticeRecordQuery) {
        return baseMapper.countEvent(noticeRecordQuery);
    }

    @Override
    public IPage<NoticeAlarmRecord> listByPage(IPage<NoticeAlarmRecord> page, Wrapper<NoticeAlarmRecord> queryWrapper) {
        return page(page, queryWrapper);
    }
}

@Service
@Slf4j
public class NoticeMonitorObjectServiceImpl extends ServiceImpl<NoticeMonitorObjectMapper, NoticeMonitorObject> implements NoticeMonitorObjectService {
    @Value("${qxwz.monitor.warning.areaCode}")
    private String areaCode;
    @Override
    public List<NoticeMonitorObjectDTO> listNoticeMonitorPoint(NoticeMonitorPointQuery query) {
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = baseMapper.listNoticeMonitorPoint(query);
        log.info("查询监测点信息:"+noticeMonitorObjectDTOList);
        return noticeMonitorObjectDTOList;
    }

    @Override
    public NoticeMonitorObjectDTO getNoticeMonitorObject(NoticeMonitorObjectQuery query) {
        NoticeMonitorObjectDTO noticeMonitorObjectDTOList = baseMapper.getNoticeMonitorObject(query);
        return noticeMonitorObjectDTOList;
    }

    @Override
    public List<MonitorObjectVO> listNoticeMonitorObject(BaseWarningRequest baseWarningRequest) {
        String areaCode = StringUtils.defaultIfBlank(baseWarningRequest.getAreaCode(),this.areaCode);
        List<NoticeMonitorObject> list = lambdaQuery()
                .eq(NoticeMonitorObject::getAreaCode, areaCode)
                .list();
        if(CollectionUtils.isEmpty(list)){
            return Collections.emptyList();
        }
        List<MonitorObjectVO> monitorObjectVOS = list.stream().map(obj -> {
            MonitorObjectVO monitorObjectVO = new MonitorObjectVO();
            monitorObjectVO.setId(obj.getId());
            monitorObjectVO.setName(obj.getName());
            monitorObjectVO.setType(obj.getType());
            monitorObjectVO.setAreaDetail(obj.getAreaDetail());
            monitorObjectVO.setAltitude(obj.getAltitude());
            monitorObjectVO.setLatitude(obj.getLatitude());
            monitorObjectVO.setLongitude(obj.getLongitude());
            return monitorObjectVO;
        }).collect(Collectors.toList());
        return monitorObjectVOS;
    }

    @Override
    public List<NoticeInspectionDetailDTO> listNoticeInspectionDetail(BaseWarningRequest query){
        List<NoticeInspectionDetailDTO> noticeInspectionDetailDTOList = baseMapper.listNoticeInspectionDetail(query);
        return noticeInspectionDetailDTOList;
    }
}

@Service
@AllArgsConstructor
public class NoticeMonitorPointServiceImpl extends ServiceImpl<NoticeMonitorPointMapper, NoticeMonitorPoint> implements NoticeMonitorPointService {
    private final NoticeMonitorObjectService noticeMonitorObjectService;
    @Override
    public Map<Long, NoticeMonitorPointDTO> mapNoticeMonitorPoint(NoticeMonitorPointQuery query) {
        List<NoticeMonitorPointDTO> noticeMonitorPoints = listNoticeMonitorPoint(query);
        if(CollectionUtils.isEmpty(noticeMonitorPoints)){
            return Collections.EMPTY_MAP;
        }
        return noticeMonitorPoints.stream().collect(Collectors.toMap(NoticeMonitorPointDTO::getId, Function.identity(), (key1, key2) -> key2));
    }

    @Override
    public List<NoticeMonitorPointDTO> listNoticeMonitorPoint(NoticeMonitorPointQuery query) {
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = noticeMonitorObjectService.listNoticeMonitorPoint(query);
        if(CollectionUtils.isEmpty(noticeMonitorObjectDTOList)){
            return Collections.emptyList();
        }
        List<NoticeMonitorPointDTO> list = new ArrayList<>();
        for (NoticeMonitorObjectDTO noticeMonitorObjectDTO : noticeMonitorObjectDTOList) {
            List<NoticeMonitorPoint> noticeMonitorPoints = noticeMonitorObjectDTO.getNoticeMonitorPoints();
            if(CollectionUtils.isEmpty(noticeMonitorPoints)){
                continue;
            }
            for (NoticeMonitorPoint noticeMonitorPoint : noticeMonitorPoints) {
                NoticeMonitorObject noticeMonitorObject = new NoticeMonitorObjectDTO();
                BeanUtils.copyProperties(noticeMonitorObjectDTO,noticeMonitorObject);
                NoticeMonitorPointDTO point = new NoticeMonitorPointDTO();
                BeanUtils.copyProperties(noticeMonitorPoint,point);
                point.setNoticeMonitorObject(noticeMonitorObject);
                list.add(point);
            }
        }
        return list;
    }

    @Override
    public List<NoticeMonitorPoint> listOfflinePoints(List<Long> ids){
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return query().select("id AS id","name AS name","longitude as longitude","latitude as latitude")
                .in("id", ids)
                .list();
    }
}

Controller层代码

@RestController
@RequestMapping("/monitor")
@AllArgsConstructor
@Api(tags = "大屏监控接口")
public class NoticeMonitorObjectController {
    private final NoticeMonitorObjectService noticeMonitorObjectService;
    @ApiOperation(value = "20、获取指定区域内所有的监测对象",
            notes = "获取指定区域内所有的监测对象")
    @GetMapping("/list-notice-monitor-object")
    @ApiOperationSupport(order = 20)
    public Result<List<MonitorObjectVO>> listNoticeMonitorObject(@Valid BaseWarningRequest baseWarningRequest){
        List<MonitorObjectVO> list = noticeMonitorObjectService.listNoticeMonitorObject(baseWarningRequest);
        return Result.success( list);
    }
}

总的serviceImpl

@Service
@AllArgsConstructor
@Slf4j
public class MonitorServiceImpl implements IMonitorService {

    @Value("${qxwz.monitor.warning.areaCode}")
    private String areaCode;
    private final NoticeAlarmRecordService noticeAlarmRecordService;
    private final NoticeMonitorObjectService noticeMonitorObjectService;
    private final NoticeMonitorPointService noticeMonitorPointService;
    private final NoticeMonitorItemService noticeMonitorItemService;
    private final NoticeMonitorPointDeviceRefService noticeMonitorPointDeviceRefService;
    private final NoticeInspectionDetailService noticeInspectionDetailService;
    private final NoticeAlarmProcessRecordService noticeAlarmProcessRecordService;

    @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<>();
        }
    }

    /**
    * 返回监测点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<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;
    }


    private NoticeMonitorPointDTO getNoticeMonitorObjectDTO(NoticeAlarmRecord noticeAlarmRecord, Map<Long, NoticeMonitorPointDTO> mapNoticeMonitorPoint){
        NoticeMonitorPointDTO noticeMonitorPointDTO = new NoticeMonitorPointDTO();
        if(mapNoticeMonitorPoint == null){
            noticeMonitorPointDTO.setNoticeMonitorObject(new NoticeMonitorObjectDTO());
            return noticeMonitorPointDTO;
        }
        if(noticeAlarmRecord == null){
            noticeMonitorPointDTO.setNoticeMonitorObject(new NoticeMonitorObjectDTO());
            return noticeMonitorPointDTO;
        }

        return mapNoticeMonitorPoint.get(noticeAlarmRecord.getPointId());
    }


    @Override
    public List<AlarmLevelVO> queryDay7DoneAlarm(WarningRequest warningRequest){
        List<AlarmLevelVO> 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> pointIds = noticeMonitorObjectDTOList.stream().flatMap(obj -> obj.getNoticeMonitorPoints().stream().map(mp -> mp.getId())).collect(Collectors.toList());
        HashMap<String,String> map = new HashMap<>();
        int n = 0;
        while(n < 7){
            // map.put(DateUtil.getDateTime(localDate.plusDays(-n))[0],DateUtil.getDateTime(localDate.plusDays(-n))[1]);
            String[] dateTime = DateUtil.getDateTime(localDate, -n);
            map.put(dateTime[0], dateTime[0].substring(0,dateTime[0].length()-8)+"23:59:59");
            n++;
        }
        for(Map.Entry<String, String> entry : map.entrySet()){
            int redAlarmLevel = noticeAlarmRecordService.countDoneRedAlarm(pointIds, entry.getKey(), entry.getValue());
            int orangeAlarmLevel = noticeAlarmRecordService.countDoneOrangeAlarm(pointIds, entry.getKey(), entry.getValue());
            int yellowAlarmLevel = noticeAlarmRecordService.countDoneYellowAlarm(pointIds, entry.getKey(), entry.getValue());
            int blueAlarmLevel = noticeAlarmRecordService.countDoneBlueAlarm(pointIds, entry.getKey(), entry.getValue());
            AlarmLevelVO alarmLevel = AlarmLevelVO.builder().date(entry.getKey().substring(0,entry.getKey().length()-8)).redAlarmSum(Long.valueOf(redAlarmLevel))
                    .orangeAlarmSum(Long.valueOf(orangeAlarmLevel)).yellowAlarmSum(Long.valueOf(yellowAlarmLevel)).blueAlarmSum(Long.valueOf(blueAlarmLevel)).build();
            list.add(alarmLevel);
        }
        return list;
    }

    /**
     * 巡检统计
     */
    @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;
    }

    /**
     * 告警处理详情接口
     */
    @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;
    }

    /**
     * 查看相关监测项的告警列表接口
     */
    @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;
    }

    /**
     * 查询近7日监测点的预警详情接口
     */
    @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;
    }
}

总的controller

@RestController
@RequestMapping({"monitor",/*"test"*/})
@AllArgsConstructor
@Api(tags = "大屏监控接口")
public class MonitorController {
    private final IMonitorService monitorService;
    @ApiOperation(value = "1、统计给定日期地质灾害和山塘水库预警总数",
            notes = "统计给定日期地质灾害和山塘水库预警总数,如果日期不传,则返回当天日期的数据")
    @GetMapping("/day-warning")
    @ApiOperationSupport(order = 1)
    public Result<WarningVO> dayWarning(@Valid WarningRequest warningRequest){
        return Result.success(monitorService.queryDaywarning(warningRequest));
    }
    @ApiOperation(value = "2、统计给定日期以前7天地质灾害和山塘水库预警总数",
            notes = "统计给定日期以前7天地质灾害和山塘水库预警总数,如果日期不传则返回当前日期以前7天的数据,包含当天日期")
    @GetMapping("/day7-warning")
    @ApiOperationSupport(order = 2)
    public Result<List<WarningVO>> day7Warning(WarningRequest warningRequest){
        return Result.success(this.monitorService.queryDay7warning(warningRequest));
    }
    @ApiOperation(value = "3、统计当日预警事件总数接口",
            notes = "统计当日预警事件总数接口")
    @GetMapping("/day-event")
    @ApiOperationSupport(order = 3)
    public Result<EventVO> dayEvent(WarningRequest warningRequest){
        return Result.success(this.monitorService.queryDayevent(warningRequest));
    }
    @ApiOperation(value = "4、统计给定日期以前7天预警事件总数",
            notes = "统计给定日期以前7天未处理与已处理预警事件总数,如果日期不传则返回当前日期以前7天的数据,包含当天日期")
    @GetMapping("/day7-event")
    @ApiOperationSupport(order = 4)
    public Result<List<EventVO>> day7Event(WarningRequest warningRequest){
        return Result.success(this.monitorService.queryDay7event(warningRequest));
    }
    @ApiOperation(value = "5、统计未处理预警监测项类别前4类接口(当日)",
            notes = "统计当日未处理预警监测项数值排名前四监测项名称与数值,按降序排序输出(含地质灾害+山塘水库)")
    @GetMapping("/top4-undo-monitor-item")
    @ApiOperationSupport(order = 5)
    public Result<List<MonitorVO>> top4undoMonitorName(WarningRequest warningRequest){
        return Result.success(this.monitorService.queryTop4undomonitor(warningRequest));
    }
}

具体使用:https://blog.csdn.net/zdsg45/article/details/105138493

笔记代码链接

https://gitee.com/kuangstudy/kuang_livenote/tree/master/%E3%80%90%E9%81%87%E8%A7%81%E7%8B%82%E7%A5%9E%E8%AF%B4%E3%80%91MyBatisPlus%E8%A7%86%E9%A2%91%E7%AC%94%E8%AE%B0

官网

https://www.mybatis-plus.com/guide/

Elastic search

Elaticsearch,简称为es,es是一个开源的高扩展分布式全文检索引擎,它可以近乎实时的存储检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别(大数据时代)的数据。es也使用java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单

据国际权威的数据库产品评测机构DB Engines的统计,在2016年1月,ElasticSearch已超过Solr等,成为排名第一的搜索引擎类应用

狂神笔记:https://www.kuangstudy.com/bbs/1354069127022583809

SpringBoot+ElasticSearch 实现全文检索:https://blog.csdn.net/u014553029/article/details/110506316

NativeSearchQueryBuilder过滤聚合高亮查询:https://blog.csdn.net/qq_40885085/article/details/105024625

使用方式:

一、导入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

二、添加配置信息

spring:
  data:
    elasticsearch:
      repositories:
        enabled: true

三、添加实体

@Data
@Document(indexName = "trms")
@Table(name = "report")
public class Report implements Serializable {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private Long cid0;
    @Field(type = FieldType.Keyword)
    private Long cid;
    @Field(type = FieldType.Keyword)
    private String uid;
    private Integer access;
    private String title;
    private String description;
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    private Date createTime;
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    private Date lastUpdateTime;
    private String version;
    private String size;
    private String path;
    private Integer versionLock;
    private Boolean delFlag;
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private transient String all;
}

四、添加对应的Repository

@Repository
public interface ReportRepository extends ElasticsearchRepository<Report, Long> {

}

五、业务代码编写

@Data
@AllArgsConstructor
@NoArgsConstructor
public class SearchResult {

    private List<Report> items;

    private Map<String, Object> filterMap;
}

@Service
@Transactional(rollbackFor=Exception.class)
public class ReportServiceImpl implements ReportService {
    @Autowired
    ReportMapper reportMapper;
    @Autowired
    private ReportRepository reportRepository;
    @Autowired
    private SearchOperations operations;
    @Autowired
    private CategoryService categoryService;
    @Override
    public SearchResult queryAccessReportList(String staffName) {
        List<Report> reports = this.reportAccessMapper.queryAccessReportByStaffName(staffName);
        if (reports.isEmpty()) {
            return null;
        }
        reports.sort((r1, r2) -> {
            Date t1 = r1.getCreateTime();
            Date t2 = r2.getCreateTime();
            return t2.compareTo(t1);
        });
        return this.handleCategoryAndAuthorAgg(reports);
    }

    @Override
    public SearchResult queryReportListByKeyword(String keyword, List<Long> rids) {
        if (keyword.isEmpty() || rids == null) {
            return null;
        }
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withQuery(QueryBuilders.boolQuery()
                .must(QueryBuilders.termsQuery("id", rids))
                .must(QueryBuilders.matchQuery("all", keyword).analyzer("ik_max_word")));
        return this.handleCategoryAndAuthorAgg(builder);
    }

    public SearchResult handleCategoryAndAuthorAgg(List<Report> reports) {
        Set<Long> cids = reports.stream().map(Report::getCid).collect(Collectors.toSet());
        Set<Long> cid0s = reports.stream().map(Report::getCid0).collect(Collectors.toSet());
        Set<String> uids = reports.stream().map(Report::getUid).collect(Collectors.toSet());
        List<Category> parentList = this.categoryService.queryCategoryListByIds(new ArrayList<>(cid0s));
        List<Category> children = this.categoryService.queryCategoryListByIds(new ArrayList<>(cids));

        SearchResult searchResult = new SearchResult();
        searchResult.setItems(reports);
        Map<String, Object> filterMap = new HashMap<>();
        filterMap.put("parentList", parentList);
        filterMap.put("children", children);
        filterMap.put("authorList", uids);
        searchResult.setFilterMap(filterMap);
        return searchResult;
    }

    private SearchResult handleCategoryAndAuthorAgg(NativeSearchQueryBuilder builder) {
        String categoryAggName = "categoryAgg";
        String authorAggName = "authorAgg";
        builder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid"));
        builder.addAggregation(AggregationBuilders.terms(authorAggName).field("uid"));
        Query query = builder.build();
        SearchHits<Report> searchHits = operations.search(query, Report.class);
        List<Report> reports = new ArrayList<>();
        searchHits.getSearchHits().forEach(item -> reports.add(item.getContent()));

        Aggregations aggregations = searchHits.getAggregations();
        ParsedStringTerms terms = Objects.requireNonNull(aggregations).get(categoryAggName);

        List<? extends Terms.Bucket> cBuckets = terms.getBuckets();
        List<Long> idList = cBuckets.stream().map(item -> item.getKeyAsNumber().longValue()).collect(Collectors.toList());
        List<Category> categories = this.categoryService.queryCategoryListByIds(idList);
        Set<Long> parentIds = categories.stream().map(Category::getParentId).collect(Collectors.toSet());
        List<Category> parents = this.categoryService.queryCategoryListByIds(new ArrayList<>(parentIds));
        Map<String, Object> filterMap = new HashMap<>();
        filterMap.put("parentList", parents);
        filterMap.put("children", categories);

        ParsedStringTerms userTerms = Objects.requireNonNull(aggregations).get(authorAggName);
        List<? extends Terms.Bucket> uBuckets = userTerms.getBuckets();
        List<String> uidList = uBuckets.stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList());
        filterMap.put("authorList", uidList);
        SearchResult searchResult = new SearchResult();
        searchResult.setItems(reports);
        searchResult.setFilterMap(filterMap);
        return searchResult;
    }
}

单元测试

一、导入依赖

<!-- 单测 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.2.RELEASE</version>
    <scope>test</scope>
</dependency>
<!-- mockito -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.0</version>
    <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito2 -->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.0</version>
    <scope>test</scope>
</dependency>

二、编写测试父类

//test.java.com.xwz.monitor包下
@RunWith(MockitoJUnitRunner.Silent.class)
public abstract class BaseMockitoTest {

}

三、编写相关单元测试类

//test.java.com.xwz.monitor.service包下
public class MonitorServiceTest extends BaseMockitoTest {
    @InjectMocks
    private MonitorServiceImpl monitorService;
    @Mock
    private BaseMapper baseMapper;
    @Mock
    private NoticeMonitorObjectService noticeMonitorObjectService;
    @Mock
    private NoticeAlarmRecordService noticeAlarmRecordService;
    @Mock
    private NoticeMonitorPointService noticeMonitorPointService;
    @Mock
    private NoticeMonitorItemService noticeMonitorItemService;
    @Mock
    private NoticeMonitorPointDeviceRefService noticeMonitorPointDeviceRefService;
    @Mock
    private NoticeAlarmProcessRecordService noticeAlarmProcessRecordService;
    @Mock
    private NoticeInspectionDetailService noticeInspectionDetailService;

    @Before
    public void setUp() {
        //MockitoAnnotations.initMocks(this);
        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), NoticeAlarmRecord.class);
        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), NoticeAlarmProcessRecord.class);
    }

    @Test
    public void queryDaywarning() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        WarningVO warningVO = monitorService.queryDaywarning(warningRequest);
        Assertions.assertEquals(warningVO.getDzzhSum(), 0);
        Assertions.assertEquals(warningVO.getStskSum(), 0);
        when(noticeAlarmRecordService.countWarning(any(), any(), any())).thenReturn(10);

        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        WarningVO warningVO1 = monitorService.queryDaywarning(warningRequest);
        Assertions.assertEquals(warningVO1.getDzzhSum(), 10);
    }


    @Test
    public void queryDay7warning() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<WarningVO> list = monitorService.queryDay7warning(warningRequest);
        int n = 0;
        List<WarningVO> list1 = new ArrayList<>();
        LocalDate localDate = DateTimeUtil.parseDate(warningRequest.getDate());
        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);
            list1.add(warning);
            n++;
        }
        Assertions.assertEquals(list, list1);
        List<WarningSumVO> warningSumVOList = new ArrayList<>();
        WarningSumVO warning = new WarningSumVO();
        warning.setDate("2022-01-26");
        warning.setWarningSum(11L);
        warningSumVOList.add(warning);
        when(noticeAlarmRecordService.countWarnType(any(NoticeRecordQuery.class))).thenReturn(warningSumVOList);
        List<WarningVO> warningVOList = monitorService.queryDay7warning(warningRequest);
        Assertions.assertNotEquals(warningVOList, Collections.emptyList());
    }

    @Test
    public void queryDayevent() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        EventVO event = monitorService.queryDayevent(warningRequest);
        Assertions.assertEquals(event.getDoneEventSum(), 0);
        Assertions.assertEquals(event.getTodoEventSum(), 0);
        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        when(noticeAlarmRecordService.countTodoEvent(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countDoneEvent(any(), any(), any())).thenReturn(10);
        EventVO event1 = monitorService.queryDayevent(warningRequest);
        Assertions.assertEquals(event1.getTodoEventSum(), 10);
    }

    @Test
    public void queryDay7event() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<EventVO> list = monitorService.queryDay7event(warningRequest);
        Assertions.assertEquals(list, Collections.emptyList());
        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<EventVO> list1 = new ArrayList<>();
        EventVO event = new EventVO();
        event.setTodoEventSum(10L);
        event.setDoneEventSum(10L);
        event.setDate("2022-03-22");
        when(noticeAlarmRecordService.countEvent(any(NoticeRecordQuery.class))).thenReturn(list1);
        list1 = monitorService.queryDay7event(warningRequest);
        Assertions.assertEquals(list1, Collections.emptyList());
    }

    @Test
    public void queryAlarmlevel() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        AlarmLevelVO alarmLevelVO = monitorService.queryAlarmlevel(warningRequest);
        AlarmLevelVO alarmLevel = AlarmLevelVO.builder().date(warningRequest.getDate()).redAlarmSum(0L).orangeAlarmSum(0L).yellowAlarmSum(0L).blueAlarmSum(0L).build();
        Assertions.assertEquals(alarmLevelVO, alarmLevel);

        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        when(noticeAlarmRecordService.countRedAlarmLevel(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countOrangeAlarmLevel(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countYellowAlarmLevel(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countBlueAlarmLevel(any(), any(), any())).thenReturn(10);
        AlarmLevelVO alarmLevel1 = monitorService.queryAlarmlevel(warningRequest);
        Assertions.assertEquals(alarmLevel1.getRedAlarmSum(), 10);
    }

    @Test
    public void queryDoneAlarm() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        AlarmLevelVO alarmLevelVO = monitorService.queryDoneAlarm(warningRequest);
        AlarmLevelVO alarmLevel = AlarmLevelVO.builder().date(warningRequest.getDate()).redAlarmSum(0L).orangeAlarmSum(0L).yellowAlarmSum(0L).blueAlarmSum(0L).build();
        Assertions.assertEquals(alarmLevelVO, alarmLevel);

        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        when(noticeAlarmRecordService.countDoneRedAlarm(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countDoneOrangeAlarm(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countDoneYellowAlarm(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countDoneBlueAlarm(any(), any(), any())).thenReturn(10);
        AlarmLevelVO alarmLevel1 = monitorService.queryDoneAlarm(warningRequest);
        Assertions.assertEquals(alarmLevel1.getRedAlarmSum(), 10);
    }

    @Test
    public void queryMonitorObject() {
        MonitorItemObjectRequest monitorObjectRequest = new MonitorItemObjectRequest();
        monitorObjectRequest.setAreaCode("331123");
        monitorObjectRequest.setObjectId(10L);
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        MonitorObjectInfoVO monitorObjectInfoVO = monitorService.queryMonitorObject(monitorObjectRequest);
        Assertions.assertEquals(monitorObjectInfoVO, null);

        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        when(noticeMonitorObjectService.getNoticeMonitorObject(any(NoticeMonitorObjectQuery.class))).thenReturn(noticeMonitorObjectDTO);
        when(noticeMonitorPointDeviceRefService.countOfflineDevice(any())).thenReturn(10);
        when(noticeMonitorPointDeviceRefService.countOnlineDevice(any())).thenReturn(10);
        MonitorObjectInfoVO monitorObjectInfoVO1 = monitorService.queryMonitorObject(monitorObjectRequest);
        Assertions.assertNotEquals(monitorObjectInfoVO1, Collections.emptyList());
    }

    @Test
    public void queryDay7Alarmlevel() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<AlarmLevelVO> alarmLevelVOList = monitorService.queryDay7Alarmlevel(warningRequest);
        Assertions.assertEquals(alarmLevelVOList.get(0).getRedAlarmSum(), 0L);

        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        when(noticeAlarmRecordService.countRedAlarmLevel(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countOrangeAlarmLevel(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countYellowAlarmLevel(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countBlueAlarmLevel(any(), any(), any())).thenReturn(10);
        List<AlarmLevelVO> alarmLevelVOList1 = monitorService.queryDay7Alarmlevel(warningRequest);
        Assertions.assertNotEquals(alarmLevelVOList1, Collections.emptyList());
    }

    @Test
    public void queryDay7DoneAlarm() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<AlarmLevelVO> alarmLevelVOList = monitorService.queryDay7DoneAlarm(warningRequest);
        Assertions.assertEquals(alarmLevelVOList.get(0).getRedAlarmSum(), 0L);

        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        when(noticeAlarmRecordService.countDoneRedAlarm(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countDoneOrangeAlarm(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countDoneYellowAlarm(any(), any(), any())).thenReturn(10);
        when(noticeAlarmRecordService.countDoneBlueAlarm(any(), any(), any())).thenReturn(10);
        List<AlarmLevelVO> alarmLevelVOList1 = monitorService.queryDay7DoneAlarm(warningRequest);
        Assertions.assertNotEquals(alarmLevelVOList1, Collections.emptyList());
    }

    @Test
    public void queryInspectionStatistics() {
        BaseWarningRequest baseWarningRequest = new BaseWarningRequest();
        baseWarningRequest.setAreaCode("11");
        List<NoticeInspectionDetailDTO> noticeInspectionDetailDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeInspectionDetail(baseWarningRequest)).thenReturn(noticeInspectionDetailDTOList);
        List<MonitorInspectionStatisticsVO> responseList= new ArrayList<>();
        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());
        List<MonitorInspectionStatisticsVO> monitorInspectionStatisticsVOList = monitorService.queryInspectionStatistics(baseWarningRequest);
        Assertions.assertEquals(monitorInspectionStatisticsVOList, responseList);

        NoticeInspectionDetailDTO noticeInspectionDetailDTO = new NoticeInspectionDetailDTO();
        List<NoticeInspectionDetail> noticeInspectionDetails = new ArrayList<>();
        NoticeInspectionDetail noticeInspectionDetail = new NoticeInspectionDetail();
        noticeInspectionDetail.setObjectId(11L);
        noticeInspectionDetails.add(noticeInspectionDetail);
        noticeInspectionDetailDTO.setNoticeInspectionDetails(noticeInspectionDetails);
        noticeInspectionDetailDTOList.add(noticeInspectionDetailDTO);
        when(noticeMonitorObjectService.listNoticeInspectionDetail(baseWarningRequest)).thenReturn(noticeInspectionDetailDTOList);
        when(noticeInspectionDetailService.countAll(any(), any(), any())).thenReturn(10);
        when(noticeInspectionDetailService.countError(any(), any(), any())).thenReturn(5);
        List<MonitorInspectionStatisticsVO> responseList1 = monitorService.queryInspectionStatistics(baseWarningRequest);
        Assertions.assertNotEquals(responseList1, Collections.emptyList());
    }

    @Test
    public void queryOfflineDevice() {
        BaseWarningRequest baseWarningRequest = new BaseWarningRequest();
        baseWarningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<OfflineDeviceVO> offlineDeviceVOList = monitorService.queryOfflineDevice(baseWarningRequest);
        Assertions.assertEquals(offlineDeviceVOList, Collections.emptyList());

        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<NoticeMonitorPointDeviceRef> noticeMonitorPointDeviceRefs = new ArrayList<>();
        NoticeMonitorPointDeviceRef noticeMonitorPointDeviceRef = new NoticeMonitorPointDeviceRef();
        noticeMonitorPointDeviceRef.setPointId(11L);
        noticeMonitorPointDeviceRefs.add(noticeMonitorPointDeviceRef);
        when(noticeMonitorPointDeviceRefService.countOfflineDeviceIds(any())).thenReturn(noticeMonitorPointDeviceRefs);
        when(noticeMonitorPointService.listOfflinePoints(any())).thenReturn(noticeMonitorPointList);
        List<OfflineDeviceVO> offlineDeviceVOList1 = monitorService.queryOfflineDevice(baseWarningRequest);
        Assertions.assertNotEquals(offlineDeviceVOList1, Collections.emptyList());
    }

    @Test
    public void queryAlarmPoint() {
        WarningRequest warningRequest = new WarningRequest();
        warningRequest.setDate("2022-03-22");
        warningRequest.setAreaCode("11");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<AlarmPointInfoVO> alarmPointInfoVOList = monitorService.queryAlarmPoint(warningRequest);
        Assertions.assertEquals(alarmPointInfoVOList, Collections.emptyList());

        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(10L);
        List<NoticeMonitorPoint> noticeMonitorPointList = new ArrayList<>();
        noticeMonitorPointList.add(noticeMonitorPoint);
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPointList);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<NoticeAlarmRecord> records = new ArrayList<>();
        NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
        noticeAlarmRecord.setId(11L);
        noticeAlarmRecord.setPointId(11L);
        records.add(noticeAlarmRecord);
        when(noticeAlarmRecordService.getAlarmRecordList(any(), any(), any())).thenReturn(records);
        Map<Long, NoticeAlarmProcessRecord> noticeAlarmProcessRecordMap = new HashMap<>();
        NoticeAlarmProcessRecord noticeAlarmProcessRecord = new NoticeAlarmProcessRecord();
        noticeAlarmProcessRecord.setFalsePositive(true);
        noticeAlarmProcessRecordMap.put(11L,noticeAlarmProcessRecord);
        when(noticeAlarmProcessRecordService.mapProcessRecord(any())).thenReturn(noticeAlarmProcessRecordMap);
        Map<Long, NoticeMonitorPointDTO> noticeMonitorPointDTOMap = new HashMap<>();
        NoticeMonitorPointDTO noticeMonitorPointDTO = new NoticeMonitorPointDTO();
        noticeMonitorPointDTO.setName("xiaoliu");
        noticeMonitorPointDTOMap.put(11L,noticeMonitorPointDTO);
        when(noticeMonitorPointService.mapNoticeMonitorPoint(any())).thenReturn(noticeMonitorPointDTOMap);
        NoticeMonitorItem noticeMonitorItem = new NoticeMonitorItem();
        when(noticeMonitorItemService.getOne(any(Wrapper.class))).thenReturn(noticeMonitorItem);
        List<AlarmPointInfoVO> alarmPointInfoVOList1 = monitorService.queryAlarmPoint(warningRequest);
        Assertions.assertNotEquals(alarmPointInfoVOList1, Collections.emptyList());
    }

    @Test
    public void queryMonitorItemWarnRecord() {
        MonitorItemRequest monitorItemRequest = new MonitorItemRequest();
        PageRequest pageRequest = new PageRequest();
        monitorItemRequest.setDate("2022-03-22");
        monitorItemRequest.setAreaCode("11");
        monitorItemRequest.setType("YL");
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<MonitorItemVO> monitorItemVOList = monitorService.queryMonitorItemWarnRecord(monitorItemRequest, pageRequest);
        Assertions.assertEquals(monitorItemVOList.size(), 0);

        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        List<NoticeMonitorPoint> noticeMonitorPoints = new ArrayList<>();
        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(1L);
        noticeMonitorPoints.add(noticeMonitorPoint);
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPoints);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        IPage<NoticeAlarmRecord> page = new Page<>();
        List<NoticeAlarmRecord> noticeAlarmRecords = new ArrayList<>();
        NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
        noticeAlarmRecord.setPointId(11L);
        NoticeAlarmRecord noticeAlarmRecord1 = new NoticeAlarmRecord();
        noticeAlarmRecord1.setPointId(22L);
        noticeAlarmRecords.add(noticeAlarmRecord);
        noticeAlarmRecords.add(noticeAlarmRecord1);
        page.setRecords(noticeAlarmRecords);

        // when(baseMapper.selectPage(any(Page.class), any(Wrapper.class))).thenReturn(page);

        when(noticeAlarmRecordService.listByPage(any(IPage.class), any(LambdaQueryWrapper.class))).thenReturn(page);
        Map<Long, NoticeMonitorPointDTO> noticeMonitorPointDTOMap = new HashMap<>();
        noticeMonitorPointDTOMap.put(11L, new NoticeMonitorPointDTO());
        NoticeMonitorPointDTO noticeMonitorPointDTO = new NoticeMonitorPointDTO();
        NoticeMonitorObject noticeMonitorObject = new NoticeMonitorObjectDTO();
        noticeMonitorPointDTO.setNoticeMonitorObject(noticeMonitorObject);
        noticeMonitorPointDTOMap.put(22L, noticeMonitorPointDTO);
        when(noticeMonitorPointService.mapNoticeMonitorPoint(any())).thenReturn(noticeMonitorPointDTOMap);
        NoticeMonitorItem noticeMonitorItem = new NoticeMonitorItem();
        when(noticeMonitorItemService.getOne(any(Wrapper.class))).thenReturn(noticeMonitorItem);
        List<MonitorItemVO> monitorItemVOS = monitorService.queryMonitorItemWarnRecord(monitorItemRequest, pageRequest);
        Assertions.assertEquals(monitorItemVOS.size(), 2);

        page.setRecords(null);
        when(noticeAlarmRecordService.listByPage(any(IPage.class), any(LambdaQueryWrapper.class))).thenReturn(page);
        try{
            List<MonitorItemVO> monitorItemVOS1 = monitorService.queryMonitorItemWarnRecord(monitorItemRequest, pageRequest);
            Assertions.assertEquals(monitorItemVOS1.size(), 0);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
    }

    @Test
    public void queryAlarmInfo() {
        MonitorItemObjectRequest monitorItemRequest = new MonitorItemObjectRequest();
        monitorItemRequest.setObjectId(6L);
        PageRequest pageRequest = new PageRequest();
        Assertions.assertEquals(monitorService.queryAlarmInfo(monitorItemRequest, pageRequest).size(), 0);
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        List<NoticeMonitorPoint> noticeMonitorPoints = new ArrayList<>();
        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(22L);
        noticeMonitorPoints.add(noticeMonitorPoint);
        noticeMonitorObjectDTO.setId(6L);
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPoints);
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any())).thenReturn(noticeMonitorObjectDTOList);

        Map<Long, NoticeMonitorPointDTO> mapNoticeMonitorPoint = new HashMap<>();
        NoticeMonitorPointDTO noticeMonitorPointDTO = new NoticeMonitorPointDTO();
        noticeMonitorPointDTO.setNoticeMonitorObject(noticeMonitorObjectDTO);

        mapNoticeMonitorPoint.put(22L, noticeMonitorPointDTO);
        when(noticeMonitorPointService.mapNoticeMonitorPoint(any())).thenReturn(mapNoticeMonitorPoint);


        // Page page = noticeAlarmRecordService.page(pageRequest.convertPage(), noticeAlarmRecordLambdaQueryWrapper);
        List<NoticeAlarmRecord> noticeAlarmRecords = new ArrayList<>();
        NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
        noticeAlarmRecord.setId(33L);
        noticeAlarmRecord.setPointId(22L);
        noticeAlarmRecords.add(noticeAlarmRecord);
        Page page = new Page();
        page.setRecords(noticeAlarmRecords);
        when(noticeAlarmRecordService.page(any(Page.class), any(Wrapper.class))).thenReturn(page);

        // List<NoticeAlarmProcessRecord> processRecordList = noticeAlarmProcessRecordService.getProcessRecord(recordIdList);
        List<NoticeAlarmProcessRecord> processRecordList = new ArrayList<>();
        NoticeAlarmProcessRecord noticeAlarmProcessRecord = new NoticeAlarmProcessRecord();
        noticeAlarmProcessRecord.setId(44L);
        noticeAlarmProcessRecord.setRecordId(33L);
        processRecordList.add(noticeAlarmProcessRecord);
        when(noticeAlarmProcessRecordService.getProcessRecord(any())).thenReturn(processRecordList);
        NoticeMonitorItem noticeMonitorItem = new NoticeMonitorItem();
        when(noticeMonitorItemService.getOne(any(Wrapper.class))).thenReturn(noticeMonitorItem);
        List<AlarmInfoVO> alarmInfoVOS = monitorService.queryAlarmInfo(monitorItemRequest, pageRequest);
        Assertions.assertEquals(alarmInfoVOS.size(), 1);

    }


    @Test
    public void queryAlarmDetail() {
        AlarmDetailRequest alarmDetailRequest = new AlarmDetailRequest();
        when(noticeAlarmRecordService.getById(any())).thenReturn(null);
        monitorService.queryAlarmDetail(alarmDetailRequest);

        NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
        when(noticeAlarmRecordService.getById(any())).thenReturn(noticeAlarmRecord);

        NoticeAlarmProcessRecord noticeAlarmProcessRecord = new NoticeAlarmProcessRecord();
        when(noticeAlarmProcessRecordService.getOne(any(Wrapper.class))).thenReturn(noticeAlarmProcessRecord);

        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        when(noticeMonitorPointService.getById(any())).thenReturn(noticeMonitorPoint);
        NoticeMonitorItem noticeMonitorItem = new NoticeMonitorItem();
        when(noticeMonitorItemService.getOne(any(Wrapper.class))).thenReturn(noticeMonitorItem);
        AlarmDetailVO alarmDetailVO = monitorService.queryAlarmDetail(alarmDetailRequest);
        Assertions.assertTrue(alarmDetailVO != null);

    }

    @Test
    public void queryDay7AlarmEventCountList(){
        MonitorItemRequest monitorItemRequest = new MonitorItemRequest();
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(new ArrayList<>());
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        Assertions.assertEquals(monitorService.queryDay7AlarmEventCountList(monitorItemRequest).size(),0);
        List<NoticeMonitorPoint> noticeMonitorPoints = new ArrayList<>();
        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(11L);
        noticeMonitorPoints.add(noticeMonitorPoint);
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPoints);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        // List<WarnItemVO> list = noticeAlarmRecordService.countWarnItem(noticeAlarmRecordQuery);
        List<WarnItemVO> list = new ArrayList<>();
        WarnItemVO warnItemVO = new WarnItemVO();
        warnItemVO.setTotalCount(100);
        warnItemVO.setTodoCount(50);
        warnItemVO.setDoneCount(50);
        list.add(warnItemVO);
        when(noticeAlarmRecordService.countWarnItem(any())).thenReturn(list);
        Assertions.assertEquals(monitorService.queryDay7AlarmEventCountList(monitorItemRequest).size(), 1);
        WarnItemVO warnItem = monitorService.queryDay7AlarmEventCountList(monitorItemRequest).get(0);

        Assertions.assertEquals(warnItem.getDoneCount()+warnItem.getTodoCount(), warnItem.getTotalCount());

    }

    @Test
    public void queryDay7AlarmDetail(){
        NoticeAlarmRecordStatusRequest noticeAlarmRecordStatusRequest = new NoticeAlarmRecordStatusRequest();
        List<NoticeMonitorObjectDTO> noticeMonitorObjectDTOList = new ArrayList<>();
        NoticeMonitorObjectDTO noticeMonitorObjectDTO = new NoticeMonitorObjectDTO();
        noticeMonitorObjectDTO.setNoticeMonitorPoints(new ArrayList<>());
        noticeMonitorObjectDTOList.add(noticeMonitorObjectDTO);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        Assertions.assertEquals(monitorService.queryDay7AlarmDetail(noticeAlarmRecordStatusRequest).size(),0);
        List<NoticeMonitorPoint> noticeMonitorPoints = new ArrayList<>();
        NoticeMonitorPoint noticeMonitorPoint = new NoticeMonitorPoint();
        noticeMonitorPoint.setId(11L);
        noticeMonitorPoints.add(noticeMonitorPoint);
        noticeMonitorObjectDTO.setNoticeMonitorPoints(noticeMonitorPoints);
        when(noticeMonitorObjectService.listNoticeMonitorPoint(any(NoticeMonitorPointQuery.class))).thenReturn(noticeMonitorObjectDTOList);
        List<NoticeAlarmRecord> list = new ArrayList<>();
        NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
        noticeAlarmRecord.setPointId(22L);
        list.add(noticeAlarmRecord);
        when(noticeAlarmRecordService.list(any(Wrapper.class))).thenReturn(list);

        Map<Long, NoticeMonitorPointDTO> noticeMonitorPointDTOMap = new HashMap<>();
        noticeMonitorPointDTOMap.put(11L, new NoticeMonitorPointDTO());
        NoticeMonitorPointDTO noticeMonitorPointDTO = new NoticeMonitorPointDTO();
        NoticeMonitorItem noticeMonitorItem = new NoticeMonitorItem();
        when(noticeMonitorItemService.getOne(any(Wrapper.class))).thenReturn(noticeMonitorItem);
        NoticeMonitorObject noticeMonitorObject = new NoticeMonitorObjectDTO();
        noticeMonitorPointDTO.setNoticeMonitorObject(noticeMonitorObject);
        noticeMonitorPointDTOMap.put(22L, noticeMonitorPointDTO);
        when(noticeMonitorPointService.mapNoticeMonitorPoint(any())).thenReturn(noticeMonitorPointDTOMap);
        List<MonitorItemVO> monitorItemVOS = monitorService.queryDay7AlarmDetail(noticeAlarmRecordStatusRequest);
        Assertions.assertEquals(monitorItemVOS.size(),1);
    }
}

public class NoticeAlarmRecordServiceTest  extends BaseMockitoTest {
    @InjectMocks
    private NoticeAlarmRecordServiceImpl noticeAlarmRecordService;
    @Mock
    private BaseMapper baseMapper;
    @Test
    public void countWarning(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countWarning(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countWarning(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countByItemCode(){
        List<NoticeAlarmRecord> noticeAlarmRecordList = new ArrayList<>();
        NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
        noticeAlarmRecord.setId(11L);
        noticeAlarmRecordList.add(noticeAlarmRecord);
        when(baseMapper.selectList(any(Wrapper.class))).thenReturn(noticeAlarmRecordList);
        List<NoticeAlarmRecord> noticeAlarmRecordList1 = noticeAlarmRecordService.countByItemCode(new ArrayList<>(),"2022-03-01","2022-03-22",4);
        Assertions.assertTrue(noticeAlarmRecordList1.size() == 0);
        List<NoticeAlarmRecord> noticeAlarmRecordList2 = noticeAlarmRecordService.countByItemCode(Arrays.asList(11L),"2022-03-01","2022-03-22",4);
        Assertions.assertEquals(noticeAlarmRecordList2.get(0).getId(), 11L);
    }

    @Test
    public void countUndoByItemCode(){
        List<NoticeAlarmRecord> noticeAlarmRecordList = new ArrayList<>();
        NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
        noticeAlarmRecord.setId(11L);
        noticeAlarmRecordList.add(noticeAlarmRecord);
        when(baseMapper.selectList(any(Wrapper.class))).thenReturn(noticeAlarmRecordList);
        List<NoticeAlarmRecord> noticeAlarmRecordList1 = noticeAlarmRecordService.countUndoByItemCode(new ArrayList<>(),"2022-03-01","2022-03-22", StatusTypeEnum.TODO,4);
        Assertions.assertTrue(noticeAlarmRecordList1.size() == 0);
        List<NoticeAlarmRecord> noticeAlarmRecordList2 = noticeAlarmRecordService.countUndoByItemCode(Arrays.asList(11L),"2022-03-01","2022-03-22",StatusTypeEnum.TODO,4);
        Assertions.assertEquals(noticeAlarmRecordList2.get(0).getId(), 11L);
    }

    @Test
    public void getAlarmRecordList(){
        List<NoticeAlarmRecord> noticeAlarmRecordList = new ArrayList<>();
        NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
        noticeAlarmRecord.setId(11L);
        noticeAlarmRecordList.add(noticeAlarmRecord);
        when(baseMapper.selectList(any(Wrapper.class))).thenReturn(noticeAlarmRecordList);
        List<NoticeAlarmRecord> noticeAlarmRecordList1 = noticeAlarmRecordService.getAlarmRecordList(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(noticeAlarmRecordList1.size() == 0);
        List<NoticeAlarmRecord> noticeAlarmRecordList2 = noticeAlarmRecordService.getAlarmRecordList(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(noticeAlarmRecordList2.get(0).getId(), 11L);
    }

    @Test
    public void countTodoEvent(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countTodoEvent(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countTodoEvent(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countDoneEvent(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countDoneEvent(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countDoneEvent(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void getAlarmRecord(){
        try {
            List<NoticeAlarmRecord> noticeAlarmRecordList = new ArrayList<>();
            NoticeAlarmRecord noticeAlarmRecord = new NoticeAlarmRecord();
            noticeAlarmRecord.setId(11L);
            noticeAlarmRecordList.add(noticeAlarmRecord);
            when(baseMapper.selectList(any(Wrapper.class))).thenReturn(noticeAlarmRecordList);
            List<NoticeAlarmRecord> noticeAlarmRecordList1 = noticeAlarmRecordService.getAlarmRecord(new ArrayList<>(),"2022-03-01","2022-03-22","YL");
            Assertions.assertTrue(noticeAlarmRecordList1.size() == 0);
            List<NoticeAlarmRecord> noticeAlarmRecordList2 = noticeAlarmRecordService.getAlarmRecord(Arrays.asList(11L),"2022-03-01","2022-03-22","YL");
            Assertions.assertEquals(noticeAlarmRecordList2.get(0).getId(), 11L);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
    }

    @Test
    public void countRedAlarmLevel(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countRedAlarmLevel(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countRedAlarmLevel(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countOrangeAlarmLevel(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countOrangeAlarmLevel(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countOrangeAlarmLevel(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countYellowAlarmLevel(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countYellowAlarmLevel(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countYellowAlarmLevel(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countBlueAlarmLevel(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countBlueAlarmLevel(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countBlueAlarmLevel(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countDoneRedAlarm(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countDoneRedAlarm(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countDoneRedAlarm(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countDoneOrangeAlarm(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countDoneOrangeAlarm(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countDoneOrangeAlarm(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countDoneYellowAlarm(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countDoneYellowAlarm(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countDoneYellowAlarm(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }

    @Test
    public void countDoneBlueAlarm(){
        int count = 1;
        when(baseMapper.selectCount(any(Wrapper.class))).thenReturn(count);
        int count1 = noticeAlarmRecordService.countDoneBlueAlarm(new ArrayList<>(),"2022-03-01","2022-03-22");
        Assertions.assertTrue(count1 == 0);
        int count2 = noticeAlarmRecordService.countDoneBlueAlarm(Arrays.asList(11L),"2022-03-01","2022-03-22");
        Assertions.assertEquals(count2, 1);
    }
}

Dubbo

Apache Dubbo 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

是的,是 Apache Dubbo,不在是Alibaba Dubbo。原因简单来说就是Alibaba 将dubbo 移交给Apache开源社区进行维护。

<!-- dubbo 2.7.x引入-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.4.1</version>
</dependency>

springboot整合dubbo2.7.x版本:https://blog.csdn.net/ycf921244819/article/details/103474394

SpringBoot整合Dubbo3.0基础配置:https://blog.csdn.net/instanceof_zjl/article/details/122014926

Duboo3.0+SpringBoot+zookeeper整合例子(附源码):https://blog.csdn.net/yudianxiaoxiao/article/details/121424179?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-121424179-blog-122014926.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-121424179-blog-122014926.pc_relevant_antiscanv2&utm_relevant_index=1

Dubbo配置类

SpringBoot整合Dubbo的三种(配置):https://blog.csdn.net/weixin_41359273/article/details/124642758

Dubbo整合Spring Boot(采用注解API方式配置):https://blog.csdn.net/u014429653/article/details/99705168

@Configuration
public class DubboConfig {
    private static final String APP_NAME = "monitor-notice-service";
    private static final String REMOTE_NAME = "monitor-notice-service-remote-idc";
    public static final String DUBBO_VERSION = "1.0.0";

    @Value("${sd.common.dubbo.registry.address:zookeeper://172.16.219.123:2181?backup=172.16.218.153:2181,172.16.216.255:2181}")
    private String sdRegistryAddress;

    @Value("${sd.common.dubbo.port:20880}")
    private Integer dubboPort;

    @Value("${sd.common.rest.port:8090}")
    private Integer restPort;

    @Value("${sd.common.rest.port:8090}")
    private Integer timeout;

    @Value("${linkx.common.dubbo.registry.address:zookeeper://172.16.219.123:2181?backup=172.16.218.153:2181,172.16.216.255:2181}")
    private String linkxRegistryAddress;

    /**
     * 配置应用名
     */
    @Bean
    public ApplicationConfig applicationConfig() {
        ApplicationConfig config = new ApplicationConfig();
        config.setName(APP_NAME);
        config.setOwner("jiang.peng");
        config.setOrganization("ps-ip");
        return config;
    }

    /**
     * 中台 配置注册中心
     */
    @Bean
    public RegistryConfig sdRegistryConfig() {
        RegistryConfig config = new RegistryConfig();
        config.setProtocol("dubbo");
        config.setAddress(sdRegistryAddress);
        config.setTimeout(10000);
        config.setVersion(DUBBO_VERSION);
        config.setId(REMOTE_NAME);
        return config;
    }

    /**
     * linkx 配置注册中心
     */
    @Bean
    public RegistryConfig linkxRegistryConfig() {
        RegistryConfig config = new RegistryConfig();
        config.setProtocol("dubbo");
        config.setAddress(linkxRegistryAddress);
        config.setTimeout(10000);
        return config;
    }

    @Bean
    public MetadataReportConfig metadataReportConfig() {
        return new MetadataReportConfig(sdRegistryAddress);
    }

    @Bean
    public ProviderConfig providerConfig() {
        ProviderConfig config = new ProviderConfig();
        config.setTimeout(3000);
        config.setRetries(0);
        config.setQueues(1000);
        return config;
    }

    @Bean("dubbo")
    public ProtocolConfig protocolConfigDubbo() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setPort(dubboPort);
        return protocolConfig;
    }

    @Bean("rest")
    public ProtocolConfig protocolConfigRest() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("rest");
        protocolConfig.setPort(restPort);
        return protocolConfig;
    }
}

Zookeeper

SpringBoot集成Zookeeper:https://blog.csdn.net/u010391342/article/details/100404588

ZooKeeper 常见面试题:https://blog.csdn.net/weixin_43122090/article/details/103645642?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165232770816782246475822%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&amp;request_id=165232770816782246475822&amp;biz_id=0&amp;utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-2-103645642-null-null.142^v9^pc_search_result_control_group,157^v4^control&amp;utm_term=ZooKeeper+%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98&amp;spm=1018.2226.3001.4187

Zookeeper 和 Dubbo 的关系? Zookeeper的作用: zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。当然也可以通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服务。zookeeper通过心跳机制可以检测挂掉的机器并将挂掉机器的ip和服务对应关系从列表中删除。至于支持高并发,简单来说就是横向扩展,在不更改代码的情况通过添加机器来提高运算能力。通过添加新的机器向zookeeper注册服务,服务的提供者多了能服务的客户就多了。

dubbo: 是管理中间层的工具,在业务层到数据仓库间有非常多服务的接入和服务提供者需要调度,dubbo提供一个框架解决这个问题。 注意这里的dubbo只是一个框架,至于你架子上放什么是完全取决于你的,就像一个汽车骨架,你需要配你的轮子引擎。这个框架中要完成调度必须要有一个分布式的注册中心,储存所有服务的元数据,你可以用zk,也可以用别的,只是大家都用zk。

zookeeper和dubbo的关系: Dubbo 的将注册中心进行抽象,它可以外接不同的存储媒介给注册中心提供服务,有 ZooKeeper,Memcached,Redis 等。

引入了 ZooKeeper 作为存储媒介,也就把 ZooKeeper 的特性引进来。首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时 候就需要分流,负载均衡就是为了分流而存在的,一个 ZooKeeper 群配合相应的 Web 应用就可以很容易达到负载均衡;资源同步,单单有负载均衡还不 够,节点之间的数据和资源需要同步,ZooKeeper 集群就天然具备有这样的功能;命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动 的时候,向 ZooKeeper 上的指定节点 /dubbo/${serviceName}/providers 目录下写入自己的 URL 地址,这个操作就完成了服务的发布。 其他特性还有 Mast 选举,分布式锁等。

Redis

Kafka

Spring-Kafka史上最强入门教程

Kafka简明教程:https://zhuanlan.zhihu.com/p/37405836

Kafka的Java客户端

生产者

1.引入依赖

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>2.4.1</version>
</dependency>

2.生产者发送消息的基本实现

//消息的发送方
public class MyProducer {
private final static String TOPIC_NAME = "my-replicated-topic";
public static void main(String[] args) throws ExecutionException,InterruptedException {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"10.31.167.10:9092,10.31.167.10:9093,10.31.167.10:9094");
//把发送的key从字符串序列化为字节数组
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
//把发送消息value从字符串序列化为字节数组
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());

Producer<String, String> producer = new KafkaProducer<String,String>(props);

Order order = new Order((long) i, i);
ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME, order.getOrderId().toString(), JSON.toJSONString(order));
RecordMetadata metadata = producer.send(producerRecord).get();
//=====阻塞=======
System.out.println("同步方式发送消息结果:" + "topic-" +metadata.topic() + "|partition-"+ metadata.partition() + "|offset-" +metadata.offset());Copy to clipboardErrorCopied

3.发送消息到指定分区上

ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME, 0 , order.getOrderId().toString(), JSON.toJSONString(order));

4.未指定分区,则会通过业务key的hash运算,算出消息往哪个分区上发

//未指定发送分区,具体发送的分区计算公式:hash(key)%partitionNum
ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME, order.getOrderId().toString(), JSON.toJSONString(order));

5.同步发送

生产者同步发消息,在收到kafka的ack告知发送成功之前一直处于阻塞状态

//等待消息发送成功的同步阻塞方法
RecordMetadata metadata = producer.send(producerRecord).get();
System.out.println("同步方式发送消息结果:" + "topic-" +metadata.topic() + "|partition-"+ metadata.partition() + "|offset-" +metadata.offset());

输入图片说明

6.异步发消息

生产者发消息,发送完后不用等待broker给回复,直接执行下面的业务逻辑。可以提供callback,让broker异步的调用callback,告知生产者,消息发送的结果

//要发送 5 条消息
Order order = new Order((long) i, i);
//指定发送分区
ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME, 0 , order.getOrderId().toString(),JSON.toJSONString(order));
//异步回调方式发送消息
producer.send(producerRecord, new Callback() {
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null) {
    System.err.println("发送消息失败:" +
    exception.getStackTrace());
}
if (metadata != null) {
System.out.println("异步方式发送消息结果:" + "topic-" +metadata.topic() + "|partition-"+ metadata.partition() + "|offset-" + metadata.offset());
         }
    }
});

7.关于生产者的ack参数配置

在同步发消息的场景下:生产者发动broker上后,ack会有 3 种不同的选择:

  • ( 1 )acks=0: 表示producer不需要等待任何broker确认收到消息的回复,就可以继续发送下一条消息。性能最高,但是最容易丢消息。
  • ( 2 )acks=1: 至少要等待leader已经成功将数据写入本地log,但是不需要等待所有follower是否成功写入。就可以继续发送下一条消息。这种情况下,如果follower没有成功备份数据,而此时leader又挂掉,则消息会丢失。
  • ( 3 )acks=-1或all: 需要等待 min.insync.replicas(默认为 1 ,推荐配置大于等于2) 这个参数配置的副本个数都成功写入日志,这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的数据保证。一般除非是金融级别,或跟钱打交道的场景才会使用这种配置。

code:

props.put(ProducerConfig.ACKS_CONFIG, "1");

8.其他一些细节

  • 发送会默认会重试 3 次,每次间隔100ms
  • 发送的消息会先进入到本地缓冲区(32mb),kakfa会跑一个线程,该线程去缓冲区中取16k的数据,发送到kafka,如果到 10 毫秒数据没取满16k,也会发送一次。
消费者

1.消费者消费消息的基本实现

public class MyConsumer {
private final static String TOPIC_NAME = "my-replicated-topic";
private final static String CONSUMER_GROUP_NAME = "testGroup";

public static void main(String[] args) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"10.31.167.10:9092,10.31.167.10:9093,10.31.167.10:9094");
// 消费分组名
props.put(ConsumerConfig.GROUP_ID_CONFIG, CONSUMER_GROUP_NAME);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class.getName());
//创建一个消费者的客户端
KafkaConsumer<String, String> consumer = new KafkaConsumer<String,String>(props);
// 消费者订阅主题列表
consumer.subscribe(Arrays.asList(TOPIC_NAME));

while (true) {
/*
* poll() API 是拉取消息的⻓轮询
*/
ConsumerRecords<String, String> records =consumer.poll(Duration.ofMillis( 1000 ));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("收到消息:partition = %d,offset = %d, key =%s, value = %s%n", record.partition(),record.offset(), record.key(), record.value());
            }
        }
    }
}

2.自动提交offset

  • 设置自动提交参数 - 默认
// 是否自动提交offset,默认就是true
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
// 自动提交offset的间隔时间
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");

消费者poll到消息后默认情况下,会自动向broker的_consumer_offsets主题提交当前主题-分区消费的偏移量。

自动提交会丢消息: 因为如果消费者还没消费完poll下来的消息就自动提交了偏移量,那么此 时消费者挂了,于是下一个消费者会从已提交的offset的下一个位置开始消费消息。之前未被消费的消息就丢失掉了。

3.手动提交offset

  • 设置手动提交参数
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");

在消费完消息后进行手动提交

  • 手动同步提交

    if (records.count() > 0 ) {
    // 手动同步提交offset,当前线程会阻塞直到offset提交成功
    // 一般使用同步提交,因为提交之后一般也没有什么逻辑代码了
    consumer.commitSync();
    }
  • 手动异步提交

    if (records.count() > 0 ) {
    // 手动异步提交offset,当前线程提交offset不会阻塞,可以继续处理后面的程序逻辑
    consumer.commitAsync(new OffsetCommitCallback() {
    @Override
    public void onComplete(Map<TopicPartition, OffsetAndMetadata>offsets, Exception exception) {
              if (exception != null) {
                  System.err.println("Commit failed for " + offsets);
                  System.err.println("Commit failed exception: " +exception.getStackTrace());
              }
           }
      });
    }

4.消费者poll消息的过程

  • 消费者建立了与broker之间的⻓连接,开始poll消息。
  • 默认一次poll 500条消息
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500 );

可以根据消费速度的快慢来设置,因为如果两次poll的时间如果超出了30s的时间间隔,kafka会认为其消费能力过弱,将其踢出消费组。将分区分配给其他消费者。

可以通过这个值进行设置:

props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30 * 1000 );

如果每隔1s内没有poll到任何消息,则继续去poll消息,循环往复,直到poll到消息。如果超出了1s,则此次⻓轮询结束。

ConsumerRecords<String, String> records =consumer.poll(Duration.ofMillis( 1000 ));

消费者发送心跳的时间间隔

props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 1000 );

kafka如果超过 10 秒没有收到消费者的心跳,则会把消费者踢出消费组,进行rebalance,把分区分配给其他消费者。

props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10 * 1000 );

5.指定分区消费

consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0 )));

6.消息回溯消费

consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0 )));
consumer.seekToBeginning(Arrays.asList(new TopicPartition(TOPIC_NAME,0 )));

7.指定offset消费

consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0 )));
consumer.seek(new TopicPartition(TOPIC_NAME, 0 ), 10 );

8.从指定时间点消费

List<PartitionInfo> topicPartitions =consumer.partitionsFor(TOPIC_NAME);
//从 1 小时前开始消费
long fetchDataTime = new Date().getTime() - 1000 * 60 * 60 ;
Map<TopicPartition, Long> map = new HashMap<>();
for (PartitionInfo par : topicPartitions) {
    map.put(new TopicPartition(TOPIC_NAME, par.partition()),fetchDataTime);
}
Map<TopicPartition, OffsetAndTimestamp> parMap =consumer.offsetsForTimes(map);
for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry :parMap.entrySet()) {
    TopicPartition key = entry.getKey();
    OffsetAndTimestamp value = entry.getValue();
    if (key == null || value == null) continue;
    Long offset = value.offset();
    System.out.println("partition-" + key.partition() +"|offset-" + offset);
    System.out.println();
    //根据消费里的timestamp确定offset
    if (value != null) {
        consumer.assign(Arrays.asList(key));
        consumer.seek(key, offset);
    }
}

9.新消费组的消费偏移量

当消费主题的是一个新的消费组,或者指定offset的消费方式,offset不存在,那么应该如何消费?

  • latest(默认) :只消费自己启动之后发送到主题的消息

  • earliest:第一次从头开始消费,以后按照消费offset记录继续消费,这个需要区别于consumer.seekToBeginning(每次都从头开始消费)

    props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
Springboot中使用Kafka

1.引入依赖

<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

2.配置文件

server:
    port: 8080
spring:
    kafka:
        bootstrap-servers: 172.16.253.21: 9093
        producer: # 生产者
            retries: 3 # 设置大于 0 的值,则客户端会将发送失败的记录重新发送
            batch-size: 16384
            buffer-memory: 33554432
            acks: 1
            # 指定消息key和消息体的编解码方式
            key-serializer: org.apache.kafka.common.serialization.StringSerializer
            value-serializer: org.apache.kafka.common.serialization.StringSerializer
        consumer:
            group-id: default-group
            enable-auto-commit: false
            auto-offset-reset: earliest
            key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
            value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
            max-poll-records: 500
        listener:
        # 当每一条记录被消费者监听器(ListenerConsumer)处理之后提交
        # RECORD
        # 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后提交
        # BATCH
        # 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,距离上次提交时间大于TIME时提交
        # TIME
        # 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,被处理record数量大于等于COUNT时提交
        # COUNT
        # TIME | COUNT 有一个条件满足时提交
        # COUNT_TIME
        # 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后, 手动调用Acknowledgment.acknowledge()后提交
        # MANUAL
        # 手动调用Acknowledgment.acknowledge()后立即提交,一般使用这种
        # MANUAL_IMMEDIATE
            ack-mode: MANUAL_IMMEDIATE
    redis:
        host: 172.16.253.21Copy to clipboardErrorCopied

3.消息生产者

  • 发送消息到指定topic
@RestController
public class KafkaController {
    private final static String TOPIC_NAME = "my-replicated-topic";
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @RequestMapping("/send")
    public void send() {
        kafkaTemplate.send(TOPIC_NAME, 0 , "key", "this is a msg");
    }
}

4.消息消费者

  • 设置消费组,消费指定topic

    @KafkaListener(topics = "my-replicated-topic",groupId = "MyGroup1")
    public void listenGroup(ConsumerRecord<String, String> record,Acknowledgment ack) {
      String value = record.value();
      System.out.println(value);
      System.out.println(record);
      //手动提交offset
      ack.acknowledge();
    }
  • 设置消费组、多topic、指定分区、指定偏移量消费及设置消费者个数。

@KafkaListener(groupId = "testGroup", topicPartitions = {
@TopicPartition(topic = "topic1", partitions = {"0", "1"}),
@TopicPartition(topic = "topic2", partitions = "0",partitionOffsets = @PartitionOffset(partition = "1",initialOffset = "100"))}
,concurrency = "3")//concurrency就是同组下的消费者个数,就是并发消费数,建议小于等于分区总数
public void listenGroup(ConsumerRecord<String, String> record,Acknowledgment ack) {
    String value = record.value();
    System.out.println(value);
    System.out.println(record);
    //手动提交offset
    ack.acknowledge();
}

Flink

线程池

//config包下
/**
 * @ClassName ExecutorConfig
 * @Description 自定线程池
 */
@Configuration
public class ExecutorConfig {
    /**
     * 线程池的名称根据自己的业务场景自己定义
     */
    public static final String NOTICE = "NOTICE";

    /**
     * 核心线程池
     */
    @Value("${thread.pool.core.size}")
    private Integer CORE_POOL_SIZE;
    /**
     * 最大线程池
     */
    @Value("${thread.pool.max.size}")
    private Integer MAX_POOL_SIZE;
    /**
     * 10s
     */
    private static final int KEEP_ALIVE_TIME = 10;
    /**
     * 缓冲队列
     */
    @Value("${thread.pool.queue.capacity}")
    private Integer QUEUE_CAPACITY;

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutorVoucher() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setThreadNamePrefix(NOTICE);
        executor.initialize();
        return executor;
    }
}

用法

@Autowired
@Qualifier("threadPoolTaskExecutor")
private ThreadPoolTaskExecutor poolTaskExecutor;

@Override
public void onMessages(ConsumerRecords<String, String> records) throws ParseException {

    log.debug("onMessages: {}", JSON.toJSONString(records));
    ServiceBo serviceBo = new ServiceBo();
    serviceBo.setMonitorDataService(monitorDataService);
    serviceBo.setTSDBUtil(tSDBUtil);
    serviceBo.setIndexConfigService(indexConfigService);
    serviceBo.setAlarmRecordService(alarmRecordService);
    serviceBo.setJedisPoolSd(jedisPoolSd);
    poolTaskExecutor.execute(new DeviceDataMqHandler(records,serviceBo));
}

Jackson

Json 是目前互联网应用使用最为广泛的信息交换格式之一。Spring Boot 内置了 Jackson。spring Boot 默认采用了 Jackson来处理诸如 @RequestBody @ResponseBody

一般情况下我们引入MVC,MVC里面帮我们引入了JSON依赖

<!-- springboot web(MVC)-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

SpringBoot基础篇(一)默认转换工具Jackson:https://blog.csdn.net/u013089490/article/details/83585794

Spring Boot jackson配置使用详解:https://blog.csdn.net/z28126308/article/details/90276705

config包下添加

/**
* Jackson配置类
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
@AutoConfigureBefore(JacksonAutoConfiguration.class)
@EnableConfigurationProperties(JacksonProperties.class)
public class JacksonConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
		builder.simpleDateFormat(JsonUtil.JacksonObjectMapper.PATTERN_DATETIME);
		//创建ObjectMapper
		ObjectMapper objectMapper = builder.createXmlMapper(false).build();
		//设置地点为中国
		objectMapper.setLocale(Locale.CHINA);
		//去掉默认的时间戳格式
		objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
		//设置为中国上海时区
		objectMapper.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
		//序列化时,日期的统一格式
		objectMapper.setDateFormat(new SimpleDateFormat(JsonUtil.JacksonObjectMapper.PATTERN_DATETIME, Locale.CHINA));
		//序列化处理
		objectMapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
		objectMapper.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true);
		//失败处理
		objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		//单引号处理
		objectMapper.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true);
		//反序列化时,属性不存在的兼容处理
		objectMapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		//日期格式化
		objectMapper.registerModule(JsonUtil.CustomJavaTimeModule.INSTANCE);
		objectMapper.findAndRegisterModules();
		return objectMapper;
	}
}

jackson包下添加

public class ExceptionUtil {
	/**
	 * 将CheckedException转换为UncheckedException.
	 */
	public static RuntimeException unchecked(Throwable e) {
		if (e instanceof Error) {
			throw (Error) e;
		} else if (e instanceof IllegalAccessException ||
			e instanceof IllegalArgumentException ||
			e instanceof NoSuchMethodException) {
			return new IllegalArgumentException(e);
		} else if (e instanceof InvocationTargetException) {
			return new RuntimeException(((InvocationTargetException) e).getTargetException());
		} else if (e instanceof RuntimeException) {
			return (RuntimeException) e;
		} else if (e instanceof InterruptedException) {
			Thread.currentThread().interrupt();
		}
		return runtime(e);
	}

	/**
	 * 不采用 RuntimeException 包装,直接抛出,使异常更加精准
	 */
	@SuppressWarnings("unchecked")
	private static <T extends Throwable> T runtime(Throwable throwable) throws T {
		throw (T) throwable;
	}
}

/**
 * 操作json的封装方法
 */
@Slf4j
public class JsonUtil {

	/**
	 * 001.json转换成对象
	 * @param:传入对象,json字符串
	 * @return:Object
	 */
	public static <T> T json2obj(String jsonStr, Class<T> clazz) throws IOException {
		return getInstance().readValue(jsonStr, clazz);
	}

	/**
	 * 002.对象转换成json
	 * @param obj
	 * @param:传入对象
	 * @return:json字符串
	 */
	public static String obj2json(Object obj) throws JsonProcessingException {
		return getInstance().writeValueAsString(obj);
	}

	/**
	 * 003.对象转换成对象
	 *
	 * @param obj   原对象
	 * @param clazz 目标对象
	 * @return T 目标对象类型
	 * @author zhangquan
	 * @date 2021/9/1 20:09
	 */
	public static <T> T obj2obj(Object obj, Class<T> clazz) throws IOException {
		return json2obj(obj2json(obj), clazz);
	}


	/**
	 * 将对象序列化成json字符串
	 *
	 * @param value javaBean
	 * @return jsonString json字符串
	 */
	public static <T> String toJson(T value) {
		try {
			return getInstance().writeValueAsString(value);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 将对象序列化成 json byte 数组
	 *
	 * @param object javaBean
	 * @return jsonString json字符串
	 */
	public static byte[] toJsonAsBytes(Object object) {
		try {
			return getInstance().writeValueAsBytes(object);
		} catch (JsonProcessingException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成对象
	 *
	 * @param content   content
	 * @param valueType class
	 * @param <T>       T 泛型标记
	 * @return Bean
	 */
	public static <T> T parse(String content, Class<T> valueType) {
		try {
			return getInstance().readValue(content, valueType);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 将json反序列化成对象
	 *
	 * @param content       content
	 * @param typeReference 泛型类型
	 * @param <T>           T 泛型标记
	 * @return Bean
	 */
	public static <T> T parse(String content, TypeReference<T> typeReference) {
		try {
			return getInstance().readValue(content, typeReference);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json byte 数组反序列化成对象
	 *
	 * @param bytes     json bytes
	 * @param valueType class
	 * @param <T>       T 泛型标记
	 * @return Bean
	 */
	public static <T> T parse(byte[] bytes, Class<T> valueType) {
		try {
			return getInstance().readValue(bytes, valueType);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}


	/**
	 * 将json反序列化成对象
	 *
	 * @param bytes         bytes
	 * @param typeReference 泛型类型
	 * @param <T>           T 泛型标记
	 * @return Bean
	 */
	public static <T> T parse(byte[] bytes, TypeReference<T> typeReference) {
		try {
			return getInstance().readValue(bytes, typeReference);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成对象
	 *
	 * @param in        InputStream
	 * @param valueType class
	 * @param <T>       T 泛型标记
	 * @return Bean
	 */
	public static <T> T parse(InputStream in, Class<T> valueType) {
		try {
			return getInstance().readValue(in, valueType);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成对象
	 *
	 * @param in            InputStream
	 * @param typeReference 泛型类型
	 * @param <T>           T 泛型标记
	 * @return Bean
	 */
	public static <T> T parse(InputStream in, TypeReference<T> typeReference) {
		try {
			return getInstance().readValue(in, typeReference);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成List对象
	 *
	 * @param content      content
	 * @param valueTypeRef class
	 * @param <T>          T 泛型标记
	 * @return List<T>
	 */
	public static <T> List<T> parseArray(String content, Class<T> valueTypeRef) {
		try {

			if (!StringUtils.startsWithIgnoreCase(content, "{")) {
				content = "{" + content + "}";
			}

			List<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<Map<String, Object>>>() {
			});

			List<T> result = new ArrayList<>();
			for (Map<String, Object> map : list) {
				result.add(toPojo(map, valueTypeRef));
			}
			return result;
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 将json字符串转成 JsonNode
	 *
	 * @param jsonString jsonString
	 * @return jsonString json字符串
	 */
	public static JsonNode readTree(String jsonString) {
		Objects.requireNonNull(jsonString, "jsonString is null");
		try {
			return getInstance().readTree(jsonString);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json字符串转成 JsonNode
	 *
	 * @param in InputStream
	 * @return jsonString json字符串
	 */
	public static JsonNode readTree(InputStream in) {
		Objects.requireNonNull(in, "InputStream in is null");
		try {
			return getInstance().readTree(in);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json字符串转成 JsonNode
	 * @param content content
	 * @return jsonString json字符串
	 */
	public static JsonNode readTree(byte[] content) {
		Objects.requireNonNull(content, "byte[] content is null");
		try {
			return getInstance().readTree(content);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json字符串转成 JsonNode
	 * @param jsonParser JsonParser
	 * @return jsonString json字符串
	 */
	public static JsonNode readTree(JsonParser jsonParser) {
		Objects.requireNonNull(jsonParser, "jsonParser is null");
		try {
			return getInstance().readTree(jsonParser);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}


	/**
	 * 将json byte 数组反序列化成对象
	 * @param content   json bytes
	 * @param valueType class
	 * @param <T>       T 泛型标记
	 * @return Bean
	 */
	@Nullable
	public static <T> T readValue(@Nullable byte[] content, Class<T> valueType) {
		if (ObjectUtils.isEmpty(content)) {
			return null;
		}
		try {
			return getInstance().readValue(content, valueType);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成对象
	 * @param jsonString jsonString
	 * @param valueType  class
	 * @param <T>        T 泛型标记
	 * @return Bean
	 */
	@Nullable
	public static <T> T readValue(@Nullable String jsonString, Class<T> valueType) {
		if (StringUtils.isBlank(jsonString)) {
			return null;
		}
		try {
			return getInstance().readValue(jsonString, valueType);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成对象
	 * @param in        InputStream
	 * @param valueType class
	 * @param <T>       T 泛型标记
	 * @return Bean
	 */
	@Nullable
	public static <T> T readValue(@Nullable InputStream in, Class<T> valueType) {
		if (in == null) {
			return null;
		}
		try {
			return getInstance().readValue(in, valueType);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成对象
	 * @param content       bytes
	 * @param typeReference 泛型类型
	 * @param <T>           T 泛型标记
	 * @return Bean
	 */
	@Nullable
	public static <T> T readValue(@Nullable byte[] content, TypeReference<T> typeReference) {
		if (ObjectUtils.isEmpty(content)) {
			return null;
		}
		try {
			return getInstance().readValue(content, typeReference);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成对象
	 * @param jsonString    jsonString
	 * @param typeReference 泛型类型
	 * @param <T>           T 泛型标记
	 * @return Bean
	 */
	@Nullable
	public static <T> T readValue(@Nullable String jsonString, TypeReference<T> typeReference) {
		if (StringUtils.isBlank(jsonString)) {
			return null;
		}
		try {
			return getInstance().readValue(jsonString, typeReference);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 将json反序列化成对象
	 * @param in            InputStream
	 * @param typeReference 泛型类型
	 * @param <T>           T 泛型标记
	 * @return Bean
	 */
	@Nullable
	public static <T> T readValue(@Nullable InputStream in, TypeReference<T> typeReference) {
		if (in == null) {
			return null;
		}
		try {
			return getInstance().readValue(in, typeReference);
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 封装 map type
	 * @param keyClass   key 类型
	 * @param valueClass value 类型
	 * @return MapType
	 */
	public static MapType getMapType(Class<?> keyClass, Class<?> valueClass) {
		return getInstance().getTypeFactory().constructMapType(Map.class, keyClass, valueClass);
	}

	/**
	 * 封装 map type
	 * @param elementClass 集合值类型
	 * @return CollectionLikeType
	 */
	public static CollectionLikeType getListType(Class<?> elementClass) {
		return getInstance().getTypeFactory().constructCollectionLikeType(List.class, elementClass);
	}

	/**
	 * 读取集合
	 * @param content      bytes
	 * @param elementClass elementClass
	 * @param <T>          泛型
	 * @return 集合
	 */
	public static <T> List<T> readList(@Nullable byte[] content, Class<T> elementClass) {
		if (ObjectUtils.isEmpty(content)) {
			return Collections.emptyList();
		}
		try {
			return getInstance().readValue(content, getListType(elementClass));
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 读取集合
	 * @param content      InputStream
	 * @param elementClass elementClass
	 * @param <T>          泛型
	 * @return 集合
	 */
	public static <T> List<T> readList(@Nullable InputStream content, Class<T> elementClass) {
		if (content == null) {
			return Collections.emptyList();
		}
		try {
			return getInstance().readValue(content, getListType(elementClass));
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 读取集合
	 * @param content      bytes
	 * @param elementClass elementClass
	 * @param <T>          泛型
	 * @return 集合
	 */
	public static <T> List<T> readList(@Nullable String content, Class<T> elementClass) {
		if (ObjectUtils.isEmpty(content)) {
			return Collections.emptyList();
		}
		try {
			return getInstance().readValue(content, getListType(elementClass));
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 读取集合
	 * @param content    bytes
	 * @param keyClass   key类型
	 * @param valueClass 值类型
	 * @param <K>        泛型
	 * @param <V>        泛型
	 * @return 集合
	 */
	public static <K, V> Map<K, V> readMap(@Nullable byte[] content, Class<?> keyClass, Class<?> valueClass) {
		if (ObjectUtils.isEmpty(content)) {
			return Collections.emptyMap();
		}
		try {
			return getInstance().readValue(content, getMapType(keyClass, valueClass));
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 读取集合
	 * @param content    InputStream
	 * @param keyClass   key类型
	 * @param valueClass 值类型
	 * @param <K>        泛型
	 * @param <V>        泛型
	 * @return 集合
	 */
	public static <K, V> Map<K, V> readMap(@Nullable InputStream content, Class<?> keyClass, Class<?> valueClass) {
		if (ObjectUtils.isEmpty(content)) {
			return Collections.emptyMap();
		}
		try {
			return getInstance().readValue(content, getMapType(keyClass, valueClass));
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 读取集合
	 * @param content    bytes
	 * @param keyClass   key类型
	 * @param valueClass 值类型
	 * @param <K>        泛型
	 * @param <V>        泛型
	 * @return 集合
	 */
	public static <K, V> Map<K, V> readMap(@Nullable String content, Class<?> keyClass, Class<?> valueClass) {
		if (ObjectUtils.isEmpty(content)) {
			return Collections.emptyMap();
		}
		try {
			return getInstance().readValue(content, getMapType(keyClass, valueClass));
		} catch (IOException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * jackson 的类型转换
	 */
	public static <T> T convertValue(Object fromValue, Class<T> toValueType) {
		return getInstance().convertValue(fromValue, toValueType);
	}

	/**
	 * jackson 的类型转换
	 */
	public static <T> T convertValue(Object fromValue, JavaType toValueType) {
		return getInstance().convertValue(fromValue, toValueType);
	}

	/**
	 * jackson 的类型转换
	 */
	public static <T> T convertValue(Object fromValue, TypeReference<T> toValueTypeRef) {
		return getInstance().convertValue(fromValue, toValueTypeRef);
	}

	/**
	 * tree 转对象
	 */
	public static <T> T treeToValue(TreeNode treeNode, Class<T> valueType) {
		try {
			return getInstance().treeToValue(treeNode, valueType);
		} catch (JsonProcessingException e) {
			throw ExceptionUtil.unchecked(e);
		}
	}

	/**
	 * 对象转为 json node
	 */
	public static JsonNode valueToTree(@Nullable Object value) {
		return getInstance().valueToTree(value);
	}

	/**
	 * 判断是否可以序列化
	 */
	public static boolean canSerialize(@Nullable Object value) {
		if (value == null) {
			return true;
		}
		return getInstance().canSerialize(value.getClass());
	}

	public static Map<String, Object> toMap(String content) {
		try {
			return getInstance().readValue(content, Map.class);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
		return null;
	}

	public static <T> Map<String, T> toMap(String content, Class<T> valueTypeRef) {
		try {
			Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, Map<String, Object>>>() {
			});
			Map<String, T> result = new HashMap<>(16);
			for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) {
				result.put(entry.getKey(), toPojo(entry.getValue(), valueTypeRef));
			}
			return result;
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
		return null;
	}

	public static <T> T toPojo(Map fromValue, Class<T> toValueType) {
		return getInstance().convertValue(fromValue, toValueType);
	}

	public static ObjectMapper getInstance() {
		return JacksonHolder.INSTANCE;
	}

	private static class JacksonHolder {
		private static final ObjectMapper INSTANCE = new JacksonObjectMapper();
	}

	public static class JacksonObjectMapper extends ObjectMapper {
		public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
		public static final String PATTERN_DATE = "yyyy-MM-dd";
		public static final String PATTERN_TIME = "HH:mm:ss";
		private static final long serialVersionUID = 4288193147502386170L;
		private static final Locale CHINA = Locale.CHINA;

		private QxwzJacksonProperties jacksonProperties ;

		public JacksonObjectMapper(ObjectMapper src) {
			super(src);
		}
		public JacksonObjectMapper(QxwzJacksonProperties jacksonProperties) {
			this.jacksonProperties = jacksonProperties;
		}

		public JacksonObjectMapper() {
			super();
			//设置地点为中国
			super.setLocale(CHINA);
			//去掉默认的时间戳格式
			super.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
			//设置为中国上海时区
			super.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
			//序列化时,日期的统一格式
			super.setDateFormat(new SimpleDateFormat(PATTERN_DATETIME, Locale.CHINA));
			// 单引号
			super.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
			// 允许JSON字符串包含非引号控制字符(值小于32的ASCII字符,包含制表符和换行符)
			super.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
			super.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true);
			super.findAndRegisterModules();
			//失败处理
			super.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
			super.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			//单引号处理
			super.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true);
			//反序列化时,属性不存在的兼容处理s
			super.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
			//日期格式化
			super.registerModule(new CustomJavaTimeModule());
			super.findAndRegisterModules();
		}

		@Override
		public ObjectMapper copy() {
			return new JacksonObjectMapper(this);
		}
	}

	public static class CustomJavaTimeModule extends SimpleModule {
		public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern(JacksonObjectMapper.PATTERN_DATETIME);
		public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(JacksonObjectMapper.PATTERN_DATE);
		public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(JacksonObjectMapper.PATTERN_TIME);
		public static final CustomJavaTimeModule INSTANCE = new CustomJavaTimeModule();

		public CustomJavaTimeModule() {
			super(PackageVersion.VERSION);
			this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATETIME_FORMAT));
			this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMAT));
			this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FORMAT));
			this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATETIME_FORMAT));
			this.addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMAT));
			this.addSerializer(LocalTime.class, new LocalTimeSerializer(TIME_FORMAT));
		}
	}
}

properties包下新增

@Getter
@Setter
@ConfigurationProperties("jackson")
public class JacksonProperties {
    /**
     * null 转为 空,字符串转成"",数组转为[],对象转为{},数字转为-1
     */
    private Boolean nullToEmpty = Boolean.TRUE;
    /**
     * 响应到前端,大数值自动写出为 String,避免精度丢失
     */
    private Boolean bigNumToString = Boolean.TRUE;
    /**
     * 支持 MediaType text/plain
     */
    private Boolean supportTextPlain = Boolean.FALSE;
}

EasyExcel

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。

github地址:https://github.com/alibaba/easyexcel

EasyExcel全面教程快速上手:https://blog.csdn.net/sinat_32366329/article/details/103109058

使用easyexcel写入:https://www.yuque.com/easyexcel/doc/write

使用easyexcel读取:https://www.yuque.com/easyexcel/doc/read

示例代码地址:https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo

excel包下新增

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Builder(toBuilder = true)
public class HeaderMapping{

    private String headName;

    private String formKey;
}

public class EasyExcelUtil {
    private EasyExcelUtil(){
        
    }
    /**
    * @Title: getSheetData 
    * @Description:  根据表头映射和数据获取easyExcel需要的数据
    * @param @param dataList 数据
    * @param @param headerMappings 表头映射
    * @param @return:参数含义
    * @return List<Object>    返回类型 
    * @throws
     */
    public static List<Object> getSheetData(List<Map<String,Object>> dataList, List<HeaderMapping> headerMappings){
        List<Object> excelDataList = new ArrayList<>();
        for (Map<String,Object> data : dataList){
            List<Object> rows = new ArrayList<>();
            for (HeaderMapping headerMapping : headerMappings){
                Object object = data.get(headerMapping.getFormKey());
                object = ObjectUtils.defaultIfNull(object, "");
                rows.add(object);
            }
            excelDataList.add(rows);
        }
        
        return excelDataList;
    }
    /**
    * @Title: getSheetHead 
    * @Description:  根据表头映射和数据获取easyExcel需要的表头格式
    * @param @param headerMappings
    * @param @return:参数含义
    * @return List<List<String>>    返回类型 
    * @throws
     */
    public static List<List<String>>  getSheetHead(List<HeaderMapping> headerMappings){
        List<List<String>> head = new ArrayList<>();
        for (HeaderMapping headerMapping : headerMappings){
            head.add(Arrays.asList(headerMapping.getHeadName()));
        }
        return head;
    }
}

test包下新增

public class WriteExcelUtils {
    public static void main(String... args) {
        List<List<String>> titleList = Lists.newArrayList();

        List fristTitle = Lists.newArrayList();
        fristTitle.add("监测时间");
        titleList.add(fristTitle);

        List secTitle = Lists.newArrayList();
        secTitle.add("监测值");
        secTitle.add("x");
        titleList.add(secTitle);

        List thiTitle = Lists.newArrayList();
        thiTitle.add("监测值");
        thiTitle.add("y");
        titleList.add(thiTitle);

        List furTitle = Lists.newArrayList();
        furTitle.add("监测值");
        furTitle.add("z");
        titleList.add(furTitle);

        List fivTitle = Lists.newArrayList();
        fivTitle.add("监测值");
        fivTitle.add("p");
        titleList.add(fivTitle);
        List<List<String>> dataList = Lists.newArrayList();
        for (int i=0;i<10;i++) {

            List fristData = Lists.newArrayList();
            fristData.add("列1-"+i);
            fristData.add("列2-"+i);
            fristData.add("列3-"+i);
            fristData.add("列4-"+i);
            fristData.add("列5-"+i);
            dataList.add(fristData);
        }
        OnceAbsoluteMergeStrategy monitorTimeStrategy = new OnceAbsoluteMergeStrategy(0,1,0,0);
        OnceAbsoluteMergeStrategy monitorValueStrategy = new OnceAbsoluteMergeStrategy(0,0,1,4);
        EasyExcel.write("/Users/zuoping.liu/Desktop/temp.xlsx").registerWriteHandler(monitorTimeStrategy).registerWriteHandler(monitorValueStrategy).head(titleList).sheet("模板").doWrite(dataList);
    }
 }

SpringCloud

SpringCloud,基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。

SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用SpringBoot的开发风格做到一键启动和部署。

SpringBoot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考研的服务框架组合起来, 通过SpringBoot风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂,易部署和易维护的分布式系统开发工具包

SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。

狂神笔记与源码:https://github.com/lzh66666/spring-cloud-kuang

脚手架:https://github.com/huzhicheng/spring-cloud-study

Eureka服务注册与发现

  • Eureka是Netflix的有个子模块,也是核心模块之一。Eureka是基于REST的服务,用于定位服务,以实现云端中间件层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务注册与发现,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper.

  • Eureka采用了C-S的架构设计,EurekaServer作为服务注册功能的服务器,他是服务注册中心.

  • 而系统中的其他微服务,使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,Springcloud 的一些其他模块 (比如Zuul) 就可以通过EurekaServer来发现系统中的其他微服务,并执行相关的逻辑.

  • Eureka 包含两个组件:Eureka ServerEureka Client.

  • Eureka Server 提供服务注册,各个节点启动后,回在EurekaServer中进行注册,这样Eureka Server中的服务注册表中将会储存所有课用服务节点的信息,服务节点的信息可以在界面中直观的看到.

  • Eureka Client 是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳 (默认周期为30秒) 。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉 (默认周期为90s).

负载均衡以及Ribbon

  • Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具

  • 简单的说,Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。简单的说,就是在配置文件中列出 LoadBalancer (简称LB:负载均衡) 后面所有的及其,Ribbon 会自动的帮助你基于某种规则 (如简单轮询,随机连接等等) 去连接这些机器。我们也容易使用 Ribbon 实现自定义的负载均衡算法!

  • Dubbo、SpringCloud 中均给我们提供了负载均衡,SpringCloud 的负载均衡算法可以自定义

Feign:负载均衡(基于服务端)

Feign是声明式Web Service客户端,它让微服务之间的调用变得更简单,类似controller调用service。SpringCloud集成了Ribbon和Eureka,可以使用Feigin提供负载均衡的http客户端

  • 前面在使用Ribbon + RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一个客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步的封装,由他来帮助我们定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它 (类似以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon 时,自动封装服务调用客户端的开发量。

Feign默认集成了Ribbon

  • 利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。

Hystrix:服务熔断

Hystrix是一个应用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。

“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控 (类似熔断保险丝) ,向调用方方茴一个服务预期的,可处理的备选响应 (FallBack) ,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

服务降级

什么是服务降级

​ 服务降级是指 当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务。   资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。所以,一般在高峰期,为了保证核心功能服务的可用性,都要对某些服务降级处理。比如当双11活动时,把交易无关的服务统统降级,如查看蚂蚁深林,查看历史订单等等。

服务降级主要用于什么场景呢?当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。   降级的方式可以根据业务来,可以延迟服务,比如延迟给用户增加积分,只是放到一个缓存中,等服务平稳之后再执行 ;或者在粒度范围内关闭服务,比如关闭相关文章的推荐。

Zull路由网关

什么是zuul?

Zull包含了对请求的路由(用来跳转的)和过滤两个最主要功能:

其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。