031 商品上架-增量同步和全量同步(cubemall-search模块)-LMLPHP

增量同步

1.功能分析

  1. 前端页面,点击"上架"链接,执行上架的动作
  2. 请求的url
    /product/spuinfo/{spuid}/up
    参数:
    spuid
    请求方法:
    post
    返回值:
    R(转换成json数据)
  3. product工程中
    controller:
    接收页面请求的参数
    调用service处理请求
    service:
    1接收controller传递过来的参数,spuId
    2根据spuId找到对应的商品修改商品状态
    修改tb_spuinfo表中的publish_status字段为1
    3需要把商品添加到索引库中,调用search工程的服务,实现ES索引库的添加
    4返回结果
  4. 服务之间调用使用OpenFeign技术
  5. Search工程中
    controller:
    接收请求的参数:spuId
    调用service商品添加到索引库
    Service:
    1.根据spuId查询对象的商品数据
    2.商品数据包含的字段 Entity中包含的字段
    3.使用ElasticSearchRepository对象将数据添加到索引库中
    4.返回结果

2.索引库中文档的结构
(1) 字段分析
tb_spu_info

  1. id
  2. spu_name
  3. spu_description
  4. category_id
  5. brand_id
  6. update_time

tb_category

  1. name

tb_brand

  1. name
  2. image

tb_spu_images

  1. img_url

tb_sku_info

  1. price 取最低价格

(2) SQL语句

全量同步

1.实现思路

  1. 把所有的商品数据查询出来
  2. 把商品数据导入到ES中
    2.dao层
    查询出所有上架状态的商品数据
        SELECT
            a.id,
            a.spu_name spuName,
            a.spu_description spuDescription,
            a.category_id categoryId,
            c.`name` categoryName,
            a.brand_id brandId,
            d.`name` brandName,
            d.image brandImage,
            a.update_time updateTime,
            b.img_url imgUrl,
            e.price
        FROM tb_spu_info a
                 left join (select * from tb_spu_images where default_img = 1) b on a.id=b.spu_id
                 left join tb_category c on a.category_id = c.id
                 left join tb_brand d on a.brand_id = d.id
                 left join (
            select spu_id, min(price) price from tb_sku_info GROUP BY spu_id
        ) e on a.id = e.spu_id
        WHERE a.publish_status=1

3.service层

  1. 把所有的商品数据查询出来
  2. 把商品数据导入到ES中

SpuInfoDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xd.cubemall.search.dao.SpuInfoDao">

	<!-- 可根据自己的需求,是否要使用 -->
    <resultMap type="com.xd.cubemall.search.entity.SpuInfoEntity" id="spuInfoMap">
        <result property="id" column="id"/>
        <result property="spuName" column="spu_name"/>
        <result property="spuDescription" column="spu_description"/>
        <result property="categoryId" column="category_id"/>
        <result property="brandId" column="brand_id"/>
        <result property="weight" column="weight"/>
        <result property="publishStatus" column="publish_status"/>
        <result property="createTime" column="create_time"/>
        <result property="updateTime" column="update_time"/>
    </resultMap>

    <select id="getSpuInfoById" parameterType="long" resultType="com.xd.cubemall.search.model.SpuInfo">
        SELECT
            a.id,
            a.spu_name spuName,
            a.spu_description spuDescription,
            a.category_id categoryId,
            c.`name` categoryName,
            a.brand_id brandId,
            d.`name` brandName,
            d.image brandImage,
            a.update_time updateTime,
            b.img_url imgUrl,
            e.price
        FROM tb_spu_info a
                 left join (select * from tb_spu_images where default_img = 1) b on a.id=b.spu_id
                 left join tb_category c on a.category_id = c.id
                 left join tb_brand d on a.brand_id = d.id
                 left join (
            select spu_id, min(price) price from tb_sku_info GROUP BY spu_id
        ) e on a.id = e.spu_id
        WHERE a.id=#{spuId}
    </select>

    <select id="getSpuInfoList" resultType="com.xd.cubemall.search.model.SpuInfo">
        SELECT
            a.id,
            a.spu_name spuName,
            a.spu_description spuDescription,
            a.category_id categoryId,
            c.`name` categoryName,
            a.brand_id brandId,
            d.`name` brandName,
            d.image brandImage,
            a.update_time updateTime,
            b.img_url imgUrl,
            e.price
        FROM tb_spu_info a
                 left join (select * from tb_spu_images where default_img = 1) b on a.id=b.spu_id
                 left join tb_category c on a.category_id = c.id
                 left join tb_brand d on a.brand_id = d.id
                 left join (
            select spu_id, min(price) price from tb_sku_info GROUP BY spu_id
        ) e on a.id = e.spu_id
        WHERE a.publish_status=1
    </select>


</mapper>


SpuInfo实体类

package com.xd.cubemall.search.model;

import lombok.Data;
import org.springframework.data.annotation.Id;
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.util.Date;

@Data
@Document(indexName = "goods_1", shards = 5, replicas = 1)
public class SpuInfo {
    @Id
    @Field(type = FieldType.Long)
    private Long id;
    @Field(type = FieldType.Text, store = true, analyzer = "ik_max_word")
    private String spuName;
    @Field(type = FieldType.Text, store = true, analyzer = "ik_max_word")
    private String spuDescription;
    @Field(type = FieldType.Long)
    private Long categoryId;
    @Field(type = FieldType.Keyword, store = true)
    private String categoryName;
    @Field(type = FieldType.Long)
    private Long brandId;
    @Field(type = FieldType.Keyword, store = true)
    private String brandName;
    @Field(type = FieldType.Keyword, store = true, index = false)
    private String brandImage;
    @Field(type = FieldType.Date, store = true, format = DateFormat.basic_date_time)
    private Date updateTime;
    @Field(type = FieldType.Keyword, store = true, index = false)
    private String imgUrl;
    @Field(type = FieldType.Double, store = true)
    private Double price;
}

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cube_goods?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        ip: 127.0.0.1
  application:
    name: cubemall-search
  elasticsearch:
    rest:
      uris:
        - 1.1.1.1:9200
        - 2.2.2.2:9200
        - 3.3.3.3:9200
server:
  port: 8082
mybatis-plus:
  mapper-locations: classpath:/mappers/*.xml
  global-config:
    db-config:
      id-type: auto
      logic-delete-value: 1 #逻辑删除值(默认为1)
      logic-not-delete-value: 0 #逻辑未删除值(默认为0)

logging:
  level:
    com.xd.cubemall: debug

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <artifactId>chubemall-search</artifactId>
    <packaging>jar</packaging>

    <name>chubemall-search</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
        <elasticsearch.version>7.10.2</elasticsearch.version>
    </properties>



    <dependencies>
<!--        <dependency>-->
<!--            <groupId>junit</groupId>-->
<!--            <artifactId>junit</artifactId>-->
<!--            <version>3.8.1</version>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>

        <!--引入common公共模块-->
        <dependency>
            <groupId>com.xd.cubemall</groupId>
            <artifactId>cubemall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>



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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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

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


        <!--阿里云OSS-->
        <!--        <dependency>-->
        <!--            <groupId>com.aliyun.oss</groupId>-->
        <!--            <artifactId>aliyun-sdk-oss</artifactId>-->
        <!--            <version>3.17.4</version>-->
        <!--        </dependency>-->


    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


        </dependencies>
    </dependencyManagement>
</project>


SpuInfoController.java

package com.xd.cubemall.search.controller;


import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.search.service.SpuInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(("/spuinfo"))
public class SpuInfoController {
    @Autowired
    private SpuInfoService spuInfoService;
    private volatile boolean executeFlag = false;

    @GetMapping("putonsale/{spuId}")
    public R putOnSale(@PathVariable("spuId") Long spuId){
        R r = spuInfoService.putOnSale(spuId);
        return r;
    }

    /**
     * 商品数据全量同步
     * @return
     */
    @GetMapping("/syncSpuInfo")
    public R syncSpuInfo() {
        if (!executeFlag) {
            synchronized (this) {
                if (!executeFlag) {
                    executeFlag = true;
                    R r = spuInfoService.syncSpuInfo();
                    return r;
                }
            }

        }
        return R.ok("数据正导入中,请勿重复执行");


    }

}

SpuInfoDao.java

package com.xd.cubemall.search.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xd.cubemall.search.entity.SpuInfoEntity;
import com.xd.cubemall.search.model.SpuInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * spu信息
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
@Mapper
public interface SpuInfoDao extends BaseMapper<SpuInfoEntity> {
    SpuInfo getSpuInfoById(Long spuId);
    List<SpuInfo> getSpuInfoList();
	
}

SpuInfoEntity.java

package com.xd.cubemall.search.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

/**
 * spu信息
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
@Data
@TableName("tb_spu_info")
public class SpuInfoEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 自增ID
	 */
	@TableId
	private Long id;
	/**
	 * spu名称
	 */
	private String spuName;
	/**
	 * spu描述
	 */
	private String spuDescription;
	/**
	 * 分类ID
	 */
	private Long categoryId;
	/**
	 * 品牌ID
	 */
	private Long brandId;
	/**
	 * 权重
	 */
	private BigDecimal weight;
	/**
	 * 发布状态
	 */
	private Integer publishStatus;
	/**
	 * 
	 */
	private Date createTime;
	/**
	 * 
	 */
	private Date updateTime;

}

SpuInfoRepository.java

package com.xd.cubemall.search.repository;

import com.xd.cubemall.search.model.SpuInfo;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface SpuInfoRepository extends ElasticsearchRepository<SpuInfo,Long> {
}

SpuInfoServiceImpl.java

package com.xd.cubemall.search.service.impl;

import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.search.dao.SpuInfoDao;
import com.xd.cubemall.search.model.SpuInfo;
import com.xd.cubemall.search.repository.SpuInfoRepository;
import com.xd.cubemall.search.service.SpuInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class SpuInfoServiceImpl implements SpuInfoService {

    @Autowired
    private SpuInfoDao spuInfoDao;
    @Autowired
    private SpuInfoRepository spuInfoRepository;

    @Override
    public R putOnSale(long spuId) {
//        1.根据spuId查询对象的商品数据
        SpuInfo spuInfo = spuInfoDao.getSpuInfoById(spuId);

//        2.商品数据包含的字段 Entity中包含的字段
//        3.使用ElasticSearchRepository对象将数据添加到索引库中
        spuInfoRepository.save(spuInfo);
//        4.返回结果
        return R.ok();
    }


    @Override
    public R syncSpuInfo() {
//        1) 把所有的商品数据查询出来
        List<SpuInfo> infoList = spuInfoDao.getSpuInfoList();

//        2) 把商品数据导入到ES中
        spuInfoRepository.saveAll(infoList);
        //返回结果
        return R.ok();
    }
}

CubemallSearchApplication.java

package com.xd.cubemall.search;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
@MapperScan("com.xd.cubemall.search.dao")
public class CubemallSearchApplication{
    public static void main(String[] args) {
        SpringApplication.run(CubemallSearchApplication.class);
    }
}
10-19 10:22