1.商品详情
当用户搜索到商品,肯定会点击查看,就会进入商品详情页,接下来我们完成商品详情页的展示,
1.1.Thymeleaf
在商品详情页中,我们会使用到Thymeleaf来渲染页面,所以需要先了解Thymeleaf的语法。
详见课前资料中《Thymeleaf语法入门.md》
创建leyou-goods-web微服务
导入依赖
<?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.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.leyou.goods</groupId>
<artifactId>leyou-goods-web</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<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-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.leyou.item</groupId>
<artifactId>leyou-item-interface</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.leyou.common</groupId>
<artifactId>leyou-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
</project>
创建配置文件
server:
port: 8084
spring:
application:
name: goods-web
thymeleaf:
cache: false
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
编写引导类
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LeyouGoodsWebApplication {
public static void main(String[] args) {
SpringApplication.run(LeyouGoodsWebApplication.class, args);
}
}
详情页页面跳转
编写Controller方法进行测试:
@Controller
public class GoodsController {
@GetMapping("item/{id}.html")
public String redirect(@PathVariable("id")Long id, Model model){
return "item.html";
}
}
成功跳转:
编写商品微服务的各个API
GoodsApi
/**
* 根据spu的id查询spu
* @param id
* @return
*/
@GetMapping("spu/{id}")
public Spu querySpuById(@PathVariable("id") Long id);
GoodsController
@GetMapping("spu/{id}")
public ResponseEntity<Spu> querySpuById(@PathVariable("id") Long id){
Spu spu = this.goodsService.querySpuById(id);
if(spu == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return ResponseEntity.ok(spu);
}
GoodsService
public Spu querySpuById(Long id) {
return this.spuMapper.selectByPrimaryKey(id);
}
组内有多个参数,为了方便展示。我们在leyou-item-service中提供一个接口,查询规格组,同时在规格组内的所有参数。,这个是根据组ID,查询出来所有的规格参数的列表
public List<SpecGroup> queryGroupWithCid(Long cid) {
//根据Cid查询出来所有的规格参数组
List<SpecGroup> specGroupList = this.queryGroupWithCid(cid);
//遍历每个规格参数组,根据组ID进行设置值,根据组ID,可以得到规格参数的列表,然后在将规格参数的列表集合设置到每个组内
specGroupList.forEach(specGroup -> {
List<SpecParam> specParamList = this.queryBySpecParam(specGroup.getId(), null, null, null);
specGroup.setParams(specParamList);
});
return specGroupList;
}
将所有要查询的对象和内容信息都封装到要给Map里面
public Map<String,Object> loadDate(Long spuId){
Map<String,Object> map=new HashMap<>();
//1.根据SpuId查询Spu
Spu spu = this.goodsClient.querySpuById(spuId);
//2.根据SpuId查询SpuDetail
SpuDetail spuDetail = this.goodsClient.queryBySpuId(spuId);
//3.根据SpuID查询Skus
List<Sku> skus = this.goodsClient.querBySpu(spuId);
//4.根据Spu查询品牌
Brand brand = this.brandClient.queryNameById(spu.getBrandId());
//5.根据Spu查询分类
List<Long> cids = Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3());
List<String> names = this.categoryClient.queryNameByids(cids);
List<Map<String,Object>> categories=new ArrayList<>();
for (int i = 0; i < cids.size(); i++) {
Map<String,Object> maps=new HashMap<>();
maps.put("id",cids.get(i));
maps.put("names",names);
categories.add(maps);
}
//6.根据Spu查询规格参数
List<SpecParam> specParamList = this.specificationClient.querySpecParam(null, spu.getCid3(), false, null);
Map<Long,Object> params=new HashMap<>();
specParamList.forEach(specParam -> {
params.put(specParam.getId(),specParam.getName());
});
//7.根据Spu查询规格参数组
List<SpecGroup> groups = this.specificationClient.queryGroupWithPramId(spu.getCid3());
map.put("spu",spu);
map.put("spuDetail",spuDetail);
map.put("skus",skus);
map.put("brand",brand);
map.put("categories",categories);
map.put("params",params);
map.put("groups",groups);
return map;
}
封装成功:
下面是渲染商品详情页,又是Vue的东西,所以写起来比较棘手,执行完ctrl+Shift+F9之后页面出现错误
经过排查发现是在Category封装的时候,添加names,把整个List给添加进去,导致了错误的循环,需要进行修改
这个Vue的页面渲染的时候,因为使用的是Consst所以JS的获取名称的脚本一定要放到Head的头部分,不然,识别部出来
这个在测试的时候,因为本人对Vue的,语法不熟悉,所以在定义一个变量的时候,Vue要能检测到该模型的前提,必须要在data里面注册过才能检测到,不然就会出现一直检测部到的现象:]
<script th:inline="javascript">
// sku集合
const skus = /*[[${skus}]]*/ [];
// 规格参数id与name对
const paramMap = /*[[${paramMap}]]*/ {};
// 特有规格参数集合
const specialSpec = JSON.parse(/*[[${spuDetail.specialSpec}]]*/ "");
const indexes={};
Object.keys(paramMap).forEach(param =>indexes[param]= 0);
</script>
测试选中项目:
进行选中数据的渲染
computed: {
sku(){
const index = Object.values(this.indexes).join("_");
return this.skus.find(sku => sku.indexes == index);
}
},
渲染大图过程中出现问题
其实根本问题还是Vue的语法掌握的不充足,很多语法部知道怎么拼写,工具也没有检测的机制,,最后发现问题,是在image 和src 前面没有加冒号导致的
下来出现规格参数组查询部出来,发现是Service出现了问题,导致无法封装
继续排查之后还是有问题发现在Specific的Service方法写的有问题
继续排查之后问题解决,页面成功渲染刷新出来
渲染规格与包装售后等信息
页面静态化,这个过程就非常简单了,执行一个Java代码就可以了
PrintWriter printWriter=null;
try {
//初始化上下文对象
Context context = new Context();
context.setVariables(this.goodsService.loadDate(spuId));
File file = new File("D:\\nginx\\html\\item\\" + spuId + ".html");
printWriter = new PrintWriter(file);
this.templateEngine.process("item", context, printWriter);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (printWriter!=null){
printWriter.close();;
}
}
}
}
页面静态化成功:
页面静态化的访问请求: