乐优商城(八)商品详情

1. 搭建商品详情微服务

当用户搜索到商品后,如果想要了解商品的更多信息,就需要进入商品详情页。

由于商品详情浏览量比较大,所以我们会创建一个微服务,用来展示商品详情。我们的商品详情页会采用 Thymeleaf 模板引擎渲染后,再返回到客户端。

1.1 创建工程

  1. 右键 leyou 项目 --> New Module --> Maven --> Next

  2. 填写项目信息 --> Next

3.填写保存的位置 --> Finish

4.添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<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"><parent><artifactId>leyou</artifactId><groupId>com.leyou.parent</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><groupId>com.leyou.parent</groupId><artifactId>leyou-goods-web</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>com.leyou.common</groupId><artifactId>leyou-common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>com.leyou.item</groupId><artifactId>leyou-item-interface</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>
</project>

5.编写配置文件 application.yaml

server:port: 8084
spring:application:name: goods-web-servicethymeleaf:cache: false
eureka:client:service-url:defaultZone: http://localhost:10086/eurekainstance:lease-renewal-interval-in-seconds: 5lease-expiration-duration-in-seconds: 10

6.编写启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LeyouGoodsWebApplication {public static void main(String[] args) {SpringApplication.run(LeyouGoodsWebApplication.class, args);}
}

1.2 创建页面模板

  1. 从 leyou-portal 项目中复制 item.html 模板到当前项目 resource 目录下的 templates 中。

2.把 HTML 的名称空间改成 xmlns:th="http://www.thymeleaf.org",这样页面就由 Thymeleaf 的引擎解析了。

1.3 页面跳转

1.3.1 修改页面跳转路径

当我们点击某个商品图片时,应该携带该商品的 SpuId 跳转到商品详情页。

例如:

http://www.leyou.com/item/2314123.html

我们打开 search.html,修改其中的商品路径:

1.3.2 Nginx 反向代理

接下来,我们要把这个地址指向我们的 leyou-goods-web 服务,其端口为 8084。

我们在 nginx.conf 中添加配置,并重启 Nginx

1.3.3 编写 Controller

在 leyou-goods-web 中编写 Controller,接收请求,并跳转到商品详情页

@Controller
@RequestMapping("/item")
public class GoodsController {/*** 跳转到商品详情页** @param model* @param id* @return*/@GetMapping("/{id}.html")public String toItemPage(Model model, @PathVariable("id") Long id) {return "item";}
}
1.3.4 测试
  1. 启动 leyou-goods-web 工程

  2. 点击一个搜索到的商品,成功跳转到商品详情页

1.4 后台提供接口

1.4.1 分析模型数据

首先我们一起来分析一下,在这个页面中需要哪些数据。

我们已知的条件是传递来的 Spu 的 id,我们需要根据 Spu 的 id 查询到下面的数据:

  • Spu 信息
  • Spu 详情
  • Spu 下的所有 Sku
  • 品牌
  • 商品三级分类
  • 规格参数组
  • 规格参数
1.4.2 商品微服务提供接口

为了查询到上面的数据,我们需要在商品微服务中提供一些接口。

通过 Spu 的 id 查询 Spu

在 SpuApi 接口中添加方法 querySpuById

/*** 通过 spuId 查询 Spu* @param spuId* @return*/
@GetMapping("{spuId}")
public Spu querySpuById(@PathVariable("spuId") Long spuId);

在 SpuController 中添加方法 querySpuById

/*** 通过 spuId 查询 Spu* @param spuId* @return*/
@GetMapping("{spuId}")
public ResponseEntity<Spu> querySpuById(@PathVariable("spuId") Long spuId){Spu spu = spuService.querySpuById(spuId);if (spu == null) {return ResponseEntity.notFound().build();}return ResponseEntity.ok(spu);
}

在 SpuService 中添加方法 querySpuById

/*** 通过 spuId 查询 Spu* @param spuId* @return*/
public Spu querySpuById(Long spuId) {Spu spu = spuMapper.selectByPrimaryKey(spuId);return spu;
}

通过分类 id 查询规格参数组

商品详情页需要展示商品的规格参数组,以及其下的规格参数。所以我们需要提供一个接口,通过 Spu 的 id 查询规格参数组,并将规格参数封装其中。

在 SpecificationApi 接口中添加方法 queryGroupWithCid

/*** 通过分类 id 查询规格参数组* @param cid* @return*/
@GetMapping("/group/param/{cid}")
public List<SpecGroup> queryGroupWithCid(@PathVariable("cid") Long cid);

在 SpecificationController 中添加方法 queryGroupsWithParam

/*** 通过分类 id 查询规格参数组** @param cid* @return*/
@GetMapping("/group/param/{cid}")
public ResponseEntity<List<SpecGroup>> queryGroupsWithParam(@PathVariable("cid") Long cid) {List<SpecGroup> specGroups = specificationService.queryGroupsWithParam(cid);if (CollectionUtils.isEmpty(specGroups)) {return ResponseEntity.notFound().build();}return ResponseEntity.ok(specGroups);
}

在 SpecificationService 中添加方法 queryGroupsWithParam

/*** 通过分类 id 查询规格参数组** @param cid* @return*/
public List<SpecGroup> queryGroupsWithParam(Long cid) {List<SpecGroup> specGroups = querySpecGroupsByCid(cid);for (SpecGroup specGroup : specGroups) {List<SpecParam> params = querySpecParams(specGroup.getId(), null, null, null);specGroup.setParams(params);}return specGroups;
}
1.4.3 创建 FeignClient

我们在 leyou-goods-web 服务中,创建 FeignClient

@FeignClient(value = "item-service")
public interface BrandClient extends BrandApi {
}
@FeignClient(value = "item-service")
public interface SpuClient extends SpuApi {
}
@FeignClient(value = "item-service")
public interface CategoryClient extends CategoryApi {
}
@FeignClient(value = "item-service")
public interface SpecificationClient extends SpecificationApi {
}
1.4.4 商品详情微服务提供接口

再来回顾一下商品详情页需要的数据,如下:

  • Spu 信息
  • Spu 详情
  • Spu 下的所有 Sku
  • 品牌
  • 商品三级分类
  • 规格参数组
  • 规格参数

我们可以使用 Map<String, Object> 的数据结构封装这些数据,第一个参数为数据名称,第二个参数为数据。

  1. 在 GoodsController 中查询到所需数据,并放入 model

@Controller
@RequestMapping("/item")
public class GoodsController {@Autowiredprivate GoodsService goodsService;/*** 通过 spuId 查询所需数据** @param model* @param id* @return*/@GetMapping("/{id}.html")public String toItemPage(Model model, @PathVariable("id") Long id) {// 通过 spuId 查询所需数据Map<String, Object> modelMap = this.goodsService.loadData(id);// 放入模型model.addAllAttributes(modelMap);return "item";}
}

在 GoodsService 中添加方法 loadData

@Service
public class GoodsService {@Autowiredprivate BrandClient brandClient;@Autowiredprivate CategoryClient categoryClient;@Autowiredprivate SpuClient spuClient;@Autowiredprivate SpecificationClient specificationClient;/*** 通过 spuId 查询所需数据* @param spuId* @return*/public Map<String, Object> loadData(Long spuId) {Map<String, Object> map = new HashMap<>();// 查询 SpuSpu spu = this.spuClient.querySpuById(spuId);// 查询 SpuDetailSpuDetail spuDetail = this.spuClient.querySpuDetailBySpuId(spuId);// 查询 Sku 集合List<Sku> skus = this.spuClient.querySkusBySpuId(spuId);// 查询分类List<Long> cids = Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3());List<String> names = this.categoryClient.queryNamesByIds(cids);List<Map<String, Object>> categories = new ArrayList<>();for (int i = 0; i < cids.size(); i++) {Map<String, Object> categoryMap = new HashMap<>();categoryMap.put("id", cids.get(i));categoryMap.put("name", names.get(i));categories.add(categoryMap);}// 查询品牌Brand brand = this.brandClient.queryBrandById(spu.getBrandId());// 查询规格参数组List<SpecGroup> groups = this.specificationClient.queryGroupsWithParam(spu.getCid3());// 查询特殊的规格参数List<SpecParam> params = this.specificationClient.querySpecParams(null, spu.getCid3(), false, null);Map<Long, String> paramMap = new HashMap<>();params.forEach(param -> {paramMap.put(param.getId(), param.getName());});// 封装 Spumap.put("spu", spu);// 封装 SpuDetailmap.put("spuDetail", spuDetail);// 封装 Sku 集合map.put("skus", skus);// 封装分类map.put("categories", categories);// 封装品牌map.put("brand", brand);// 封装规格参数组map.put("groups", groups);// 封装特殊规格参数map.put("paramMap", paramMap);return map;}
}
1.4.5 测试
  1. 在 item.html 页面写一段 JS 代码,把模型中的数据取出观察

<script th:inline="javascript">const a = /*[[${groups}]]*/ [];const b = /*[[${params}]]*/ [];const c = /*[[${categories}]]*/ [];const d = /*[[${spu}]]*/ {};const e = /*[[${spuDetail}]]*/ {};const f = /*[[${skus}]]*/ [];const g = /*[[${brand}]]*/ {};
</script>
  1. 点击一个商品详情页,查看网页源码,成功查到数据

  2. 在 item.html 页面写一段 JS 代码,把模型中的数据取出观察

1.5 渲染页面

略,交给前端吧。最终效果如下:

在这里插入图片描述

在这里插入图片描述

2. 页面静态化

2.1 问题分析

现在我们的商品详情页会采用 Thymeleaf 模板引擎渲染后,再返回到客户端。

但这样在后台需要做大量的数据查询,而后渲染得到 HTML 页面。会对数据库造成压力,并且请求的响应时间过长,并发能力不高。

有没有办法解决这些问题呢?

  • 首先,我们能想到的就是缓存技术。比如使用 Redis 缓存,不过 Redis 适合数据规模比较小的情况。假如数据量比较大,比如我们的商品详情页,每个页面如果 10 kb,100 万商品,就是 10 GB 空间,对内存占用比较大,此时就给缓存系统带来极大压力,如果缓存崩溃,接下来倒霉的就是数据库了。
  • 其次,可以使用静态化技术。静态化是指把动态生成的 HTML 页面变为静态内容保存,以后用户的请求到来,直接访问静态页面,不再经过服务器的渲染。而静态的 HTML 页面可以部署在 Nginx 中,从而大大提高并发能力。

2.2 如何实现静态化

原来,我们商品详情页通过 Thymeleaf 模板引擎生成后,直接就返回给客户端了。

现在,我们生成商品详情页后,将它先部署一份在 Nginx 中,再返回给客户端。下一次在访问这个页面时,就直接访问 Niginx 中的静态页面

2.3 实现页面静态化

  1. 在 leyou-goods-web 工程中的 service 包下创建 GoodsHtmlService

@Service
public class GoodsHtmlService {@Autowiredprivate GoodsService goodsService;@Autowiredprivate TemplateEngine templateEngine;/*** 创建 HTML 静态页面** @param spuId* @throws Exception*/public void createHtml(Long spuId) {PrintWriter writer = null;try {// 获取页面数据Map<String, Object> spuMap = this.goodsService.loadData(spuId);// 创建 Thymeleaf 上下文对象Context context = new Context();// 把数据放入上下文对象context.setVariables(spuMap);// 创建输出流File file = new File("D:\\nginx-1.14.0\\html\\item\\" + spuId + ".html");writer = new PrintWriter(file);// 执行页面静态化方法templateEngine.process("item", context, writer);} catch (Exception e) {e.printStackTrace();} finally {if (writer != null) {writer.close();}}}
}

在 GoodsController 中调用页面静态化方法

/*** 通过 spuId 查询所需数据** @param model* @param id* @return*/
@GetMapping("/{id}.html")
public String toItemPage(Model model, @PathVariable("id") Long id) {// 通过 spuId 查询所需数据Map<String, Object> modelMap = this.goodsService.loadData(id);// 放入模型model.addAllAttributes(modelMap);// 页面静态化goodsHtmlService.createHtml(id);return "item";
}

修改 Nginx 配置,使 Nginx 代理静态页面。让它对商品请求进行监听,先指向本地静态页面,如果本地没找到,才反向代理到商品详情微服务。

server {listen       80;server_name  www.leyou.com;proxy_set_header X-Forwarded-Host $host;proxy_set_header X-Forwarded-Server $host;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;location /item {# 先找本地root html;if (!-f $request_filename) { #请求的文件不存在,就反向代理proxy_pass http://127.0.0.1:8084;break;}}location / {proxy_pass http://127.0.0.1:9002;proxy_connect_timeout 600;proxy_read_timeout 600;}
}

2.4 测试

  1. 重启商品详情微服务

  2. 重启 Nginx

  3. 访问一个商品详情页后,成功生成静态页面

4.再次访问,速度得到极大提升

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/271682.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【三维重建】相移法+格雷码

本篇文章介绍一种稠密点云的获取方式——条纹结构光三维重建算法。 在学习此算法前&#xff0c;我们需要对基于视觉的三维重建算法有一定了解。 需要了解什么是相机模型、相机标定以及三角化的相关知识。 【三维重建】摄像机几何-CSDN博客 【三维重建】摄像机标定&#xff…

【C++干货基地】六大默认成员函数: This指针 | 构造函数 | 析构函数

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 哈喽各位铁汁们好啊&#xff0c;我是博主鸽芷咕《C干货基地》是由我的襄阳家乡零食基地有感而发&#xff0c;不知道各位的…

听 GPT 讲 client-go 源代码 (24)

分享更多精彩内容&#xff0c;欢迎关注&#xff01; File: client-go/applyconfigurations/batch/v1/jobstatus.go 在client-go的applyconfigurations/batch/v1/jobstatus.go文件中&#xff0c;定义了与Job的状态相关的配置和操作。 文件中定义了以下几个结构体&#xff1a; Jo…

Kube-Prometheus 监控Istio

推荐 Istio 多集群监控使用 Prometheus&#xff0c;其主要原因是基于 Prometheus 的分层联邦&#xff08;Hierarchical Federation&#xff09;。 通过 Istio 部署到每个集群中的 Prometheus 实例作为初始收集器&#xff0c;然后将数据聚合到网格层次的 Prometheus 实例上。 网…

Kruscal建树+倍增LCA,蓝桥2023省赛,网络稳定性

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 2.网络稳定性 - 蓝桥云课 (lanqiao.cn) 二、解题报告 1、思路分析 考虑到…

自动驾驶预测与决策规划(nuplan数据集)

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 目录 1.概述 2 数据采集 3.开环与闭环仿真 4.数据注释 5.场景 6.规划框架 6.1Train 6.2Simulation 6.3Metric 6.4Visualization 7.下载…

【2024.03.05】定时执行专家 V7.1 发布 - TimingExecutor V7.1 Release

目录 ▉ 软件介绍 ▉ 新版本 V7.1 下载地址 ▉ V7.1 新功能 ▼2024-03-03 V7.1 - 更新日志 ▉ V7.0 新UI设计 ▉ 软件介绍 《定时执行专家》是一款制作精良、功能强大、毫秒精度、专业级的定时任务执行软件。软件具有 25 种【任务类型】、12 种【触发器】触发方式&#x…

基于51单片机风速仪风速测量台风预警数码管显示

基于51单片机风速仪风速测量报警数码管显示 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真4. 程序代码5. 设计报告&#x1f517;6. 下载链接资料下载链接&#xff1a; 基于51单片机风速仪风速测量报警数码管显示( proteus仿真程序设计报告讲解视频&#xff09; 仿真图…

CRMCHAT修复获取客户ip信息,地区信息

CRMCHAT修复获取客户ip信息&#xff0c;地区信息-TP源码网原因&#xff1a; 因pv.sohu.com/cityjson?ieutf-8接口已无法正确获取ip信息&#xff0c;导致后台站点统计无法正确获取用户ip信息&#xff0c;无法获取地区信息 修改 注释掉无用接口地址 修复ip信息 也可以使用&…

【Lazy ORM】 小工具 acw 本地客户端 你负责点击页面,他负责输出代码

介绍 wu-smart-acw-client 简称acw-client&#xff0c;是一个基于Lazy ORM定制的客户端代码生成小工具 Lazy ORM 小工具 acw 本地客户端 你负责点击页面&#xff0c;他负责输出代码安装 <dependency><groupId>top.wu2020</groupId><artifactId>wu-sma…

即插即用篇 | YOLOv8 引入 ParNetAttention 注意力机制 | 《NON-DEEP NETWORKS》

论文名称:《NON-DEEP NETWORKS》 论文地址:https://arxiv.org/pdf/2110.07641.pdf 代码地址:https://github.com/imankgoyal/NonDeepNetworks 文章目录 1 原理2 源代码3 添加方式4 模型 yaml 文件template-backbone.yamltemplate-small.yamltemplate-large.yaml

2024蓝桥杯每日一题(前缀和)

一、第一题&#xff1a;壁画 解题思路&#xff1a;前缀和贪心枚举 仔细思考可以发现B值最大的情况是一段连续的长度为n/2上取整的序列的累加和 【Python程序代码】 import math T int(input()) for _ in range(1,1T):n int(input())s input()l math.ceil(len(s)/…

图文并茂的讲清楚Linux零拷贝技术

今天我们来聊一聊Linux零拷贝技术&#xff0c;今天我们以一个比较有代表性的技术sendfile系统调用为切入点&#xff0c;详细介绍一下零拷贝技术的原理。 1.零拷贝技术简介 Linux零拷贝技术是一种优化数据传输的技术&#xff0c;它可以减少数据在内核态和用户态之间的拷贝次数&…

Linux报错排查-刚安装好的ubuntu系统无法ssh连接

Linux运维工具-ywtool 目录 一.问题描述二.问题解决2.1 先给ubuntu系统配置阿里云源2.2 安装openssh-server软件2.3 在尝试ssh连接,可以连接成功了 三.其他命令 一.问题描述 系统:ubuntu-18.04-desktop-amd64 系统安装完后,想要通过xshell软件连接系统,发现能Ping通系统的IP,但…

【探索Linux】—— 强大的命令行工具 P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )

阅读导航 引言一、socket 常见API表二、函数详细介绍01. socket()02. bind()03. listen()04. accept()05. connect()06. send()07. recv()08. close()09. select()10. getaddrinfo()11. sendto()12. recvfrom()13. setsockopt()14. getsockopt()15. shutdown()16. inet_pton()1…

昇腾芯片解析:华为自主研发的人工智能处理器全面分析

在当今科技发展的浪潮中&#xff0c;昇腾芯片作为一种新兴的处理器&#xff0c;正引起广泛的关注和讨论。升腾芯片究竟是由哪家公司生产的&#xff1f;这个问题一直困扰着许多人。下面小编将全面介绍、分析升腾芯片的生产商及各类参数、应用&#xff0c;以便读者对其有更全面的…

centos7 python3.12.1 报错 No module named _ssl

https://blog.csdn.net/Amio_/article/details/126716818 安装python cd /usr/local/src wget https://www.python.org/ftp/python/3.12.1/Python-3.12.1.tgz tar -zxvf Python-3.12.1.tgz cd Python-3.12.1/ ./configure -C --enable-shared --with-openssl/usr/local/opens…

C++:Stack和Queue的模拟实现

创作不易&#xff0c;感谢三连&#xff01; 一、容器适配器 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结)&#xff0c;该种模式是将一个类的接口转换成客户希望的另外一个接口。 就如同是电源适配器将不适用的交流电…

css实现背景渐变叠加

线性渐变效果图: .box{width: 100vw;height: 100vh;background:linear-gradient(to bottom,transparent,#fff 30%),linear-gradient(to right,pink,skyblue);}径像渐变效果图&#xff1a; .box{width: 100vw;height: 100vh;background:linear-gradient(to bottom,transparent,#…

【JavaEE初阶】 JVM类加载简介

文章目录 &#x1f343;前言&#x1f332;类加载过程&#x1f6a9;加载&#x1f6a9;验证&#x1f6a9;准备&#x1f6a9;解析&#x1f6a9;初始化 &#x1f384;双亲委派模型&#x1f6a9;什么是双亲委派模型&#xff1f;&#x1f6a9;双亲委派模型的优点 ⭕总结 &#x1f343…