SAAS-HRM-day9(页面静态化实现)

1. 搭建页面代理模块

1.1 步骤分析

  1. 创建项目
  2. 导包
  3. 配置application.yml
  4. 入口类
  5. 日志
  6. swagger
  7. 路由
  8. 测试
  9. 抽取交换机和队列常量
  10. 配置RabbitMQ
  11. 配置处理程序(队列的消费者)

1.2 步骤实现

  1. 创建项目
  • hrm_parent
    • hrm_page_agent_parent
      • hrm_page_agent_common
      • hrm_page_agent_client
      • hrm_page_agent_service
  1. 导包
  • hrm_page_agent_common
        <!--公共的工具-->
        <dependency>
            <groupId>cn.wangningbo.hrm</groupId>
            <artifactId>hrm_basic_util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--web场景-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--测试场景-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
  • hrm_page_agent_client
    <!--依赖于公共的conmmon-->
    <dependency>
        <groupId>cn.wangningbo.hrm</groupId>
        <artifactId>hrm_page_agent_common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--客户端feign支持-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  • hrm_page_agent_service
        <!--公共的common依賴-->
        <dependency>
            <groupId>cn.wangningbo.hrm</groupId>
            <artifactId>hrm_page_agent_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- Eureka 客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--引入swagger支持-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--RabbitMQ支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!--阿里巴巴的json工具fastjson-->
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
        <!--fastdfs的内部支持,内部调用自己的fastdfs-->
        <dependency>
            <groupId>cn.wangningbo.hrm</groupId>
            <artifactId>hrm_basic_fastdfs_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--io工具包-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
  1. 配置application.yml

在hrm_page_agent_service配置

server:
  port: 9007
spring:
  application:
    name: hrm-page-agent
rabbitmq:
  host: 127.0.0.1
  port: 5672
  username: guest
  password: guest
  virtualHost: /
  queues:
    routingKey: hrmCourseSite
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    prefer-ip-address: true
  1. 入口类

在hrm_page_agent_service配置

package cn.wangningbo.hrm;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class PageAgent9007Application {
    public static void main(String[] args) {
        SpringApplication.run(PageAgent9007Application.class, args);
    }
}
  1. 日志

在hrm_page_agent_service配置

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="/hrm/" />
    <!-- 定义日志文件名称 -->
    <property name="appName" value="hrm-page-agent"></property>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
            %d表示日期时间,
            %thread表示线程名,
            %-5level:级别从左显示5个字符宽度
            %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
            %msg:日志消息,
            %n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->  
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称
           /hrm/hrm-course/hrm-course.log
        -->
        <file>${LOG_HOME}/${appName}/${appName}.log</file>
        <!--
        当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
        TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动
            -->
            <fileNamePattern>${LOG_HOME}/${appName}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!-- 
            可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
            且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
            那些为了归档而创建的目录也会被删除。
            -->
            <MaxHistory>365</MaxHistory>
            <!-- 
            当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出格式: -->     
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 
        logger主要用于存放日志对象,也可以定义日志类型、级别
        name:表示匹配的logger类型前缀,也就是包的前半部分
        level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
        additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
        false:表示只用当前logger的appender-ref,true:
        表示当前logger的appender-ref和rootLogger的appender-ref都有效
    -->
    <!-- hibernate logger -->
    <logger name="cn.wangningbo" level="debug" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>



    <!-- 
    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 
    -->
    <root level="info">
        <appender-ref ref="stdout" />
        <appender-ref ref="appLogAppender" />
    </root>
</configuration> 
  1. swagger

在hrm_page_agent_service配置

package cn.wangningbo.hrm.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class Swagger2 {
 
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                //对外暴露服务的包,以controller的方式暴露,所以就是controller的包.
                .apis(RequestHandlerSelectors.basePackage("cn.wangningbo.hrm.web.controller"))
                .paths(PathSelectors.any())
                .build();
    }


    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("页面代理api")
                .description("页面代理接口文档说明")
                .contact(new Contact("wangningbo", "", "wang_ning_bo163@163.com"))
                .version("1.0")
                .build();
    }

}
  1. 路由

在zuul的配置文件里配置一下

zuul:
    routes:
        pageAgent.serviceId: hrm-page-agent # 服务名
        pageAgent.path: /pageAgent/** # 把pageAgent打头的所有请求都转发给hrm-page-agent

在zuul的config包下配置一下

resources.add(swaggerResource("页面代理系统", "/services/pageAgent/v2/api-docs", "2.0"));

最终结果

package cn.wangningbo.hrm.config;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.ArrayList;
import java.util.List;

@Component
@Primary
public class DocumentationConfig implements SwaggerResourcesProvider {
    @Override
    public List<SwaggerResource> get() {
        List resources = new ArrayList<>();
        //通过网关访问服务地址
        resources.add(swaggerResource("系统管理", "/services/sysmanage/v2/api-docs", "2.0"));
        resources.add(swaggerResource("课程中心", "/services/course/v2/api-docs", "2.0"));
        resources.add(swaggerResource("分布式文件系统", "/services/fastdfs/v2/api-docs", "2.0"));
        resources.add(swaggerResource("分布式全文检索", "/services/es/v2/api-docs", "2.0"));
        resources.add(swaggerResource("redis中央缓存", "/services/redis/v2/api-docs", "2.0"));
        resources.add(swaggerResource("页面管理系统", "/services/page/v2/api-docs", "2.0"));
        resources.add(swaggerResource("页面代理系统", "/services/pageAgent/v2/api-docs", "2.0"));
        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location, String version) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion(version);
        return swaggerResource;
    }
}
  1. 测试

    启动项目,测试swagger是否成功

  2. 抽取交换机和队列常量

在公共模块hrm_basic_parent下新建子模块hrm_basic_rabbitmq

配置常量

package cn.wangningbo.hrm.config;

/**
 * 抽取常量
 */
public class RabbitmqConstants {
    // 交换机常量
    public static final String EXCHANGE_DIRECT_INFORM = "exchange_direct_inform";
    // 队列常量
    public static final String QUEUE_INFORM_PAGESTATIC = "queue_inform_pageStatic";
}
  1. 配置RabbitMQ

在hrm_page_agent_service的pom里面依赖抽取的常量模块

        <dependency>
            <groupId>cn.wangningbo.hrm</groupId>
            <artifactId>hrm_basic_rabbitmq</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

在hrm_page_agent_service配置RabbitMQ

package cn.wangningbo.hrm.config;


import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 主站
 * 课程管理
 * 职位管理
 */
@Configuration
public class RabbitmqConfig {

    @Value("${rabbitmq.queues.routingKey}")
    public  String routingKey;
    public static final String EXCHANGE_DIRECT_INFORM = RabbitmqConstants.EXCHANGE_DIRECT_INFORM;
    public static final String QUEUE_INFORM_PAGESTATIC = RabbitmqConstants.QUEUE_INFORM_PAGESTATIC;

    /**
     * 交换机配置
     * ExchangeBuilder提供了fanout、direct、topic、header交换机类型的配置
     *
     * @return the exchange
     */
    @Bean(EXCHANGE_DIRECT_INFORM)
    public Exchange exchange_direct_inform() {
    //durable(true)持久化,消息队列重启后交换机仍然存在
        return ExchangeBuilder.directExchange(EXCHANGE_DIRECT_INFORM).durable(true).build();
    }

    //声明队列
    @Bean(QUEUE_INFORM_PAGESTATIC) //交给spring管理的bean的名字可以随便的
    public Queue pageStaticQueue() {
        Queue queue = new Queue(QUEUE_INFORM_PAGESTATIC); //队列名
        return queue;
    }

    //    Qualifier//获取特定名称bean
    @Bean
    public Binding BINDING_QUEUE_INFORM_HRMJOBSITE(@Qualifier(QUEUE_INFORM_PAGESTATIC) Queue queue,
                                              @Qualifier(EXCHANGE_DIRECT_INFORM) Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
    }


    public String getRoutingKey() {
        return routingKey;
    }

    public void setRoutingKey(String routingKey) {
        this.routingKey = routingKey;
    }
}
  1. 配置处理程序(队列的消费者)

改造fastdfs的下载方法

FastDFSClient

    /**
     * 下载
     *
     * @param path
     */
    @GetMapping(value = "/download", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    //直接把流写到Response // 注意:这个Response是feign的
    Response download(@RequestParam("path") String path);

FastDFSClientHystrixFallbackFactory

package cn.wangningbo.hrm.client;

import cn.wangningbo.hrm.util.AjaxResult;
import feign.Response;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Component
public class FastDFSClientHystrixFallbackFactory implements FallbackFactory<FastDFSClient> {
    @Override
    public FastDFSClient create(Throwable throwable) {
        return new FastDFSClient() {
            @Override
            public String upload(MultipartFile file) {
                return null;
            }

            @Override
            public AjaxResult delete(String path) {
                return null;
            }

            @Override
            public Response download(String path) {
                return null;
            }
        };
    }
}

在hrm_page_agent_service配置处理程序

package cn.wangningbo.hrm.handler;

import cn.wangningbo.hrm.client.FastDFSClient;
import cn.wangningbo.hrm.config.RabbitmqConstants;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import feign.Response;
import org.apache.commons.io.IOUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

@Component
public class StaticPageDownloadHandler {

    @Autowired
    private FastDFSClient fastDFSClient;

    @RabbitListener(queues = RabbitmqConstants.QUEUE_INFORM_PAGESTATIC)
    public void receiveHomeSite(String msg, Message message, Channel channel) {
        //msg -fileSysType,staticPageUrl,physicalPath
        JSONObject jsonObject = JSONObject.parseObject(msg);
        Integer fileSysType = jsonObject.getInteger("fileSysType");
        String staticPageUrl = jsonObject.getString("staticPageUrl");
        String physicalPath = jsonObject.getString("physicalPath");
        System.out.println(staticPageUrl);
        System.out.println(physicalPath);

        switch (fileSysType) {
            case 0: //fastdfs
                System.out.println("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[");
                fastDfsDownloadAndCopy(staticPageUrl, physicalPath);
                break;
            case 1: //hdfs
                hdfsDownloadAndCopy(staticPageUrl, physicalPath);
                break;
            default:
                break;
        }
    }
    /**
     * 通过fastdfs下载文件,并且拷贝到特定的目录
     * @param staticPageUrl
     * @param physicalPath
     */
    private void hdfsDownloadAndCopy(String staticPageUrl, String physicalPath) {

        //@TODO
    }

    /**
     * 通过fastdfs下载文件,并且拷贝到特定的目录
     * @param staticPageUrl
     * @param physicalPath
     */
    private void fastDfsDownloadAndCopy(String staticPageUrl, String physicalPath) {
        System.out.println(staticPageUrl+"=====================================staticPageUrl");
        System.out.println(physicalPath+"-----------------------------------------physicalPath");
        Response response = fastDFSClient.download(staticPageUrl);
        System.out.println("pppppppppppppppppp||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||");
        InputStream is = null;
        OutputStream os = null;
        try {
            is  = response.body().asInputStream();
            os = new FileOutputStream(physicalPath);
            IOUtils.copy(is,os);
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2. 发布页面架构图

[图片上传失败...(image-ceee53-1569803685569)]
技术方案说明:

  1. 平台包括多个站点,页面归属不同的站点。
  2. 发布一个页面应将该页面发布到所属站点的服务器上。
  3. 每个站点服务部署cms client程序,并与交换机绑定,绑定时指定站点Id为routingKey。 指定站点id为routingKey就可以实现cms client只能接收到所属站点的页面发布消息。
  4. 页面发布程序向MQ发布消息时指定页面所属站点Id为routingKey,将该页面发布到它所在服务器上的cms client。

路由模式分析如下:

发布一个页面,需发布到该页面所属的每个站点服务器,其它站点服务器不发布。

比如:发布一个门户的页面,需要发布到每个门户服务器上,而用户中心服务器则不需要发布。 所以本项目采用routing模式,用站点id作为routingKey,这样就可以匹配页面只发布到所属的站点服务器上。

页面发布流程图如下:
[图片上传失败...(image-9ea392-1569803685569)]

2.1 页面发布生产方

2.1.1 页面静态化服务

hrm_page_service的最终导包情况

        <!--公共代码-->
        <dependency>
            <groupId>cn.wangningbo.hrm</groupId>
            <artifactId>hrm_page_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- Eureka 客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--mybatis-plus支持-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!--数据库支持-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--配置中心支持-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!--引入swagger支持-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--内部调用redis-->
        <dependency>
            <groupId>cn.wangningbo.hrm</groupId>
            <artifactId>hrm_basic_redis_client</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--web场景-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--测试场景-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>cn.wangningbo.hrm</groupId>
            <artifactId>hrm_basic_fastdfs_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>cn.wangningbo.hrm</groupId>
            <artifactId>hrm_basic_rabbitmq</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
  1. client
    /**
     * 静态化页面
     * @param map
     * @return
     */
    @PostMapping("/staticPage")
    AjaxResult staticPage(Map<String, String> map);
  1. HystrixFallbackFactory暂时不做处理
            @Override
            public AjaxResult staticPage(Map<String, String> map) {
                return null;
            }
  1. controller
    /**
     * 静态化页面
     * @param map
     * @return
     */
    @PostMapping("/staticPage")
    AjaxResult staticPage(@RequestBody Map<String, String> map){
        String dataKey = map.get("dataKey");
        String pageName = map.get("pageName");
        try {
            pageConfigService.staticPage(dataKey,pageName);
            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setSuccess(false).setMessage("静态化失败!"+e.getMessage());
        }
    }
  1. IService
    void staticPage(String dataKey, String pageName);
  1. ServiceImpl

修改fastdfs的上传:client

    @PostMapping(value="/upload", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
            , consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String upload(@RequestPart("file") MultipartFile file);

修改fastdfs的上传:controller

    /**
     * 上传
     *
     * @param file
     * @return
     */
    @PostMapping(value = "/upload", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
            , consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String upload(@RequestPart("file") MultipartFile file) {
        try {
            String fileName = file.getOriginalFilename(); // 1.png
            String extName = fileName.substring(fileName.lastIndexOf(".") + 1);
            System.out.println(extName);
            return FastDfsApiOpr.upload(file.getBytes(), extName);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("error...." + e.getMessage());
        }
        return null;
    }

压缩包工具ZipUtil

package cn.wangningbo.hrm.util;

import java.io.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 *  
 * @version 1.0 
 * @since 2015-9-14 
 * 文件解压缩工具类 
 * 
 */  
public final class ZipUtil  
{  
  
    /** 
     * 缓冲大小 
     */  
    private static int BUFFERSIZE = 2 << 10;  
      
    /** 
     * 压缩 
     * @param paths 
     * @param fileName 
     */  
    public static void zip(String[] paths, String fileName)  
    {  
          
        ZipOutputStream zos = null;
        try  
        {  
            zos = new ZipOutputStream(new FileOutputStream(fileName));
            for(String filePath : paths)  
            {  
                //递归压缩文件  
                File file = new File(filePath);  
                String relativePath = file.getName();  
                if(file.isDirectory())  
                {  
                    relativePath += File.separator;
                }  
                zipFile(file, relativePath, zos);  
            }  
        }  
        catch (IOException e)
        {  
            e.printStackTrace();  
        }  
        finally  
        {  
            try  
            {  
                if(zos != null)  
                {  
                    zos.close();  
                }  
            }  
            catch (IOException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }  
      
    public static void zipFile(File file, String relativePath, ZipOutputStream zos)  
    {  
        InputStream is = null;
        try  
        {  
            if(!file.isDirectory())  
            {  
                ZipEntry zp = new ZipEntry(relativePath);
                zos.putNextEntry(zp);  
                is = new FileInputStream(file);
                byte[] buffer = new byte[BUFFERSIZE];  
                int length = 0;  
                while ((length = is.read(buffer)) >= 0)  
                {  
                    zos.write(buffer, 0, length);  
                }  
                zos.flush();  
                zos.closeEntry();  
            }  
            else  
            {  
                for(File f: file.listFiles())  
                {  
                    zipFile(f, relativePath + f.getName() + File.separator, zos);  
                }  
            }  
        }  
        catch (IOException e)  
        {  
            e.printStackTrace();  
        }  
        finally  
        {  
            try  
            {  
                if(is != null)  
                {  
                    is.close();  
                }  
            }  
            catch (IOException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }  
      
    /** 
     * 解压缩 
     * @param fileName 
     * @param path 
     */  
    public static void unzip(String fileName, String path)  
    {  
        FileOutputStream fos = null;  
        InputStream is = null;  
        try  
        {  
            ZipFile zf = new ZipFile(new File(fileName));
            Enumeration en = zf.entries();
            while (en.hasMoreElements())  
            {  
                ZipEntry zn = (ZipEntry) en.nextElement();  
                if (!zn.isDirectory())  
                {  
                    is = zf.getInputStream(zn);  
                    File f = new File(path + zn.getName());  
                    File file = f.getParentFile();  
                    file.mkdirs();  
                    fos = new FileOutputStream(path + zn.getName());  
                    int len = 0;  
                    byte bufer[] = new byte[BUFFERSIZE];  
                    while (-1 != (len = is.read(bufer)))  
                    {  
                        fos.write(bufer, 0, len);  
                    }  
                    fos.close();  
                }  
            }  
        }  
        catch (ZipException e)
        {  
            e.printStackTrace();  
        }  
        catch (IOException e)  
        {  
            e.printStackTrace();  
        }  
        finally  
        {  
            try  
            {  
                if(null != is)  
                {  
                    is.close();  
                }  
                if(null != fos)  
                {  
                    fos.close();  
                }  
            }  
            catch (IOException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }  
      
    /** 
     * @param args 
     */  
    public static void main(String[] args)  
    {  
        //zip(new String[] {"D:/tmp/20150418/logs/file/2015-08-28/debug-log.log","D:/tmp/20150418/logs/file/2015-08-28/error-log.log"}, "D:/tmp/20150418/logs/file/2015-08-28/test.zip");
        //unzip("D://tmep.zip", "D:/temp/");

        Map<String, Object> modelMap = new HashMap<>(); //封装两个参数作为一个对象传入进去
        String templatePagePath = "D://temp/home.vm.html"; //本地静态页面地址
        String staticRoot = "D://temp/";
        modelMap.put("staticRoot", staticRoot);
        modelMap.put("courseTypes", null);
        VelocityUtils.staticByTemplate(modelMap,"D://temp/home.vm",templatePagePath); //进行页面静态化
    }  
}  

页面静态化工具

package cn.wangningbo.hrm.util;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

import java.io.*;
import java.util.Properties;

public class VelocityUtils {
    private static Properties p = new Properties();
    static {
        p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
        p.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
        p.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
        p.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8");
    }
    
    /**
     * 返回通过模板,将model中的数据替换后的内容
     * @param model
     * @param templateFilePathAndName
     * @return
     */
    public static String getContentByTemplate(Object model, String templateFilePathAndName){
        try {
            Velocity.init(p);
            Template template = Velocity.getTemplate(templateFilePathAndName);
            VelocityContext context = new VelocityContext();
            context.put("model", model);
            StringWriter writer = new StringWriter();
            template.merge(context, writer);
            String retContent = writer.toString();
            writer.close();
            return retContent;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 根据模板,静态化model到指定的文件 模板文件中通过访问model来访问设置的内容
     * 
     * @param model
     *            数据对象
     * @param templateFilePathAndName
     *            模板文件的物理路径
     * @param targetFilePathAndName
     *            目标输出文件的物理路径
     */
    public static void staticByTemplate(Object model, String templateFilePathAndName, String targetFilePathAndName) {
        try {
            Velocity.init(p);
            Template template = Velocity.getTemplate(templateFilePathAndName);
            
            VelocityContext context = new VelocityContext();
            context.put("model", model);
            FileOutputStream fos = new FileOutputStream(targetFilePathAndName);
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"));// 设置写入的文件编码,解决中文问题
            template.merge(context, writer);
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 静态化内容content到指定的文件
     * 
     * @param content
     * @param targetFilePathAndName
     */
    public static void staticBySimple(Object content, String targetFilePathAndName) {
        VelocityEngine ve = new VelocityEngine();
        ve.init(p);
        String template = "${content}";
        VelocityContext context = new VelocityContext();
        context.put("content", content);
        StringWriter writer = new StringWriter();
        ve.evaluate(context, writer, "", template);
        try {
            FileWriter fileWriter = new FileWriter(new File(targetFilePathAndName));
            fileWriter.write(writer.toString());
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

ServiceImpl

package cn.wangningbo.hrm.service.impl;


import cn.wangningbo.hrm.client.FastDFSClient;
import cn.wangningbo.hrm.client.RedisClient;
import cn.wangningbo.hrm.config.RabbitmqConstants;
import cn.wangningbo.hrm.domain.PageConfig;
import cn.wangningbo.hrm.domain.Pager;
import cn.wangningbo.hrm.domain.Site;
import cn.wangningbo.hrm.dto.CourseTypeDto;
import cn.wangningbo.hrm.mapper.PageConfigMapper;
import cn.wangningbo.hrm.mapper.PagerMapper;
import cn.wangningbo.hrm.mapper.SiteMapper;
import cn.wangningbo.hrm.service.IPageConfigService;
import cn.wangningbo.hrm.util.VelocityUtils;
import cn.wangningbo.hrm.util.ZipUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import feign.Response;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.IOUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author wangningbo
 * @since 2019-09-09
 */
@Service
public class PageConfigServiceImpl extends ServiceImpl<PageConfigMapper, PageConfig> implements IPageConfigService {
    @Autowired
    private PagerMapper pagerMapper;
    @Autowired
    private FastDFSClient fastDFSClient;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private PageConfigMapper pageConfigMapper;
    @Autowired
    private SiteMapper siteMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void staticPage(String dataKey, String pageName) {
        //一、页面静态化
        // 根据name去数据库中查询name记录,获取到第一个
        Pager pager = pagerMapper.selectList(new EntityWrapper<Pager>().eq("name", pageName)).get(0);
        // 获取到fastdfs中模板压缩包的路径
        String templateUrl = pager.getTemplateUrl();
        // 获取要执行的模板文件
        String templateName = pager.getTemplateName();
        // 下载fastdfs上面的模板压缩包
        Response response = fastDFSClient.download(templateUrl);
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            //获得输入流资源
            inputStream = response.body().asInputStream();
            // 所有静态化中数据都写入操作系统的临时目录(这个可以跨操作系统,而且临时目录的东西会自动维护,无需手动删除)
            String tempdir = System.getProperty("java.io.tmpdir");
            System.out.println(tempdir + "-------------------------------临时目录");
            // 要下载压缩包的路径
            String zipPath = tempdir + "/temp.zip";
            // 要解压到的路径
            String unZipPath = tempdir + "/temp/";
            // 获得输出流资源
            fileOutputStream = new FileOutputStream(zipPath);
            // 保存到本地 //import org.apache.commons.io.IOUtils;
            IOUtils.copy(inputStream, fileOutputStream); //保存到本地

            // 解压缩
            ZipUtil.unzip(zipPath, unZipPath);
            // 获取到模板 //模板路径 temp/home.vm
            String templatePath = unZipPath + "/" + templateName;
            System.out.println(templatePath + "-----------------------------------模板路径");
            // 获取到生成生成静态页面的路径 //本地静态页面的地址
            String templatePagePath = templatePath + ".html";
            System.out.println(templatePagePath + "----------------------------------本地静态页面的地址");

            // 生成页面需要的数据 //从redis中获取
            String courseTypes = redisClient.get("courseTypes");
            // json字符串转list
            List<CourseTypeDto> courseTypeDtos = JSONArray.parseArray(courseTypes, CourseTypeDto.class);
            // 封装两个参数成一个对象传入进去
            HashMap<String, Object> map = new HashMap<>();
            map.put("staticRoot", unZipPath);
            map.put("courseTypes", courseTypeDtos);

            // 进行页面静态化
            VelocityUtils.staticByTemplate(map, templatePath, templatePagePath);
            // 传递到fastdfs
            String pageUrl = fastDFSClient.upload(
                    new CommonsMultipartFile(createFileItem(new File(templatePagePath), "file")));

            // 二 PageConfig 并且进行保存
            PageConfig config = new PageConfig();
            config.setTemplateUrl(templateUrl);
            config.setTemplateName(templateName);
            config.setDataKey(dataKey);
            config.setPhysicalPath(pager.getPhysicalPath());
            config.setDfsType(0L); //0表示fastDfs
            config.setPageUrl(pageUrl);
            config.setPageId(pager.getId());
            pageConfigMapper.insert(config);

            // 三、往消息队列放入消息
            String routingKey = siteMapper
                    .selectList(new EntityWrapper<Site>().eq("id", pager.getSiteId())).get(0).getSn();
            System.out.println(routingKey + "----------------------------routingKey");
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("fileSysType", 0);
            jsonObject.put("staticPageUrl", pageUrl);
            jsonObject.put("physicalPath", pager.getPhysicalPath());
            System.out.println(jsonObject.toJSONString() + "---------------------------------------------jsonObject.toJSONString()");
            rabbitTemplate.convertAndSend(
                    RabbitmqConstants.EXCHANGE_DIRECT_INFORM, routingKey, jsonObject.toJSONString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 创建FileItem
     *
     * @param file
     * @param filedName
     * @return
     */
    private FileItem createFileItem(File file, String filedName) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        FileItem item = factory.createItem(filedName, "text/plain", true, file.getName());
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        try {
            FileInputStream fis = new FileInputStream(file);
            OutputStream os = item.getOutputStream();
            while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return item;
    }
}

2.1.2 页面静态化调用

2.1.2.1 初始化

IService

    /**
     *初始化课程站点主页
     */
    void InitCourseSiteIndex();

ServiceImpl

    @Override
    public void InitCourseSiteIndex() {
        //1 准备模板,并且上传fastdfs
        //2存放数据到redis
        List<CourseType> courseTypes = queryTypeTree(0L);
        String dataKey = "courseTypes";
        redisClient.set(dataKey, JSONArray.toJSONString(courseTypes));
        //3调用静态化接口产生静态页面,并且放入fastdfs
        String pageName = "CourseIndex";
        //本来应该通过PageName获取page后设置pageconfig传递,由于数据在查询端,还不如直接传入pageName到那边查询.
        Map<String,String> map = new HashMap<>();
        map.put("dataKey",dataKey);
        map.put("pageName",pageName);
        pageConfigClient.staticPage(map);
        //4往消息队列放一个消息,让pageAgent来下载静态页面
    }

2.1.2.2 feign文件上传与下载

参考文档:Feign文件上传与下载.md
链接:http://note.youdao.com/noteshare?id=b3a62850f8b259a0fbc14b40b46288b7&sub=08937A6337FD44DBBB3BF9C4C1839436

2.2 页面发布消费方

新建公共模块抽取常量

导包

        <!--spirngboot集成rabbitmq-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

抽取常量

package cn.wangningbo.hrm.config;

/**
 * 抽取常量
 */
public class RabbitmqConstants {
    // 交换机常量
    public static final String EXCHANGE_DIRECT_INFORM = "exchange_direct_inform";
    // 队列常量
    public static final String QUEUE_INFORM_PAGESTATIC = "queue_inform_pageStatic";
}

3. 课程列表页

3.1 需求

3.1.1 入口

  1. 主页里面关键字搜索
  2. 主页里面通过类型导航过去.

3.1.2 需求分析

  1. 主页里面关键字搜索

    • 分页查询+排序 select name from t_user where name like xxx order by limit 0,10
  2. 主页里面通过类型导航过去.

    • 有面包屑
    • 有品牌
    • 分页查询+排序 select name from t_user where name like xxx order by limit 0,10

3.2 实现

3.2.1 主页跳转到列表页

分析:

  1. 主页携带参数跳转到列表页.
    location.href = list.html?keyword=xx
    location.href = list.html?productType=154
  2. 列表页,加载完毕后,获取传递过来keyword ,或者productType
    • keyword
      • ①回显搜索框
      • ②以keyword为搜索条件到es中查询分页数据,展示在界面
    • productType
      • ①发送获取面包屑请求,展示在界面
      • ②发送品牌请求获取品牌,展示在界面
      • ②以productType为搜索条件到es中查询分页数据,展示在界面

实现:

<script type="text/javascript">
          new Vue({
              el:"#myBody",
              data(){
                  return {
                      queryParams:{
                          "keyword":'',
                          "productTye":null
                      }
                  }
              },
              methods:{

              },
              mounted(){

                  //获取路径中值赋值给keyword,keyword在做一个双向绑定
                  //http://127.0.0.1:6002/list.html?productType=102
                  let url = location.href;
                  url = url.substr(url.indexOf("?")+1);//productType=102
                  var tmps = url.split("="); //[producttype,102]
                  var paramName = tmps[0]; //keyword productType
                  var paramValue = tmps[1];
                  //动态属性
                  this.queryParams[paramName] = paramValue;
                  console.log(this.queryParams,"jjjjjjjjjjjjjj")

              }
          });
</script>

3.2.2 类型所独有面包屑实现

  1. 结构分析

根据当前类型获取层级关系,并且每一级都有自己及其兄弟. List<Map<Sring,Object>>
每一级有一个Map表示,里面有两个值,一个是ProductType ower;一个是List<ProductType> otherProductTypes.

  1. 后台接口

配置跨域

        config.addAllowedOrigin("http://127.0.0.1:6002");
        config.addAllowedOrigin("http://localhost:6002");

controller

    /**
     *  通过类型查询面包屑数据
     *     有层次(Node): path
     *     Node: 自己和兄弟  path里面就是自己 通过自己查询父亲,再通过父亲找到儿子,删除自己就ok
     * @return
     */
    @RequestMapping(value = "/crumbs",method = RequestMethod.GET)
    public List<Map<String,Object>> getCrumbs(Long courseTypeId){
        return courseTypeService.getCrumbs(courseTypeId);
    }

IService

    List<Map<String,Object>> getCrumbs(Long courseTypeId);

ServiceImpl

    @Override
    public List<Map<String, Object>> getCrumbs(Long courseTypeId) {
        List<Map<String,Object>> result = new ArrayList<>();
        //1 获取path 1.2.3
        CourseType courseType = courseTypeMapper.selectById(courseTypeId);
        String path = courseType.getPath();

        //2 截取path中各个节点自己  1 2 3
        String[] paths = path.split("\\.");

        //3 获取自己节点兄弟封装Map,放入List中进行返回
        for (String ownerIdStr : paths) {
            Map<String,Object> map = new HashMap<>();

            Long ownerId = Long.valueOf(ownerIdStr);

            System.out.println(ownerId);
            //获取每个自己
            CourseType owner =  courseTypeMapper.selectById(ownerId);
            map.put("owner",owner);
            //查询兄弟
            //获取父亲所有儿子
            List<CourseType> allChildren = courseTypeMapper
                    .selectList(new EntityWrapper<CourseType>().eq("pid",owner.getPid()));

            //干掉自己-边遍历边操作(正删改),要用迭代器
            Iterator<CourseType> iterator = allChildren.iterator();
            while (iterator.hasNext()){
                CourseType currentType = iterator.next();
                if (currentType.getId().longValue()==owner.getId().longValue()){
                    iterator.remove();
                    continue; //跳出当前循环
                }
            }
            map.put("otherCourseTypes",allChildren);
            result.add(map);
        }
        return result;
    }

3.2.3 分页列表+高级查询+排序展示功能

es的client

    /**
     * 从es中查询
     * @param params
     * @return
     */
    @PostMapping("/query")
    PageList<Map<String,Object>> query(@RequestBody Map<String, Object> params);

HystrixFallbackFactory先不做处理

            @Override
            public PageList<Map<String, Object>> query(Map<String, Object> params) {
                return null;
            }

es的controller

    @PostMapping("/query")
    PageList<Map<String, Object>> query(@RequestBody Map<String, Object> params) {
        return esCourseService.query(params);
    }

es的IService

    PageList<Map<String,Object>> query(Map<String, Object> params);

es的ServiceImpl

    @Override
    public PageList<Map<String, Object>> query(Map<String, Object> params) {
        // keyword CourseyType brandId priceMin priceMax sortField sortType page rows
        String keyword = (String) params.get("keyword"); //查询
        String sortField = (String) params.get("sortField"); //排序
        String sortType = (String) params.get("sortType");//排序

        Long courseType = params.get("CourseType") != null ? Long.valueOf(params.get("CourseType").toString()) : null;//过滤
        Long priceMin = params.get("priceMin") != null ? Long.valueOf(params.get("priceMin").toString()) * 100 : null;//过滤
        Long priceMax = params.get("priceMax") != null ? Long.valueOf(params.get("priceMax").toString()) * 100 : null;//过滤
        Long page = params.get("page") != null ? Long.valueOf(params.get("page").toString()) : null; //分页
        Long rows = params.get("rows") != null ? Long.valueOf(params.get("rows").toString()) : null;//分页

        //构建器
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //设置查询条件=查询+过滤
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (StringUtils.isNotBlank(keyword)) {
            boolQuery.must(QueryBuilders.matchQuery("all", keyword));
        }
        List<QueryBuilder> filter = boolQuery.filter();
        if (courseType != null) { //类型
            filter.add(QueryBuilders.termQuery("courseTypeId", courseType));
        }
        //最大价格 最小价格
        //minPrice <= priceMax && maxPrice>=priceMin
        if (priceMax != null && priceMin != null) {
            filter.add(QueryBuilders.rangeQuery("price").gte(priceMin).lte(priceMax));
        }
        builder.withQuery(boolQuery);
        //排序
        SortOrder defaultSortOrder = SortOrder.DESC;
        if (StringUtils.isNotBlank(sortField)) {//销量 新品 价格 人气 评论
            //如果传入的不是降序改为升序
            if (StringUtils.isNotBlank(sortType) && !sortType.equals(SortOrder.DESC)) {
                defaultSortOrder = SortOrder.ASC;
            }
            // 价格  索引库有两个字段 最大,最小
            //如果用户按照升序就像买便宜的,就用最小价格,如果用户按照降序想买贵的,用最大价格
            if (sortField.equals("jg")) {
                builder.withSort(SortBuilders.fieldSort("price").order(defaultSortOrder));
            }
        }
        //分页
        Long pageTmp = page - 1; //从0开始
        builder.withPageable(PageRequest.of(pageTmp.intValue(), rows.intValue()));
        //截取字段 @TODO
        //封装数据
        Page<ESCourse> CourseDocs = courseRepository.search(builder.build());
        List<Map<String, Object>> datas = esCourses2ListMap(CourseDocs.getContent());
        return new PageList<>(CourseDocs.getTotalElements(), datas);
    }

    /**
     * 数据转换
     *
     * @param content
     * @return
     */
    private List<Map<String, Object>> esCourses2ListMap(List<ESCourse> content) {
        List<Map<String, Object>> result = new ArrayList<>();
        for (ESCourse esCourse : content) {
            result.add(esCourse2Map(esCourse));
        }
        return result;
    }

    private Map<String, Object> esCourse2Map(ESCourse esCourse) {
        Map<String, Object> result = new HashMap<>();
        result.put("id", esCourse.getId());
        result.put("name", esCourse.getName());
        result.put("users", esCourse.getUsers());
        result.put("courseTypeId", esCourse.getCourseTypeId());
        result.put("courseTypeName", esCourse.getCourseTypeName());
        result.put("gradeId", esCourse.getGradeId());
        result.put("gradeName", esCourse.getGradeName());
        result.put("status", esCourse.getStatus());
        result.put("tenantId", esCourse.getTenantId());
        result.put("tenantName", esCourse.getTenantName());
        result.put("userId", esCourse.getUserId());
        result.put("userName", esCourse.getUserName());
        result.put("startTime", esCourse.getStartTime());
        result.put("endTime", esCourse.getEndTime());
        result.put("expires", esCourse.getExpires());
        result.put("priceOld", esCourse.getPriceOld());
        result.put("price", esCourse.getPrice());
        result.put("intro", esCourse.getIntro());
        result.put("qq", esCourse.getQq());
        result.put("resources", esCourse.getResources());
        return result;
    }

高级搜索的controller

    @PostMapping("/queryCourses")
    public PageList<Map<String,Object>> queryCourses(@RequestBody Map<String,Object> query){
        return courseService.queryCourses(query);
    }

IService

    PageList<Map<String,Object>> queryCourses(Map<String, Object> query);

ServiceImpl

    /**
     * 高级搜索
     * @param query
     * @return
     */
    @Override
    public PageList<Map<String, Object>> queryCourses(Map<String, Object> query) {
        return esCourseClient.query(query);
    }
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容