我是靠谱客的博主 贪玩冥王星,最近开发中收集的这篇文章主要介绍SpringCloud商城day08 商品详情页-2021-10-14,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一. Thymeleaf入门

1. thymeleaf: 页面静态化技术 -> 提前生成静态资源 -> 用户访问时加载到浏览器-> 提高访问效率减少占用资源

Thymeleaf is a modern server-side Java template engine for both web and standalone environments. 

2. SpringMVC整合thymeleaf模块 -> 六种模板

3. 创建springboot工程 -> 依赖starter-web/thymeleaf -> 启动类ThymeleafApplication.java

(1) yml配置: spring.thymeleaf.cache: false //页面加载时关闭缓存
(2) 新建页面 resourece/templates/*.html 

 4. thymeleaf语法:

(1) <html xmlns:th="http://www.thymeleaf.org">
//表单提交
<p th:text="${hello}"></p>
<form th:action="@{/demo/test}">
<input th:type="text" th:name="id">
//输入框属性名
<button>提交</button>
</form>
(2) 遍历对象:
<tr th:each="user, userStat:${userList}" >
<td>
<span th:text="${userStat.index}"></span>
// 0 1 2
</td>
<td th:text="${user.id}"></td>
// 1 2 3
</tr>
(3) Map输出:
<div th:each="map, mapStat:${dataMap}">
<div th:text="${map}"></div>
<span th:text="${mapStat.current.key}"></span>
//取键
<span th:text="${mapStat.current.value}"></span> //取值
</div>
(4) 数组输出:
<div th:each="array, arrayStat:${names}">
<span th:text="{arrayStat.count}"></span> //count属性 从1开始
</div>
(5) 日期输出:
<div th:text="${#dates.format(now, 'yyyy-MM-dd HH:mm:ss')}">
</div>
(6) 条件判断:
<div>
<span th:if="${(age>=18)}">符合条件则输出</span>
</div>
(7) 创建页面模块footer.html
-> 引入命名空间 <html xmlns:th="http://www.thymeleaf.org">
-> fragment模块:
<div id="C" th:fragment="copy"></div>
-> demo.html引入模块: <div id="F" th:include="footer::copy"></div>
(8) th:utext="直接解析自带html标签"

二. 搜索页面渲染

1. 需求: 显示 用户勾选的搜索条件+商品属性规格+商品搜索结果集合

2. 实现: 客户端搜索 -> 搜索微服务查询索引 -> thymeleaf渲染静态化页面 -> 返回页面;

3. 步骤:

1. search微服务
(1) 添加thymeleaf依赖: spring-boot-starter-thymeleaf
(2) yml配置文件: thymeleaf.cache: false
(3) 添加页面静态化资源:
1) resources/static/js/img/css
2) resources/templates/html
(4) 更新SearchController.java
1)
类注解:@Controller
//不用@RestController, 新增方法返回值不是JSON
2) 添加方法
@GetMapping()
public String list(){
//特殊符号处理
this. handleSearchMap(searchMap);
//获取查询结果
Map resultMap = searchService.search(searchMap);
//携带数据
model.addAttribute("searchMap", searchMap);
model.addAttributes("resultMap", resultMap);
//跳转搜索页面
return "search";
}
(1) 修改SearchServiceImpl.java
-> 转换规格字符串的集合[{"颜色":"黑", "尺码":"小",...},{"颜色":"白", "尺码":"中"},....]
-> 转换成Map {颜色=[黑,白,...], 尺码=[小,中],...}
-> thymeleaf遍历map得到数据
(2)实现规格转换:
public Map<String, Set<String>> formatSpec(List<String> specList){
//定义map
Map<String, Set<String>> resultMap = new HashMap<>();
if(specList !=null&&specList.size()>0){
//遍历
for(String specJasonString : specList){
//jason转换为map
Map<String, String> specMap = JASON.parseObject(specJasonString, Map.class);
//遍历key 颜色/尺码
for(String specKey:specMap.keySet()){
//获得值集合
Set<String> specSet = resultMap.get(specKey);
if(specSet==null){
specSet=new HashSet<String>();
}
//将规格放入set中
specSet.add(specMap.get(specKey)):
//将set放入map
resultMap.put(specKey, specSet);
}
}
}
return resultMap;
}
(1) 搜索关键字页面渲染 -> 搜索框条件 -> 数据回显-> 表单提交 -> 商品列表查询
thymeleaf表单提交:
<form th:action="@{/search/list/}'>
<input th:type="text" id="autocomplete" name="keywords" th:value="${searchMap.keywords}">
<button th:type="submit">
(2) 条件搜索实现: 搜索 -> 添加条件 -> URL+条件 -> 存入Model -> 取出URL -> 拼接条件 ->跳转
//修改Controller, 拼装URL
StringBuilder url = new StringBuilder("/search/list");
if(searchMap !=null&&searchMap.size()>0){
//查询条件
url.append(?);
for(String paramkey:searchMap.keyset()){
if(!"sortRule".equals(paramKey)&&!"sortField".equals(paramKey)&&!"pageNum".equals(paramKey)){
url.append(paramKey).append("=").append(searchMap.get(paramKey)).append("&");
}
}
String urlString = url.toString();
//取出路径多余的&号
urlString = urlString.subString(0,urlString.length()-1);
model.addAttributes("url",urlString);
}

1. 前端url拼接: 

<a th:text="${brand}" th:href="@{${url}(brand=${brand})}"></a>

2. 去除搜索条件: X 

<a th:href="@{${#strings.replace(url, '&price='+searchMap.price,'')}}">X</a>

3. 产品价格排序:  价格↑  价格↓

<a th:href="@{${url}(sortRule='ASC', sortField='price')}">价格↑</a>
<a th:href="@{${url}(sortRule='DESC', sortField='price')}">价格↓</a>

4. 分页查询实现: 

(1) 后端封装分页数据的实体类: Page.java -> 初始化分页数据
public class Page<T> implements Serializable{
//当前页为第一页
public static final Integer pageNum=1;
//默认每页显示
public static final Integer pageSize=10;
//判断当前页是否为空或<1
public static Integer currenPagegNumber(Integer pageNum){
if(num==pageNum||pageNum<1){
pageNum=1;
}
return pageNum;
}
//设置当前页
public voidsetCurrentPage(long currenpage, long total, long pagesize){
//能整除正好分N页, 不能则N+1
int totalPages = (int)(total%pagesize=0?total/pagesize:(total/pagesize)+1);
//总页数
this.last=totalPage;
//判断当前页是否大于总页数, 越界则查最后一页
if(currentpage>totalpages){
this.currentpage=totalpages;
}else{
this.currentpage=currentpage;
}
//计算起始页
this.start=(this.currentpage-1)*pagesize;
}
//计算起始页
public void initPage(long total, int currenpage, int pagesize){
//总记录数
this.total=total;
//每页条数
this.size=pagesize;
//计算当前页数据库查询起始值和总页数
setCurrentpage(currentpage, total, pagesize);
//分页计算
int leftcount = this.offsize;
//向上一页执行多少次
rightcount = this.offsize;
//起始页
this.lpage=currentpage;
//结束页
this.rpage=currentpage;
//2点判断
this.lpage=currentpage-leftcount;
//正常起点
this.rpage=currentpage+rightcount; //正常终点
//页差=总页数-结束页
int topdiv = this.last-rpage;
//判断是否大于最大页数
//页差<0, 起点页=起点页+页差
//页差>=0, 起点和终点判断
this.lpage=topdiv<0?this.lpage+topdiv:this.lpage;
//起点页<=0, 结束页=|起点页|+1
//起点页>0, 结束页
this.rpage=this.lpage<=0?this.rpage+(this.lpage*-1)+1:this.rpage;
//起点页<=0, 起点页成为第一页
this.lpage=this.lpage<=?1:this.lpage;
//结束页>总页数, 结束页=总页数
this.rpage=this.rpage>last?this.last:this.rpage;
}
}

       (2). SearchControler中封装分页数据:

//封装分页数据并返回
Page<SkuInfo> page = new Page<SkuInfo>{
//总记录数
Long.parseLong(String.valueOf(resultMap.get("total")));
//设置当前页
Integer.parseInt(String.valueOf(resultMap.get("pageNum")));
Page.pageSize
}

        (3) 分页前端实现:

<a th:href="@{${url}(pageNum=${page.upper})}">上一页</a>
<a th:href="@{${url}(pageNum=${page.next})}">下一页</a>
<!--动态页码-->
<li th:each="i:${#numbers.sequence(page.lpage, page,rpage)}" th:class="${i}==${page.currentpage}?'active':''></li>

三. 商品详情页静态化

1. 提前生成商品详情页 -> 部署到高性能web服务器 -> 静态化加载 -> 无需请求后端服务

流程:
->商品状态已审核
-> 发送商品spuId到rabbitMQ
-> MQ发送消息到changgou_web渲染微服务
-> 静态页生成渲染微服务监听消息
-> 渲染微服务发送spuId远程调用商品微服务
-> 通过spuId查询goods
-> 使用thymeleaf生成商品详情页面

2. 静态页生成服务: changgou_service_page

service_page微服务:
(1) 添加依赖: common / starter-thymeleaf / amqp / goods-api
(2) 配置文件: pagepath: D:items
#自定义属性, 静态页存储路径
(3) 启动类: com.changgou.page.PageApplication.java
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages= {"com.changgou.goods.feign"})
public class PageApplication{
SpringApplication.run(PageApplication.class, args);
}
(4) feign接口: changgou_service_goods_api
@FeignClient(name="goods")
public interface CategoryFeign{
@GetMapping("/category/{id}")
public Result<Category> findById(@PathVariable("id") Integer id);
}
@FeignClient(name="goods")
public interface SpuFeign{
@GetMapping("/findSpuById/{id}")
public Result<Category> findSpuById(@PathVariable("id") String id);
}
(5) 静态页生成代码:
监听商品审核状态 -> 上架后发送消息spuId -> page服务监听消息 -> 接收spuId -> 调用goods服务查询数据 -> page服务生成静态页
@Service
public class PageServiceImpl implements PageService{
//获取配置文件中的自定义属性
@Value("${pagepath}")
private String pagepath;
@Autowired
private TemplateEngine templateEngine;
@Override
public void generateHtml(String spuId) {
//1.获取context对象, 用于存储商品的相关数据
Context context = new Context();
//定义方法, 获取静态化页面数据
Map<String, Object> itemData = this.getItemData(spuId);
//存储静态化页面数据, 参数map
context.setVariables(itemData);
//2.获取商品详情页面存储位置
File dir = new File(pagepath);
//3.判断当前存储位置的文件夹是否存在, 不存在则新建
if (!dir.exists()){
dir.mkdirs();}
//4.定义输出流, 完成文件生成
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 (FileNotFoundException e) {
e.printStackTrace();}
//5. 关闭流
try {out.close();} catch (IOException e) {
e.printStackTrace();}
}
//获取静态化页面的相关数据
private Map<String,Object> getItemData(String spuId) {
Map<String, Object> resultMap = new HashMap<>();
//1. 获取spu
Result<Spu> spuById = spuFeign.findSpuById(spuId);
Spu spu = spuById.getData();
resultMap.put("spu", spu);
//2. 获取图片信息
if (spu != null){
if (StringUtils.isNotEmpty(spu.getImages())){
resultMap.put("imageList", spu.getImages().split(","));
}
}
//3. 获取商品的分类信息
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);
//4. 获取sku信息
List<Sku> skuList = skuFeign.findSkuListBySpuId(spuId);
resultMap.put("skuList",skuList);
//5. 获取商品规格信息
resultMap.put("specificationList", JSON.parseObject(spu.getSpecItems(),Map.class));
return resultMap;
}
}

(1) com.changgou.page.config.RabbitMQConfig.java配置类

//定义队列
public static final String PAGE_CREATE_QUEUE="page_create_queue";
//声明队里
@Bean(PAGE_CREATE_QUEUE)
Publiv Queue newQueue(){
return new Queue(PAGE_CREATE_QUEUE);
}
//绑定
@Bean
public Binding PAGE_CREATE_QUEUE_BINDING(@Qualifier(PAGE_CREATE_QUEUE)Queue queue, @Qualifier(GOODS_UP_EXCHAGNE)Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("").noargs();
}

(2) 监听类: com.changgou.page.listener.PageListener.java

@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.generateHtml(spuId);
}
}

(3) 修改canal监控微服务的 spuListener 

SpuListener监听tb_spu表:
@CanalEventListener: 事件监听
@ListenPoint(schema="changgou_goods", table="tb_spu"): 监听点
//获取表数据改变前的数据
Map<String,String> oldData = CanalEntry.RowData rowData.getBeforeColumnsList()
//获取表数据改变后的数据
Map<String,String> newData = CanalEntry.RowData rowData.getAfterColumnsList()
//获取最新被审核通过的商品 status 0->1
if ("0".equals(oldData.get("status")) && "1".equals(newData.get("status"))){
//将商品的spuid发送到mq
rabbitTemplate.convertAndSend(RabbitMQConfig.GOODS_UP_EXCHANGE,"",newData.get("id"));
}

(4) nginx静态资源服务器 -> 存储商品静态页

        1) nginx目录: usr/local/openresty/nginx/html -> 放入商品静态页

        2) 刷新nginx: systemctl restart openresty 

                或进入cd usr/local/openresty/nginx/sbin ->  输入命令./nginx -s reload

最后

以上就是贪玩冥王星为你收集整理的SpringCloud商城day08 商品详情页-2021-10-14的全部内容,希望文章能够帮你解决SpringCloud商城day08 商品详情页-2021-10-14所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(40)

评论列表共有 0 条评论

立即
投稿
返回
顶部