SpringCloud分布式新闻项目实战 - 8.App端文章搜索

咖啡不用冲,你一定会成功

在这里插入图片描述



系列文章目录

  1. 项目搭建
  2. App登录及网关
  3. App文章
  4. 自媒体平台(博主后台)
  5. 自媒体文章审核
  6. 延迟任务
  7. kafka及文章上下架
  8. App端文章搜索
  9. 后台系统管理
  10. Long类型精度丢失问题
  11. 定时计算热点文章(xxl-Job)
  12. 热点文章-实时计算(kafkaStream)
  13. 项目部署_持续集成(Jenkins)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


文章目录




一、需求分析

1. 页面

在这里插入图片描述


2. 功能

  • 文章搜索:
    • ElasticSearch环境搭建
    • 索引库创建
    • 文章搜索多条件复合查询
    • 索引数据同步
  • 搜索历史记录:
    • Mongodb环境搭建
    • 异步保存搜索历史
    • 查看搜索历史列表
    • 删除搜索历史
  • 联想词查询:
    • 联想词的来源
    • 联想词功能实现




二、文章搜索

1.ElasticSearch环境搭建

⑴. docker

# 列出本地镜像
docker images
# 拉取镜像(已完成)
docker pull elasticsearch:7.4.0
# 创建容器(已完成)
docker run -id --name elasticsearch -d --restart=always -p 9200:9200 -p 9300:9300 -v /usr/share/elasticsearch/plugins:/usr/share/elasticsearch/plugins -e "discovery.type=single-node" elasticsearch:7.4.0
# 列出容器
docker ps

⑵. 配置中文分词器 ik

ik资源链接: https://pan.baidu.com/s/177KvX2WULUJuLdWd-_Xhzw?pwd=abcd

#切换目录
cd /usr/share/elasticsearch/plugins
#新建目录
mkdir analysis-ik
cd analysis-ik
# 查看目录内容
ls
# 将本地文件上传(上传上面的ik资源包)
rz
#解压文件
unzip elasticsearch-analysis-ik-7.4.0.zip
# 无提示强制删除目录
rm -fr elasticsearch-analysis-ik-7.4.0.zip
# 查看目录内容
ls
# 列出容器
docker ps
# 重新启动单个服务
docker restart [ID]

⑶. postman

在这里插入图片描述


2.需求分析

  • 用户输入关键可搜索文章列表
  • 关键词高亮显示
  • 文章列表展示与home展示一样,当用户点击某一篇文章,可查看文章详情
    在这里插入图片描述

为了加快检索的效率,在查询的时候不会直接从数据库中查询文章,需要在elasticsearch中进行高速检索。
在这里插入图片描述


3.创建索引和映射

put请求添加映射: http://192.168.200.130:9200/app_info_article

{
    "mappings":{
        "properties":{
            "id":{
                "type":"long"
            },
            "publishTime":{
                "type":"date"
            },
            "layout":{
                "type":"integer"
            },
            "images":{
                "type":"keyword",
                "index": false
            },
            "staticUrl":{
                "type":"keyword",
                "index": false
            },
            "authorId": {
                "type": "long"
            },
            "authorName": {
                "type": "text"
            },
            "title":{
                "type":"text",
                "analyzer":"ik_smart"
            },
            "content":{
                "type":"text",
                "analyzer":"ik_smart"
            }
        }
    }
}

在这里插入图片描述

  • GET请求查询映射: http://192.168.200.130:9200/app_info_article
  • DELETE请求,删除索引及映射: http://192.168.200.130:9200/app_info_article
  • GET请求,查询所有文档: http://192.168.200.130:9200/app_info_article/_search

4.初始化索引数据库

⑴. 导入模块

es包资源链接: https://pan.baidu.com/s/1zsr6nBaXTXIK-WYBSPBFaQ?pwd=abcd
解压至 heima-leadnews-test 目录下,Maven导入pom依赖
在这里插入图片描述


⑵.将文章导入es索引库

编辑 heima-leadnews\heima-leadnews-test\es-init\src\test\java\com\heima\es\ApArticleTest.java 文件:

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 注意:数据量的导入,如果数据量过大,需要分页导入
     * @throws Exception
     */
    @Test
    public void init() throws Exception {

        // 1. 查询所有符合条件的文章数据
        List<SearchArticleVo> searchArticleVos = apArticleMapper.loadArticleList();

        // 2. 批量导入到es索引库
        BulkRequest bulkRequest = new BulkRequest("app_info_article");

        for (SearchArticleVo searchArticleVo : searchArticleVos) {
            IndexRequest indexRequest = new IndexRequest().id(searchArticleVo.getId().toString())
                    .source(JSON.toJSONString(searchArticleVo), XContentType.JSON);

            //批量添加数据
            bulkRequest.add(indexRequest);
        }
        restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }

⑶.postman

在这里插入图片描述


⑷.踩坑经验

jackson-dataformat-smile-2.11.4.jar时出错; error in opening zip file

如上报错,是因为 jackson-dataformat 文件无法使用,需要重新下载替换,jackson官网地址: https://repo1.maven.org/maven2/com/fasterxml/jackson/dataformat/


5.功能实现

⑴. 搭建搜索微服务

①. 导入文章搜索微服务

es包资源链接: https://pan.baidu.com/s/140PjICtNC9_d5dK0MMQU0Q?pwd=abcd
解压至 heima-leadnews-service 目录下,Maven导入pom依赖
在这里插入图片描述

②. pom依赖

编辑 leadnews\heima-leadnews\heima-leadnews-service\pom.xml 文件:

	<!--elasticsearch-->
	<dependency>
	    <groupId>org.elasticsearch.client</groupId>
	    <artifactId>elasticsearch-rest-high-level-client</artifactId>
	    <version>7.4.0</version>
	</dependency>
	<dependency>
	    <groupId>org.elasticsearch.client</groupId>
	    <artifactId>elasticsearch-rest-client</artifactId>
	    <version>7.4.0</version>
	</dependency>
	<dependency>
	    <groupId>org.elasticsearch</groupId>
	    <artifactId>elasticsearch</artifactId>
	    <version>7.4.0</version>
	</dependency>

编辑 leadnews\heima-leadnews\heima-leadnews-service\pom.xml 文件:

    <modules>
...
        <!--文章搜索模块-->
        <module>heima-leadnews-search</module>
    </modules>

...
    <dependencies>
		<!--elasticsearch-->
		<dependency>
		    <groupId>org.elasticsearch.client</groupId>
		    <artifactId>elasticsearch-rest-high-level-client</artifactId>
		    <version>7.4.0</version>
		</dependency>
		<dependency>
		    <groupId>org.elasticsearch.client</groupId>
		    <artifactId>elasticsearch-rest-client</artifactId>
		    <version>7.4.0</version>
		</dependency>
		<dependency>
		    <groupId>org.elasticsearch</groupId>
		    <artifactId>elasticsearch</artifactId>
		    <version>7.4.0</version>
		</dependency>
    </dependencies>

③. Nacos

新建 leadnews-search 文件:

spring:
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
elasticsearch:
  host: 192.168.200.130
  port: 9200

在这里插入图片描述


⑵. 接口定义

①. 说明
说明
接口路径/api/v1/article/search/search
请求方式POST
参数UserSearchDto
响应结果ResponseResult

②. DTO

新建 heima-leadnews\heima-leadnews-model\src\main\java\com\heima\model\search\dtos\UserSearchDto.java 文件:

@Data
public class UserSearchDto {

    /**
    * 搜索关键字
    */
    String searchWords;
    /**
    * 当前页
    */
    int pageNum;
    /**
    * 分页条数
    */
    int pageSize;
    /**
    * 最小时间
    */
    Date minBehotTime;

    public int getFromIndex(){
        if(this.pageNum<1)return 0;
        if(this.pageSize<1) this.pageSize = 10;
        return this.pageSize * (pageNum-1);
    }
}

③. ResponseResult

在这里插入图片描述


⑶. 基础搭建

①. Controller

新建 heima-leadnews\heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\controller\v1\ArticleSearchController.java 文件:

@RestController
@RequestMapping("/api/v1/article/search")
public class ArticleSearchController {

    @PostMapping("/search")
    public ResponseResult search(@RequestBody UserSearchDto dto) {
        return null;
    }
}

②. Service

新建 heima-leadnews\heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\ArticleSearchService.java 文件:

public interface ArticleSearchService {

    /**
     * es文章分页搜索
     * @param dto
     * @return
     */
    ResponseResult search(UserSearchDto dto);
}

③. ServiceImpl

新建 heima-leadnews\heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\impl\ArticleSearchServiceImpl.java 文件:

@Service
@Slf4j
public class ArticleSearchServiceImpl implements ArticleSearchService {

    /**
     * es文章分页搜索
     * @param dto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto dto) {

        // 1. 检查参数

        // 2. 设置查询条件

        // 2.1 关键词的分词之后查询

        // 2.2 查询小于mindata的数据

        // 2.3 分页查询

        // 2.4 按照发布时间倒序查询

        // 2.5 设置高亮 title

        // 3. 结果封装返回

        return null;
    }
}

⑷. 逻辑处理

①. ServiceImpl

编辑 heima-leadnews\heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\impl\ArticleSearchServiceImpl.java 文件:

    @Autowired
    private RestHighLevelClient restHighLevelClient;
...
        // 1. 检查参数
        if(dto == null || StringUtils.isBlank(dto.getSearchWords())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 2. 设置查询条件
        SearchRequest searchRequest = new SearchRequest("app_info_article");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 2.1 布尔查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 2.2 关键词的分词之后查询
        QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(dto.getSearchWords()).field("title").field("content").defaultOperator(Operator.OR);
        boolQueryBuilder.must(queryStringQueryBuilder);

        // 2.3 查询小于mindata的数据
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime().getTime());
        boolQueryBuilder.filter(rangeQueryBuilder);

        // 2.4 分页查询
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(dto.getPageSize());

        // 2.5 按照发布时间倒序查询
        searchSourceBuilder.sort("publishTime", SortOrder.DESC);

        // 2.6 设置高亮 title
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        // 3. 结果封装返回
        List<Map> list = new ArrayList<>();

        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            String json = hit.getSourceAsString();
            Map map = JSON.parseObject(json, Map.class);
            //处理高亮
            if(hit.getHighlightFields() != null && hit.getHighlightFields().size() > 0){
                Text[] titles = hit.getHighlightFields().get("title").getFragments();
                String title = StringUtils.join(titles);
                //高亮标题
                map.put("h_title",title);
            }else {
                //原始标题
                map.put("h_title",map.get("title"));
            }
            list.add(map);
        }

        return ResponseResult.okResult(list);
    }

②. Controller

编辑 heima-leadnews\heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\controller\v1\ArticleSearchController.java 文件:

    public ResponseResult search(@RequestBody UserSearchDto dto) throws IOException {
        return articleSearchService.search(dto);
    }

③. Nacos

编辑 leadnews-app-gateway 文件:

        #搜索微服务
        - id: leadnews-search
          uri: lb://leadnews-search
          predicates:
            - Path=/search/**
          filters:
            - StripPrefix= 1

在这里插入图片描述


⑸. 测试

启动 UserApplicationAppGatewayApplicationArticleApplicationSearchApplication,以及 Nginx,打开 自媒体端地址 http://localhost:8802
在这里插入图片描述

6.新增文章创建索引

⑴. 思路分析

在这里插入图片描述

  • 文章微服务
    • 文章审核成功使用kafka发送消息
    • 文章微服务是消息的生产者
  • 搜索微服务
    • 搜索微服务接收消息,添加数据到索引库
    • 搜索微服务是消息的消费者

⑵. 文章微服务(生产者发送消息)

①.vo

新建 heima-leadnews\heima-leadnews-model\src\main\java\com\heima\model\search\vos\SearchArticleVo.java 文件:

@Data
public class SearchArticleVo {

    /**
     * 文章id
     */
    private Long id;

    /**
     * 文章标题
     */
    private String title;

    /**
     * 文章发布时间
     */
    private Date publishTime;

    /**
     * 文章布局
     */
    private Integer layout;

    /**
     * 封面
     */
    private String images;

    /**
     * 作者id
     */
    private Long authorId;

    /**
     * 作者名词
     */
    private String authorName;

    /**
     * 静态url
     */
    private String staticUrl;

    /**
     * 文章内容
     */
    private String content;

}

②.常量

编辑 heima-leadnews\heima-leadnews-common\src\main\java\com\heima\common\constants\ArticleConstants.java 文件:

    public static final String ARTICLE_ES_SYNC_TOPIC = "article.es.sync.topic";

    public static final Integer HOT_ARTICLE_LIKE_WEIGHT = 3;
    public static final Integer HOT_ARTICLE_COMMENT_WEIGHT = 5;
    public static final Integer HOT_ARTICLE_COLLECTION_WEIGHT = 8;

    public static final String HOT_ARTICLE_FIRST_PAGE = "hot_article_first_page_";

③.实现类

编辑 heima-leadnews\heima-leadnews-service\heima-leadnews-article\src\main\java\com\heima\article\service\impl\ArticleFreemarkerServiceImpl.java 文件:

            //发送消息,创建索引
            createArticleESIndex(apArticle,content,path);
        }
    }

    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    /**
     * 送消息,创建索引
     * @param apArticle
     * @param content
     * @param path
     */
    private void createArticleESIndex(ApArticle apArticle, String content, String path) {
        SearchArticleVo vo = new SearchArticleVo();
        BeanUtils.copyProperties(apArticle,vo);
        vo.setContent(content);
        vo.setStaticUrl(path);

        kafkaTemplate.send(ArticleConstants.ARTICLE_ES_SYNC_TOPIC, JSON.toJSONString(vo));
    }

④.Nacos

编辑 leadnews-article 文件:

kafka:
    bootstrap-servers: 192.168.200.130:9092
    producer:
      retries: 10
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer

⑶. 搜索微服务(消费者接收消息)

①.监听

新建 heima-leadnews\heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\listener\SyncArticleListener.java 文件:

@Component
@Slf4j
public class SyncArticleListener {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @KafkaListener(topics = ArticleConstants.ARTICLE_ES_SYNC_TOPIC)
    public void onMessage(String message) {
        if (StringUtils.isNotBlank(message)) {

            log.info("SyncArticleListener,message={}",message);

            SearchArticleVo searchArticleVo = JSON.parseObject(message, SearchArticleVo.class);
            IndexRequest indexRequest = new IndexRequest("app_info_article");
            indexRequest.id(searchArticleVo.getId().toString());
            indexRequest.source(message, XContentType.JSON);
            try {
                restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
                log.error("sync es error={}",e);
            }
        }
    }
}
②.Nacos

编辑 leadnews-search 文件:

spring:
  kafka:
    bootstrap-servers: 192.168.200.130:9092
    consumer:
      group-id: ${spring.application.name}
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

⑷. 测试

启动 UserApplicationAppGatewayApplicationArticleApplicationWemediaGatewayAplicationScheduleApplicationWemediaApplicationSearchApplication,以及 Nginx,打开 自媒体端地址 http://localhost:8802 、App端地址 http://localhost:8801

发布文章:
在这里插入图片描述
在这里插入图片描述


搜索文章:
在这里插入图片描述





三、搜索历史记录

1.需求分析

在这里插入图片描述

  • 展示用户的搜索记录10条,按照搜索关键词的时间倒序
  • 可以删除搜索记录
  • 保存历史记录,保存10条,多余的则删除最久的历史记录

2.MongoDB

⑴. 数据存储说明

在这里插入图片描述
用户的搜索记录,需要给每一个用户都保存一份,数据量较大,要求加载速度快,通常这样的数据存储到mongodb更合适,不建议直接存储到关系型数据库中

⑵. 安装

# 拉取镜像
docker pull mongo
# 创建容器
docker run -di --name mongo-service --restart=always -p 27017:27017 -v ~/data/mongodata:/data mongo

连接MongoDB:
在这里插入图片描述


⑶. 集成

①. 导入模块

MongoDeMo包链接: https://pan.baidu.com/s/1KX1ShX_UCy3NPx5CQkhvLg?pwd=abcd
解压至 heima-leadnews-test 目录下,Maven导入pom依赖
在这里插入图片描述

②. pom依赖(已完成)

heima-leadnews-test\mongo-demo\pom.xml 文件:

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

③. 配置

heima-leadnews-test\mongo-demo\src\main\resources\application.yml 文件:

server:
  port: 9998
spring:
  data:
    mongodb:
      host: 192.168.200.130
      port: 27017
      database: leadnews-history

④. 映射(已完成)

heima-leadnews-test\mongo-demo\src\main\java\com\itheima\mongo\pojo\ApAssociateWords.java 文件:

/**
 * <p>
 * 联想词表
 * </p>
 *
 * @author itheima
 */
@Data
@Document("ap_associate_words")
public class ApAssociateWords implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;

    /**
     * 联想词
     */
    private String associateWords;

    /**
     * 创建时间
     */
    private Date createdTime;

}

⑤. 核心方法(已完成)

heima-leadnews-test\mongo-demo\src\test\java\com\itheima\mongo\test\MongoTest.java 文件:

@SpringBootTest(classes = MongoApplication.class)
@RunWith(SpringRunner.class)
public class MongoTest {


    @Autowired
    private MongoTemplate mongoTemplate;

    //保存
    @Test
    public void saveTest(){
        ApAssociateWords apAssociateWords = new ApAssociateWords();
        apAssociateWords.setAssociateWords("新闻头条");
        apAssociateWords.setCreatedTime(new Date());
        mongoTemplate.save(apAssociateWords);
    }

    //查询一个
    @Test
    public void saveFindOne(){
        ApAssociateWords apAssociateWords = mongoTemplate.findById("63db6eeca0a7867f34fc8128", ApAssociateWords.class);
        System.out.println(apAssociateWords);
    }

    // 保存多个
    @Test
    public void saveListTest(){
        for (int i = 0; i < 9; i++) {
            ApAssociateWords apAssociateWords = new ApAssociateWords();
            apAssociateWords.setAssociateWords("新闻头条");
            apAssociateWords.setCreatedTime(new Date());
            mongoTemplate.save(apAssociateWords);
        }
    }

    //条件查询
    @Test
    public void testQuery(){
        Query query = Query.query(Criteria.where("associateWords").is("新闻头条"))
                .with(Sort.by(Sort.Direction.DESC,"createdTime"));
        List<ApAssociateWords> apAssociateWordsList = mongoTemplate.find(query, ApAssociateWords.class);
        System.out.println(apAssociateWordsList);
    }

    // 删除
    @Test
    public void testDel(){
        mongoTemplate.remove(Query.query(Criteria.where("associateWords").is("新闻头条")),ApAssociateWords.class);
    }
}

⑥.执行测试类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


3.思路步骤

异步存储搜索关键词,更快获取搜索数据
在这里插入图片描述
用户输入关键字进行搜索的异步记录关键字:
在这里插入图片描述

4.功能实现

⑴. 集成MongoDB

①.pom依赖

heima-leadnews-test\mongo-demo\pom.xml 文件:

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

②.Nacos

编辑 leadnews-search 文件:

spring:
  data:
   mongodb:
    host: 192.168.200.130
    port: 27017
    database: leadnews-history

③.初始化数据库

sql资源链接: https://pan.baidu.com/s/18bjGwSGZlumMSicvteG34g?pwd=abcd
在这里插入图片描述

⑵. 保存用户搜索历史记录

①.实体类

新建 heima-leadnews-test\mongo-demo\pom.xml 文件:

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

①.Service

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\ApUserSearchService.java 文件:

public interface ApUserSearchService {

    /**
     * 保存用户搜索历史记录
     * @param keyword
     * @param userId
     */
    public void insert(String keyword, Integer userId);
}

②.ServiceImpl

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\impl\ApUserSearchServiceImpl.java 文件:

public class ApUserSearchServiceImpl implements ApUserSearchService {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 保存用户搜索历史记录
     *
     * @param keyword
     * @param userId
     */
    @Override
    public void insert(String keyword, Integer userId) {

        // 1. 查询用户搜索的关键词
        Query query = Query.query(Criteria.where("userId").is(userId).and("keyword").is(keyword));
        ApUserSearch apUserSearch = mongoTemplate.findOne(query, ApUserSearch.class);

        // 2. 存在, 更新时间
        if (apUserSearch != null) {
            apUserSearch.setCreatedTime(new Date());
            mongoTemplate.save(apUserSearch);
            return;
        }

        // 3. 不存在,判断当前历史记录总数量是否超过10
        apUserSearch = new ApUserSearch();
        apUserSearch.setUserId(userId);
        apUserSearch.setKeyword(keyword);
        apUserSearch.setCreatedTime(new Date());

        // 判断当前历史记录总数量是否超过10
        Query userQuery = Query.query(Criteria.where("userId").is(userId));
        userQuery.with(Sort.by(Sort.Direction.DESC, "createdTime"));
        List<ApUserSearch> apUserSearchList = mongoTemplate.find(userQuery, ApUserSearch.class);

        if (apUserSearchList == null || apUserSearchList.size() < 10) {
            mongoTemplate.save(apUserSearch);
        } else {
            ApUserSearch lastUserSearch = apUserSearchList.get(apUserSearchList.size() - 1);
            mongoTemplate.findAndReplace(Query.query(Criteria.where("id").is(lastUserSearch.getId())), apUserSearch);
        }
    }
}

⑶. 获取当前用户

①.存储用户信息

编辑 heima-leadnews-gateway\heima-leadnews-app-gateway\src\main\java\com\heima\app\gateway\filter\AuthorizeFilter.java 文件:

        //5.判断token是否有效
        try {
            Claims claimsBody = AppJwtUtil.getClaimsBody(token);
            //是否是过期
            int result = AppJwtUtil.verifyToken(claimsBody);
            if(result == 1 || result  == 2){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }

            // 网关: 进行token解析后,把解析后的用户信息存储到header中
            // 获取用户信息
            Object userId = claimsBody.get("id");

            // 存放到header中
            ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
                httpHeaders.add("userId", userId + "");
            }).build();

            // 重置请求
            exchange.mutate().request(serverHttpRequest);

②.工具类

新建 heima-leadnews-utils\src\main\java\com\heima\utils\thread\AppThreadLocalUtils.java 文件:

public class AppThreadLocalUtils {

    private final static ThreadLocal<ApUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();

    /**
     * 存入线程中
     */
    public static void setUser(ApUser apUser){
        WM_USER_THREAD_LOCAL.set(apUser);
    }

    /**
     * 从线程中获取
     */
    public static ApUser getUser(){
        return WM_USER_THREAD_LOCAL.get();
    }

    /**
     * 清理
     */
    public static void clear(){
        WM_USER_THREAD_LOCAL.remove();
    }
}

③.处理器

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\interceptor\AppTokenInterceptor.java 文件:

public class AppTokenInterceptor implements HandlerInterceptor {

    /**
     * 前置处理器: 得到hedaer中的用户信息, 并且存入到当前线程中
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取当前用户
        String userId = request.getHeader("userId");
        if(userId != null) {
            // 存入到当前线程中
            ApUser apUser = new ApUser();
            apUser.setId(Integer.valueOf(userId));
            AppThreadLocalUtils.setUser(apUser);
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //AppThreadLocalUtils.clear();
    }

    /**
     * 后置处理器: 清理线程中的数据
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AppThreadLocalUtils.clear();
    }
}

④.自定义拦截器

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\config\WebMvcConfig.java 文件:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 添加自定义拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AppTokenInterceptor()).addPathPatterns("/**");
    }
}

⑷. 异步调用

①.调用方法

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\impl\ArticleSearchServiceImpl.java 文件:

        // 1. 检查参数
        if(dto == null || StringUtils.isBlank(dto.getSearchWords())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 异步调用 保存用户搜索记录
        ApUser user = AppThreadLocalUtils.getUser();
        if (user != null && dto.getFromIndex() == 0) {
            apUserSearchService.insert(dto.getSearchWords(), user.getId());
        }

②.异步调用

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\impl\ApUserSearchServiceImpl.java 文件:

    @Async
    public void insert(String keyword, Integer userId) {

③.启动类

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\SearchApplication.java 文件:

@EnableAsync

⑸. 测试

启动 UserApplicationAppGatewayApplicationArticleApplicationSearchApplication,以及 Nginx,打开 App端地址 http://localhost:8801

搜索(保存用户搜索记录):
在这里插入图片描述
在这里插入图片描述


搜索(替换搜索记录):
在这里插入图片描述
在这里插入图片描述

搜索(更新已存在的搜索记录):
在这里插入图片描述
在这里插入图片描述





四、搜索历史列表

1.查询搜索历史列表

⑴.接口定义

说明
接口路径/api/v1/history/load
请求方式POST
参数
响应结果ResponseResult

⑵.功能实现

①.Controller

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\controller\v1\ApUserSearchController.java 文件:

@RestController
@RequestMapping("/api/v1/history")
public class ApUserSearchController {

    /**
     * 查询搜索历史列表
     * @return
     */
    @PostMapping("/load")
    public ResponseResult findUserSearch() {
        return null;
    }
}

②.Service

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\ApUserSearchService.java 文件:

    /**
     * 查询搜索历史列表
     * @return
     */
    public ResponseResult findUserSearch();

③.ServiceImpl

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\impl\ApUserSearchServiceImpl.java 文件:

    /**
     * 查询搜索历史列表
     * @return
     */
    @Override
    public ResponseResult findUserSearch() {
        // 1. 获取用户信息
        ApUser user = AppThreadLocalUtils.getUser();

        // 2. 判断用户是否登录
        if(user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        // 3. 时间倒序查询数据
        List<ApUserSearch> apUserSearchList = mongoTemplate.find(Query.query(Criteria.where("userId").is(user.getId())).with(Sort.by(Sort.Direction.DESC, "createdTime")), ApUserSearch.class);
        return ResponseResult.okResult(apUserSearchList);
    }

④.Controller

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\controller\v1\ApUserSearchController.java 文件:

    @Autowired
    private ApUserSearchService apUserSearchService;

    /**
     * 查询搜索历史列表
     * @return
     */
    @PostMapping("/load")
    public ResponseResult findUserSearch() {
        return apUserSearchService.findUserSearch();
    }

⑶.测试

启动 UserApplicationAppGatewayApplicationArticleApplicationSearchApplication,以及 Nginx,打开 App端地址 http://localhost:8801
在这里插入图片描述


2.删除搜索记录

⑴.接口定义

说明
接口路径/api/v1/history/del
请求方式POST
参数HistorySearchDto
响应结果ResponseResult

DTO:
新建 heima-leadnews-model\src\main\java\com\heima\model\search\dtos\HistorySearchDto.java 文件:

@Data
public class HistorySearchDto {
    /**
    * 接收搜索历史记录id
    */
    String id;
}

⑵.功能实现

①.Controller

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\controller\v1\ApUserSearchController.java 文件:

    /**
     * 删除搜索记录
     */
    @PostMapping("/del")
    public ResponseResult delUserSearch(@RequestBody HistorySearchDto dto) {
        return null;
    }

②.Service

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\ApUserSearchService.java 文件:

    /**
     * 删除搜索记录
     * @param dto
     * @return
     */
    public ResponseResult delUserSearch(HistorySearchDto dto);

③.ServiceImpl

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\impl\ApUserSearchServiceImpl.java 文件:

    /**
     * 删除搜索记录
     * @param dto
     * @return
     */
    @Override
    public ResponseResult delUserSearch(HistorySearchDto dto) {

        // 1. 检查参数
        if (dto.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 2. 判断是否登录
        ApUser user = AppThreadLocalUtils.getUser();
        if(user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        // 3. 删除搜索记录
        mongoTemplate.remove(Query.query(Criteria.where("userId").is(user.getId()).and("id").is(dto.getId())), ApUserSearch.class);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

④.Controller

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\controller\v1\ApUserSearchController.java 文件:

    /**
     * 删除搜索记录
     */
    @PostMapping("/del")
    public ResponseResult delUserSearch(@RequestBody HistorySearchDto dto) {
        return apUserSearchService.delUserSearch(dto);
    }

⑶.测试

启动 UserApplicationAppGatewayApplicationArticleApplicationSearchApplication,以及 Nginx,打开 App端地址 http://localhost:8801
在这里插入图片描述
在这里插入图片描述




五、联想词查询

1.需求分析

⑴.需求说明

  • 根据用户输入的关键字展示联想词
    在这里插入图片描述

⑵.实体类

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\pojos\ApAssociateWords.java 文件:

/**
 * <p>
 * 联想词表
 * </p>
 *
 * @author itheima
 */
@Data
@Document("ap_associate_words")
public class ApAssociateWords implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;

    /**
     * 联想词
     */
    private String associateWords;

    /**
     * 创建时间
     */
    private Date createdTime;

}

⑶.数据来源

通常是网上搜索频率比较高的一些词,通常在企业中有两部分来源:

  • 自己维护搜索词: 通过分析用户搜索频率较高的词,按照排名作为搜索词
  • 第三方获取: 关键词规划师(百度)、5118关键词库、爱站网

ap_associate_word: 添加关键词数据
在这里插入图片描述


2.功能实现

⑴.接口定义

说明
接口路径/api/v1/associate/search
请求方式POST
参数UserSearchDto
响应结果ResponseResult

DTO:

@Data
public class UserSearchDto {

    /**
     * 搜索关键字
     */
    String searchWords;
    /**
     * 当前页
     */
    int pageNum;
    /**
     * 分页条数
     */
    int pageSize;
    /**
     * 最小时间
     */
    Date minBehotTime;

    public int getFromIndex() {
        if (this.pageNum < 1) return 0;
        if (this.pageSize < 1) this.pageSize = 10;
        return this.pageSize * (pageNum - 1);
    }
}

⑵.功能实现

①.Controller

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\controller\v1\ApAssociateWordsController.java 文件:

@RestController
@RequestMapping("/api/v1/associate")
public class ApAssociateWordsController {

    @PostMapping("/search")
    public ResponseResult search(@RequestBody UserSearchDto userSearchDto) {
        return null;
    }
}

②.Service

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\ApAssociateWordsService.java 文件:

public interface ApAssociateWordsService {

    /**
     * 搜索联想词
     * @param userSearchDto
     * @return
     */
    public ResponseResult search(UserSearchDto userSearchDto);
}

③.ServiceImpl

新建 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\service\impl\ApAssociateWordsServiceImpl.java 文件:

@Service
public class ApAssociateWordsServiceImpl implements ApAssociateWordsService {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 搜索联想词
     * @param userSearchDto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto userSearchDto) {

        // 1. 校验参数
        if (userSearchDto == null || StringUtils.isBlank(userSearchDto.getSearchWords())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 2. 分页检查
        if(userSearchDto.getPageSize() > 20) {
            userSearchDto.setPageSize(20);
        }

        // 3. 模糊查询
        Query query = Query.query(Criteria.where("associateWords").regex(".*?\\" + userSearchDto.getSearchWords() + ".*"));
        query.limit(userSearchDto.getPageSize());
        List<ApAssociateWords> wordsList = mongoTemplate.find(query, ApAssociateWords.class);

        return ResponseResult.okResult(wordsList);
    }
}

④.Controller

编辑 heima-leadnews-service\heima-leadnews-search\src\main\java\com\heima\search\controller\v1\ApAssociateWordsController.java 文件:

    @Autowired
    private ApAssociateWordsService apAssociateWordsService;

    @PostMapping("/search")
    public ResponseResult search(@RequestBody UserSearchDto userSearchDto) {
        return apAssociateWordsService.search(userSearchDto);
    }

3.测试

启动 UserApplicationAppGatewayApplicationArticleApplicationSearchApplication,以及 Nginx,打开 App端地址 http://localhost:8801
在这里插入图片描述



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后海 0_o

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值