稻草问答-实现搜索功能

稻草问答-实现搜索功能

1 实现问题搜索功能

1.1 问题搜索功能

在稻草问答的首页上搜索框,就是问题搜索功能其大致原理如下:

  1. 将全部的Question数据导入到ES服务器中;
  2. 查询时候根据用户输入到KEY请求ES查询数据;
  3. 将查询数据利用VUE显示到界面上;
  4. 为了能够搜索到新问题,在创建新问题时候,将新问题同时存储到ES服务器。

由于需要进行远程调用,则需要将straw-search注册到Eureka,首先导入eureka-client包和straw-commons包:

<dependency>
    <groupId>cn.tedu</groupId>
    <artifactId>straw-commons</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

配置启动类,添加@EnableEurekaClient:

@SpringBootApplication
@EnableEurekaClient
public class StrawSearchApplication {

    public static void main(String[] args) {
        SpringApplication.run(StrawSearchApplication.class, args);
    }
}

配置application.properties:

service.port=8099
spring.application.name=search-service

eureka.instance.prefer-ip-address=false
eureka.instance.hostname=localhost
eureka.instance.ip-address=127.0.0.1
eureka.instance.instance-id=${spring.application.name}:${eureka.instance.hostname}:${server.port}

注册测试...

1.2 将数据库中的Question复制到Elasticsearch

根据上述学习的结果,我们要想完成搜索功能,首先需要在ES中创建问题索引,然后在才能进行问题的搜索。创建问题索引的步骤:

  1. 在straw-faq服务模块中添加Rest API方法,分页返回Question数据;

  2. straw-search项目中创建值对象QuestionVo封装需要保存到ES中的数据,在这个文件中标注Spring-Data注解;

  3. 在straw-search项目中创建数据导入方法,将数据导入到Elasticsearch中。为了快速将Question数据导入到ES中,我们分页从straw-faq获取Question数据,然后分批的存储到ES中。

    首先,straw-faq的业务层IQuestionService声明业务层接口方法;

    /**
     * 返回一页问题数据
     * @param pageNum 页号:从1开始
     * @param pageSize 页面行数
     * @return 一页数据
     */
PageInfo<Question> getQuestions(Integer pageNum, Integer pageSize);

实现业务层接口方法:

@Override
public PageInfo<Question> getQuestions(Integer pageNum, Integer pageSize) {
    PageHelper.startPage(pageNum,pageSize);
    List<Question> list=questionMapper.selectList(null);
    return new PageInfo<>(list);
}

编写测试案例进行测试:

@Test
void getQuestions(){
    PageInfo<Question> pageInfo=questionService.getQuestions(1,10);
    pageInfo.getList().forEach(question -> log.debug("{}",question));
    log.debug("{}",pageInfo);
}

编写控制器方法:

/*
 * Rest API用于将Question数据导到straw-search中
 * 分页导出数据,这个方法用于返回数据页数
 * @Param pageSuze 页面大小
*/
@GetMapping("/page/count")
public Integer pageCount(Integer pageSize){
    Integer rows=questionService.count();
    return rows%pageSize==0 ? rows/pageSize:rows/pageSize+1;
}

/*
 * Rest API用于将Question数据导到straw-search中
 * 分页导出数据,这个方法用于返回一页数据
 * @param pageNum页号,从1开始的页号
 * @Param pageSuze 页面大小
 * @return 返回一页数据
 */
@GetMapping("/page")
public List<Question> page(Integer pageNum, Integer pageSize){
    PageInfo<Question> pageInfo=
        questionService.getQuestions(pageNum,pageSize);
    return pageInfo.getList();
}

在straw-search服务模块添加值对象QuestionVo:

package cn.tedu.straw.search.vo;

import cn.tedu.straw.commons.model.Tag;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 *用于封装存储到Elasticsearch中的问题数据
 * </p>
 *
 * @author tedu.cn
 * @since 2021-07-29
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Document(indexName = "questions")
public class QuestionVo implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 已经发布待解决
     * 老师已经回复,正在解决中
     * 已经接受答案,已经解决问题
     */
    public static final Integer POSTED=0;
    public static final Integer SOLVING=1;
    public static final Integer SOLVED=2;

    @Id
    private Integer id;

    /**
     * 问题的标题
     */
    @Field(type= FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_smart")
    private String title;

    /**
     * 提问内容
     */
    @Field(type= FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_smart")
    private String content;

    /**
     * 提问者用户名
     */
    @Field(type=FieldType.Keyword)
    private String userNickName;

    /**
     * 提问者id
     */
    @Field(type=FieldType.Integer)
    private Integer userId;

    /**
     * 创建时间
     * 时间类型必须指定格式format,否则不能保存
     */
    @Field(type=FieldType.Date,format= DateFormat.basic_date_time)
    private LocalDateTime createtime;

    /**
     * 修改时间
     */
    @Field(type=FieldType.Date,format= DateFormat.basic_date_time)
    private LocalDateTime modifytime;

    /**
     * 状态,0-》未回答,1-》待解决,2-》已解决
     */
    @Field(type = FieldType.Integer)
    private Integer status;

    /**
     * 浏览量
     */
    @Field(type = FieldType.Integer)
    private Integer pageViews;

    /**
     * 该问题是否公开,所有学生都可见,0-》否,1-》是
     */
    @Field(type = FieldType.Integer)
    private Integer publicStatus;

    /**
     * 删除状态, 0-》否,1-》是
     */
    @Field(type = FieldType.Integer)
    private Integer deleteStatus;

    /**
     * 标签名列表
     */
    @Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_smart")
    private String tagNames;

    /**
     * 当前问题的标签列表
     * 不是数据库存储的数据,@TableField(exist=false)加以说明
     * 不是数据库存储的数据,标注@Transient注解以后,将不保存到ES数据库中
     */
    @Transient
    private List<Tag> tags;
}

编写QuestionRepository接口:

@Repository
public interface QuestionRepository extends
        ElasticsearchRepository<QuestionVo, Integer> {
}

添加业务层IQuestionService,定义同步方法

public interface IQuestionService {
    /**
     * 同步方法,将straw-faq服务模块中的信息读取出来
     * 然后保存到当前straw-search中的Elasticsearch的数据库中
     */
    void sync();
}

由于需要使用Ribbon调用straw-faq中的问题数据,所以需要声明RestTemplate对象:

@SpringBootApplication
@EnableEurekaClient
public class StrawSearchApplication {

    public static void main(String[] args) {
        SpringApplication.run(StrawSearchApplication.class, args);
    }
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

实现业务层,实现同步方法,采用分页方法批量复制:

@Service
@Slf4j
public class QuestionServiceImpl implements IQuestionService {
    @Resource
    private QuestionRepository questionRepository;

    @Resource
    private RestTemplate restTemplate;
    @Override
    public void sync() {
        //先获取总页数
        String url="http://faq-service/v1/questions/page/count?pageSize={1}";
        int pageSize=10;
        Integer pages=restTemplate.getForObject(url, Integer.class, pageSize);
        for (int i=1; i<=pages; i++){
            //每次读取一页数据
            url="http://faq-service/v1/questions/page?pageNum={1}&pageSize={2}";
            QuestionVo[] questions=restTemplate.getForObject(
                url, QuestionVo[].class, i, pageSize
            );
            //将读取到的一页数据存储到Elasticsearch中
            questionRepository.saveAll(Arrays.asList(questions));
            log.debug("保存 page {}", i);
        }
    }
}

测试,执行测试案例以后就已将数据复制到ES中了:

@SpringBootTest
@Slf4j
public class QuestionServiceTests {
    @Resource
    IQuestionService iQuestionService;

    @Test
    void sync(){
        iQuestionService.sync();
    }
}

1.3 编写搜索业务层方法

使用关键字进行搜索:

Elasticseatch搜索:

  • must:必须的,就是并且关系
  • should:应该的,就是或者关系.
  • match:内容匹配
  • term:完全相等
  • bool:布尔,套在must、shoul外面

使用Rest客户端测试Query语句:

###条件搜索,查询用户12或者公开的 同时标题或者内容中包含Java的问题
    POST http://localhost:9200/questions/_search
Content-Type: application/json
{
    "query": {
        "bool": {
            "must": [{
                    "bool": {
                        "should": [ {
                                "match": {"title": "java"
                                }},
                            {"match": {"content": "java"}}]
                    }
                },{
                    "bool": {
                        "should": [{"term": {"publicStatus": 1}},{"term": {"userId": 12}}]
                 }
            }]
        }
    }
}

编写搜索问题数据层方法,将上述查询query的值部分复制到@Query注解中,然后将参数替换为占位符?0,?1, ?2,参数占位符个数要与方法参数个数对应,执行查询时候Spring会自动将占位符替换为参数,搜索结果往往非常多,所以采用Spring进行分页查询:

package cn.tedu.straw.search.mapper;

import cn.tedu.straw.search.vo.QuestionVo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface QuestionRepository extends
    ElasticsearchRepository<QuestionVo, Integer> {

    @Query(" {\n" +
           "\"bool\": {\n" +
           "\"must\": [\n" +
           "  {\n" +
           "    \"bool\": {\n" +
           "      \"should\": [\n" +
           "        {\n" +
           "          \"match\": {\n" +
           "            \"title\": \"?0\"\n" +
           "          }\n" +
           "        },\n" +
           "        {\n" +
           "          \"match\": {\n" +
           "            \"content\": \"?1\"\n" +
           "          }\n" +
           "        }\n" +
           "      ]\n" +
           "    }\n" +
           "  },{\n" +
           "  \"bool\": {\n" +
           "  \"should\": [{\"term\": {\"publicStatus\": 1}},{\"term\": {\"userId\": ?2}}]\n" +
           "  }\n" +
           "\n" +
           "}]\n" +
           "}\n" +
           "}")
    public Page<QuestionVo> queryAllByParams(String title, String content, Integer userId, Pageable pageable);
}

测试:

package cn.tedu.straw.search;

import cn.tedu.straw.search.mapper.QuestionRepository;
import cn.tedu.straw.search.vo.QuestionVo;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import javax.annotation.Resource;

@SpringBootTest
@Slf4j
public class QuestionRepositoryTests {
    @Resource
    private QuestionRepository questionRepository;

    @Test
    void queryAllByParams(){
        Pageable pageable= PageRequest.of(0,3);
        Page<QuestionVo> page=questionRepository.queryAllByParams(
                "编程", "编程", 11, pageable
        );
        page.getContent().forEach(questionVo -> log.debug("{}",questionVo));
    }
}

编写将Spring翻页转换为PageHelper翻页。PageHelper更适合与VUE配合显示界面,而Spring提供的翻页组件与VUE配合显示不是很方便,顾设计一个方法将Spring翻页组件转换为PageHelper翻页组件,这样就可以做到统一处理界面了。 如下是转换方法,这个方法可以作为工具类使用,由于只是转换子对应的数据规则,没有必要逐句掌握代码:

package cn.tedu.straw.search.service;

import com.github.pagehelper.PageInfo;
import org.springframework.data.domain.Page;

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

public class Pages {
    /**
         * 将Spring-Data提供的翻页数据,转换为Pagehelper翻页数据对象
         * @param page Spring-Data提供的翻页数据
         * @return PageInfo
         */
    public static <T> PageInfo<T> pageInfo(Page<T> page){
        //当前页号从1开始, Spring-Data从0开始,所以要加1
        int pageNum = page.getNumber()+1;
        //当前页面大小
        int pageSize = page.getSize();
        //总页数 pages
        int pages = page.getTotalPages();
        //当前页面中数据
        List<T> list = new ArrayList<>(page.toList());
        //当前页面实际数据大小,有可能能小于页面大小
        int size = page.getNumberOfElements();
        //当前页的第一行在数据库中的行号, 这里从0开始
        int startRow = page.getNumber()*pageSize;
        //当前页的最后一行在数据库中的行号, 这里从0开始
        int endRow = page.getNumber()*pageSize+size-1;
        //当前查询中的总行数
        long total = page.getTotalElements();

        PageInfo<T> pageInfo = new PageInfo<>(list);
        pageInfo.setPageNum(pageNum);
        pageInfo.setPageSize(pageSize);
        pageInfo.setPages(pages);
        pageInfo.setStartRow(startRow);
        pageInfo.setEndRow(endRow);
        pageInfo.setSize(size);
        pageInfo.setTotal(total);
        pageInfo.calcByNavigatePages(PageInfo.DEFAULT_NAVIGATE_PAGES);

        return pageInfo;
    }
}

在straw-search中添加依赖的组件,Pagehelper:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
</dependency>

添加业务层lQuestionSerice中声明查询方法:

PageInfo<QuestionVo> search(String key, String username, Integer pageNum,
                            Integer pageSize);

实现查询方法:

private User getUser(String username){
    String url="http://sys-service/v1/auth/user?username={1}";
    User user=restTemplate.getForObject(url, User.class, username);
    return user;
}

@Override
public PageInfo<QuestionVo> search(String key, String username, Integer pageNum,
                                   Integer pageSize) {
    if (pageNum==null){
        pageNum=1;
    }
    if(pageSize==null){
        pageSize=8;
    }
    int page=pageNum-1;
    int size=pageSize;
    Pageable pageable= PageRequest.of(page,size, Sort.Direction.DESC,"createtime");

    User user=getUser(username);

    Page<QuestionVo> questions=questionRepository.queryAllByParams(key, key, user.getId(), pageable);
    return Pages.pageInfo(questions);
}

测试:

@Test
void search(){
    String username="st2";
    String key="编程";
    PageInfo<QuestionVo> pageInfo=iQuestionService.search(key, username, 1, 8);
    pageInfo.getList().forEach(questionVo -> log.debug("{}",questionVo));
    log.debug("{}",pageInfo);
}

1.4 实现首页搜索功能

搜索时候需要搜索当前用户的问题信息,而当前用户信息需要利用Session共享获得,所以首先配置Session共享,导入包:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

配置session共享application.properties:

spring.session.store-type=redis
spring.redis.host=localhost
spring.redis.port=6379

在StrawSearchApplication中启用Redis保存Session:

@SpringBootApplication
@EnableEurekaClient
@EnableRedisHttpSession
public class StrawSearchApplication {

    public static void main(String[] args) {
        SpringApplication.run(StrawSearchApplication.class, args);
    }
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

配置SpringSecurity:

package cn.tedu.straw.search.security;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests().anyRequest().permitAll();
    }
}

迁移ExceptionControllerAdvice,统一处理控制器异常。编写控制器:

@RestController
@RequestMapping("/v1/questions")
@Slf4j
public class QuestionController {

    @Resource
    private IQuestionService questionService;

    /**
     * 请求路径:http://localhost:9000/search/v1/questions
     */
    @PostMapping
    public R<PageInfo<QuestionVo>> search(String key, Integer pageNum,
                                          @AuthenticationPrincipal UserDetails userDetails){
        if (key==null){
            key="";
        }
        if (pageNum==null){
            pageNum=1;
        }
        int pageSize=8;
        PageInfo<QuestionVo> pageInfo=questionService.search(
            key, userDetails.getUsername(), pageNum, pageSize);
        return R.ok(pageInfo);
    }
}

在straw-gateway中配置zuul路由规则:

zuul.routes.search.path=/search/**
zuul.routes.search.service-id=search-service
zuul.routes.search.sensitive-headers=Authorization

在straw-gateway项目中创建搜索界面,重构首页模板index.html,使用Thymeleaf定义搜索框片段:

<div class="form-inline my-2 my-lg-0" id="searchApp" th:fragment="searchApp" >
    <input class="form-control form-control-sm mr-sm-2 rounded-pill" type="search" placeholder="Search" aria-label="Search" v-model="key" >
    <button class="btn btn-sm btn-outline-secondary my-2 my-sm-0 rounded-pill" type="button" v-on:click="search" ><i class="fa fa-search" aria-hidden="true"></i></button>
</div>

编写vue视图模型search_app.js:

let searchApp=new Vue({
    el:"#searchApp",
    data:{
        key:""
    },
    methods:{
        search:function () {
            //利用URL地址栏 传递搜索关键字,地址栏必须编码
            location.href = "/search.html?key=" + encodeURI(this.key);
        }
    },
    created:function () {
        let key=location.search;
        //检查地址栏上是否?key=
        if(key && key.startsWith("?key=")){
            //截取key=后面的信息
            key =decodeURI(key.substring("?key=".length));
            this.key=key;
        }
    }
});

测试......
复制index_teacher.html为搜索模板search.html,在HomeCotoller中添加转发方法,显示搜索页面:

@GetMapping("/search.html")
public String search(){
    return "search";
}

重构search.html,使用thymeleaf复用index.html中的searchApp段落:

<div class="form-inline my-2 my-lg-0" id="searchApp" th:replace="index::searchApp">
    <input class="form-control form-control-sm mr-sm-2 rounded-pill" v-model="keyword" type="search" placeholder="Search" aria-label="Search">
    <button class="btn btn-sm btn-outline-secondary my-2 my-sm-0 rounded-pill" type="button"><i class="fa fa-search" aria-hidden="true"></i></button>
</div>

复制index_teacher.js为search.js重构:

let questionsApp = new Vue({
    el:'#questionsApp',
    data:{
      questions:[],
        pageInfo:{},
    },
    methods:{
        loadQuestions:function(pageNum){
            if(! pageNum){
                pageNum =1;
            }
            let key=location.search;
            if(! key){
                return;
            }
            if (! key.startsWith("?key=")){
                return;
            }
            key=decodeURI(key.substring("?key=".length));
            $.ajax({
               url: '/search/v1/questions',
                method: "POST",
                data:{
                   key:key,
                   pageNum:pageNum
                },
                success:function(r){
                   console.log("成功加载数据");
                   console.log(r);
                   if (r.code === OK){
                       questionsApp.questions=r.data.list;
                       questionsApp.pageInfo=r.data;
                       //为question对象添加持续时间属性
                       questionsApp.updateDuration();
                       questionsApp.updateTagImage();
                   }
                }
            });
        },
        updateTagImage:function (){
            let questions=this.questions;
            for(let i=0; i<questions.length; i++){
                let tags=questions[i].tags;
                if(tags){
                    let tagImage='/img/tags/'+tags[0].id+'.jpg';
                    console.log(tagImage);
                    questions[i].tagImage=tagImage;
                }
            }
        },
        updateDuration:function () {
            let questions = this.questions;
            for(let i=0; i<questions.length; i++){
                //创建问题时候的时间毫秒数
                addDuration(questions[i]);
            }
        }
    },
    created:function(){
        console.log("执行了方法");
        this.loadQuestions(1);
    }
});

测试......

重构所有包含搜索框的页面..
上述案例中没有显示每个问题的配图,其原因是我们的配图是后期利用tag进行关联的,就和显示问题时候一样, 将显示问题与tag进行关联,显示时候就能显示对应的配图了,具体做法是在业务层中进行将问题的tags属性添加上。

在straw-faq项目TagController中添加Rest接口,返回全部tag信息:

    /**
     * 请求URL: /v1/tags/list
     */
@GetMapping("/list")
public List<Tag> list(){
    return tagService.getTags();
}

重构straw-search中QuestionServicelmp的搜索方法,利用Ribbon获取标签列表,然后为搜索结果添加tags属性:

@Override
public PageInfo<QuestionVo> search(String key, String username, Integer pageNum,
                                   Integer pageSize) {
    if (pageNum==null){
        pageNum=1;
    }
    if(pageSize==null){
        pageSize=8;
    }
    int page=pageNum-1;
    int size=pageSize;
    Pageable pageable= PageRequest.of(page,size, Sort.Direction.DESC,"createtime");

    User user=getUser(username);

    Page<QuestionVo> questions=questionRepository.queryAllByParams(key, key, user.getId(), pageable);

    Map<String,Tag> name2TagMap=getName2TagMap();

    for (QuestionVo question : questions.getContent()){
        List<Tag> tags=tagNameToTag(question.getTagNames());
        question.setTags(tags);
    }

    return Pages.pageInfo(questions);
}
private final Map<String, Tag> name2TagMap =new ConcurrentHashMap<>();
private Map<String, Tag> getName2TagMap(){
    if(name2TagMap.isEmpty()){
        string url = "http://faq-service/v1/tags/list"; 
        Tag[] tags = restTemplate.getForobject(url, Tag[].class); 
        for(Tag tag:tags){
            name2TagMap.put(tag.getName(),tag);
        }
    }
    return name2TagMap;
}

private List<Tag> tagNameToTag(String tagNames) {
    Map<String, Tag> tagMap=getTagMap();
    String[] names=tagNames.split(",\\s?");
    List<Tag> tags=new ArrayList<>();
    for (String name: names){
        Tag tag=tagMap.get(name);
        tags.add(tag);
    }
    return tags;
}

1.5 显示配图和标签列表

搜索功能实现了,但是不能显示配图,原因QuestionVO的tags属性是空的, 填上值就行了。首先利用Ribbon回去标签列表。

在straw-faq的TagCotoller控制器上添加获取tag列表功能:

    /**
     * 请求URL: /v1/tags/list
     */
@GetMapping("/list")
public List<Tag> list(){
    return tagService.getTags();
}

然后在straw-search的业务层QuestionServicelmpl中添加本地缓存:

//缓存标签数据
private final ConcurrentHashMap<String, Tag> tagMap=new ConcurrentHashMap<>();

@Scheduled(initialDelay = 1000*60*60, fixedRate = 1000*6*60)
private void clearTagMap(){
    tagMap.clear();
}

private Map<String, Tag> getTagMap() {
    if(tagMap.isEmpty()) {
        synchronized (tagMap) {
            if (tagMap.isEmpty()) {
                String url = "http://faq-service/v1/tags/list";
                Tag[] tags = restTemplate.getForObject(url, Tag[].class);
                for (Tag tag : tags) {
                    tagMap.put(tag.getName(), tag);
                }
            }
        }
    }
    return tagMap;
}

在straw-search的启动类上开启定时机会功能@EnableScheduling:

@SpringBootApplication
@EnableEurekaClient
@EnableRedisHttpSession
@EnableScheduling
public class StrawSearchApplication {

    public static void main(String[] args) {
        SpringApplication.run(StrawSearchApplication.class, args);
    }
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

重构straw-search的业务层QuestionServicelmpl搜索方法,将tags填充值:

@Override
public PageInfo<QuestionVo> search(String key, String username, Integer pageNum,
                                   Integer pageSize) {
    if (pageNum==null){
        pageNum=1;
    }
    if(pageSize==null){
        pageSize=8;
    }
    int page=pageNum-1;
    int size=pageSize;
    Pageable pageable= PageRequest.of(page,size, Sort.Direction.DESC,"createtime");

    User user=getUser(username);

    Page<QuestionVo> questions=questionRepository.queryAllByParams(key, key, user.getId(), pageable);

    for (QuestionVo question : questions.getContent()){
        List<Tag> tags=tagNameToTag(question.getTagNames());
        question.setTags(tags);
    }

    return Pages.pageInfo(questions);
}

private List<Tag> tagNameToTag(String tagNames) {
    Map<String, Tag> tagMap=getTagMap();
    String[] names=tagNames.split(",\\s?");
    List<Tag> tags=new ArrayList<>();
    for (String name: names){
        Tag tag=tagMap.get(name);
        tags.add(tag);
    }
    return tags;
}

重新启动,进行测试。

一个字体图标无法显示的错误,有时候由于Maven在部署复制字体文件时候会出现问题,造成字体无法显示的现象,解决方案是在Maven的pom文件中添加plugin,告诉Maven在编译时候放过字体文件。

重构straw-gateway的pom文件:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <configuration>
                <nonFilteredFileExtensions>
                    <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                    <nonFilteredFileExtension>woff</nonFilteredFileExtension>
                    <nonFilteredFileExtension>woff2</nonFilteredFileExtension>
                </nonFilteredFileExtensions>
            </configuration>
        </plugin>
    </plugins>
</build>
posted @ 2022-04-18 22:40  指尖上的未来  阅读(35)  评论(0)    收藏  举报