Loading

商品详情页生成

需求分析

当系统审核完成商品,需要将商品详情页进行展示,那么采用静态页面生成的方式生成,并部署到高性能的web服务器中进行访问是比较合适的。所以,开发流程如下图所示:

此处MQ我们使用Rabbitmq即可。

执行步骤解释:

  • 系统管理员(商家运维人员)修改或者审核商品的时候, 会更改数据库中商品上架状态并发送商品id给rabbitMq中的上架交换器
  • 上架交换器会将商品id发给静态页生成队列
  • 静态页微服务设置监听器, 监听静态页生成队列, 根据商品id获取商品详细数据并使用thymeleaf的模板技术生成静态页

商品静态化微服务创建

需求分析

该微服务只用于生成商品静态页,不做其他事情。

搭建项目

(1)在changgou-service下创建一个名称为changgou_service_page的项目,作为静态化页面生成服务

(2)changgou-service-page中添加起步依赖,如下

<dependencies>
    <dependency>
        <groupId>com.changgou</groupId>
        <artifactId>changgou_common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    <dependency>
        <groupId>com.changgou</groupId>
        <artifactId>changgou_service_goods_api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

(3)修改application.yml的配置

server:
  port: 9011
spring:
  application:
    name: page
  rabbitmq:
    host: 192.168.200.128
  main:
    allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: false
  client:
    config:
      default:   #配置全局的feign的调用超时时间  如果 有指定的服务配置 默认的配置不会生效
        connectTimeout: 600000 # 指定的是 消费者 连接服务提供者的连接超时时间 是否能连接  单位是毫秒
        readTimeout: 600000  # 指定的是调用服务提供者的 服务 的超时时间()  单位是毫秒
#hystrix 配置
hystrix:
  command:
    default:
      execution:
        timeout:
          #如果enabled设置为false,则请求超时交给ribbon控制
          enabled: true
        isolation:
          strategy: SEMAPHORE
# 生成静态页的位置
pagepath: D:\items

(4)创建系统启动类

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "com.changgou.goods.feign")
public class PageApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(PageApplication.class);
    }
}

生成静态页

需求分析

页面发送请求,传递要生成的静态页的商品的SpuID.后台controller 接收请求,调用thyemleaf的原生API生成商品静态页。

上图是要生成的商品详情页,从图片上可以看出需要查询SPU的3个分类作为面包屑显示,同时还需要查询SKU和SPU信息。

Feign创建

一会儿需要查询SPU和SKU以及Category,所以我们需要先创建Feign,修改changgou-service-goods-api,添加CategoryFeign,并在CategoryFeign中添加根据ID查询分类数据,代码如下:

@FeignClient(name = "goods")
public interface CategoryFeign {
​
    @GetMapping("/category/{id}")
    public Result<Category> findById(@PathVariable("id") Integer id);
}

在changgou-service-goods-api,添加SkuFeign,并添加根据SpuID查询Sku集合,代码如下:

@FeignClient(name = "goods")
public interface SkuFeign {
​
    @GetMapping("/sku/spu/{spuId}")
    public List<Sku> findSkuListBySpuId(@PathVariable("spuId") String spuId);
}

在changgou-service-goods-api,添加SpuFeign,并添加根据SpuID查询Spu信息,代码如下:

@FeignClient(name = "goods")
public interface SpuFeign {
​
    @GetMapping("/spu/findSpuById/{id}")
    public Result<Spu> findSpuById(@PathVariable("id") String id);
}

静态页生成代码

(1)创建PageService

public interface PageService {
​
    /**
     * 生成静态化页面
     * @param spuId
     */
    void generateItemPage(String spuId);
}

(2)创建PageServiceImpl

@Service
public class PageServiceImpl implements PageService {
​
    @Autowired
    private SpuFeign spuFeign;
​
    @Autowired
    private CategoryFeign categoryFeign;
​
    @Autowired
    private SkuFeign skuFeign;
​
    @Value("${pagepath}")
    private String pagepath;
​
    @Autowired
    private TemplateEngine templateEngine;
​
    @Override
    public void generateItemPage(String spuId) {
        //获取context对象,用于存放商品详情数据
        Context context = new Context();
        Map<String, Object> itemData = this.findItemData(spuId);
        context.setVariables(itemData);
        //获取商品详情页生成的指定位置
        File dir = new File(pagepath);
        //判断商品详情页文件夹是否存在,不存在则创建
        if (!dir.exists()){
            dir.mkdirs();
        }
        //定义输出流,进行文件生成
        File file = new File(dir+"/"+spuId+".html");
        Writer out = null;
        try{
            out = new PrintWriter(file);
            //生成文件
            /**
             * 1.模板名称
             * 2.context对象,包含了模板需要的数据
             * 3.输出流,指定文件输出位置
             */
            templateEngine.process("item",context,out);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭流
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
    }
​
    //获取静态化页面数据
    private Map<String, Object> findItemData(String spuId) {
​
        Map<String,Object> resultMap = new HashMap<>();
​
        //获取spu信息
        Result<Spu> spuResult = spuFeign.findSpuById(spuId);
        Spu spu = spuResult.getData();
        resultMap.put("spu",spu);
​
        //获取图片信息
        if (spu != null){
            if(!StringUtils.isEmpty(spu.getImages())){
                resultMap.put("imageList",spu.getImages().split(","));
            }
        }
        //获取分类信息
        Category category1 = categoryFeign.findById(spu.getCategory1Id()).getData();
        resultMap.put("category1",category1);
        Category category2 = categoryFeign.findById(spu.getCategory2Id()).getData();
        resultMap.put("category2",category2);
        Category category3 = categoryFeign.findById(spu.getCategory3Id()).getData();
        resultMap.put("category3",category3);
        //获取sku集合信息
        List<Sku> skuList = skuFeign.findSkuListBySpuId(spuId);
        resultMap.put("skuList",skuList);
​
        resultMap.put("specificationList",JSON.parseObject(spu.getSpecItems(), Map.class));
        return resultMap;
    }
}

(3)声明page_create_queue队列,并绑定到商品上架交换机

@Configuration
public class RabbitMQConfig {
​
    //定义交换机名称
    public static final String GOODS_UP_EXCHANGE="goods_up_exchange";
    public static final String GOODS_DOWN_EXCHANGE="goods_down_exchange";
​
    //定义队列名称
    public static final String AD_UPDATE_QUEUE="ad_update_queue";
    public static final String SEARCH_ADD_QUEUE="search_add_queue";
    public static final String SEARCH_DEL_QUEUE="search_del_queue";
    public static final String PAGE_CREATE_QUEUE="page_create_queue";
​
    //声明队列
    @Bean
    public Queue queue(){
        return new Queue(AD_UPDATE_QUEUE);
    }
    @Bean(SEARCH_ADD_QUEUE)
    public Queue SEARCH_ADD_QUEUE(){
        return new Queue(SEARCH_ADD_QUEUE);
    }
    @Bean(SEARCH_DEL_QUEUE)
    public Queue SEARCH_DEL_QUEUE(){
        return new Queue(SEARCH_DEL_QUEUE);
    }
    @Bean(PAGE_CREATE_QUEUE)
    public Queue PAGE_CREATE_QUEUE(){
        return new Queue(PAGE_CREATE_QUEUE);
    }
​
    //声明交换机
    @Bean(GOODS_UP_EXCHANGE)
    public Exchange GOODS_UP_EXCHANGE(){
        return ExchangeBuilder.fanoutExchange(GOODS_UP_EXCHANGE).durable(true).build();
    }
    @Bean(GOODS_DOWN_EXCHANGE)
    public Exchange GOODS_DOWN_EXCHANGE(){
        return ExchangeBuilder.fanoutExchange(GOODS_DOWN_EXCHANGE).durable(true).build();
    }
​
​
    //队列与交换机的绑定
    @Bean
    public Binding GOODS_UP_EXCHANGE_BINDING(@Qualifier(SEARCH_ADD_QUEUE)Queue queue,@Qualifier(GOODS_UP_EXCHANGE)Exchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("").noargs();
    }
    @Bean
    public Binding PAGE_CREATE_QUEUE_BINDING(@Qualifier(PAGE_CREATE_QUEUE)Queue queue,@Qualifier(GOODS_UP_EXCHANGE)Exchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("").noargs();
    }
    @Bean
    public Binding GOODS_DOWN_EXCHANGE_BINDING(@Qualifier(SEARCH_DEL_QUEUE)Queue queue,@Qualifier(GOODS_DOWN_EXCHANGE)Exchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("").noargs();
    }
​
}

(4)创建PageListener监听类,监听page_create_queue队列,获取消息,并生成静态化页面

@Component
public class PageListener {
​
    @Autowired
    private PageService pageService;
​
    @RabbitListener(queues = RabbitMQConfig.PAGE_CREATE_QUEUE)
    public void receiveMessage(String spuId){
        System.out.println("生成商品详情页面,商品id为: "+spuId);
        //生成静态化页面
        pageService.generateItemPage(spuId);
    }
}

(5)更新canal中消息队列配置类与Page服务一致

(6)更新canal中对于spu表的监听类,当商品审核状态从0变1,则将当前spuId发送到消息队列

//获取最新审核商品
if ("0".equals(oldData.get("status")) && "1".equals(newData.get("status"))){
  //发送商品spuId
  rabbitTemplate.convertAndSend(RabbitMQConfig.GOODS_UP_EXCHANGE,"",newData.get("id"));
}

模板填充

(1)面包屑数据

修改item.html,填充三个分类数据作为面包屑,代码如下:

(2)商品图片

修改item.html,将商品图片信息输出,在真实工作中需要做空判断,代码如下:

(3)规格输出

(4)默认SKU显示

静态页生成后,需要显示默认的Sku,我们这里默认显示第1个Sku即可,这里可以结合着Vue一起实现。可以先定义一个集合,再定义一个spec和sku,用来存储当前选中的Sku信息和Sku的规格,代码如下:

页面显示默认的Sku信息

(5)记录选中的Sku

在当前Spu的所有Sku中spec值是唯一的,我们可以根据spec来判断用户选中的是哪个Sku,我们可以在Vue中添加代码来实现,代码如下:

添加规格点击事件

(6)样式切换

点击不同规格后,实现样式选中,我们可以根据每个规格判断该规格是否在当前选中的Sku规格中,如果在,则返回true添加selected样式,否则返回false不添加selected样式。

Vue添加代码:

页面添加样式绑定,代码如下:

启动测试

启动eurekea服务端,数据监控服务,商品服务,静态页生成服务. 将spu表中status字段从0更新为1.

基于nginx完成静态页访问

生成的静态页我们可以先放到changgou-service-page工程中,后面项目实战的时候可以挪出来放到Nginx指定发布目录。一会儿我们将生成的静态页放到resources/templates/items目录下,所以请求该目录下的静态页需要直接到该目录查找即可。

posted @ 2021-08-04 10:32  1640808365  阅读(421)  评论(0编辑  收藏  举报