今日目标:
(1)掌握 Freemarker常用的指令与内建函数
(2)完成商品详细页的数据显示
(3)完成商品详细页的动态显示
(4)完成商品详细页读取SKU信息的业务逻辑
(5)完成商品审核调用功能
目录
1、商品详细页-数据显示
1.1 配置
1.2 服务层
1.3 基本测试
1.4 替换模板基本信息为插值
1.5 替换模板图片列表
1.6 生成扩展属性列表
1.7 生成规格列表
1.8 生成商品类型面包屑
2、商品详细页-前端逻辑
2.1 数量的加减
2.2 规格选择
3、商品详细页-读取SKU信息
3.1 服务层实现(page-service)
3.2 加载默认的SKU标题和价格
3.3 选择规格加载对应的标题和价格
3.4 预留加入购物车方法
4、审核商品生成商品详细页
4.1 控制层(manager-web)
其他:与搜索模块对接
1、配置Nginx
2、运行测试,执行http://localhost:9101/goods/genHtml.do?goodsId=149187842867981
3、对接
准备工作:搭建工程(page-interface、page-service),并编写相关配置和引入相关依赖
(1)配置Freemarker的bean,在WEB-INF下创建ftl文件夹
(2)配置存放商品详细页的位置,注意保证文件夹存在,还需要将样式文件拷入该目录
## 商品详细页存放位置
PAGE_DIR=D:\\item\\
(1)page-interface,编写ItemPageService接口
package com.pinyougou.page.service;
/**
* 商品详细页静态化
* Author xushuai
* Description
*/
public interface ItemPageService {
/**
* 生成商品详细页
*
* @param goodsId 商品id
* @return boolean
*/
boolean genItemPage(Long goodsId);
}
(2)page-service,编写ItemPageServiceImpl实现
package com.pinyougou.page.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.mapper.TbGoodsDescMapper;
import com.pinyougou.mapper.TbGoodsMapper;
import com.pinyougou.page.service.ItemPageService;
import com.pinyougou.pojo.TbGoods;
import com.pinyougou.pojo.TbGoodsDesc;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import java.io.FileWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
/**
* 商品详情页静态化实现
* Author xushuai
* Description
*/
@Service
@Transactional
public class ItemPageServiceImpl implements ItemPageService {
@Autowired
private FreeMarkerConfigurer FreeMarkerConfigurer;
@Autowired
private TbGoodsMapper goodsMapper;
@Autowired
private TbGoodsDescMapper goodsDescMapper;
@Value("PAGE_DIR")
private String PAGE_DIR;
@Override
public boolean genItemPage(Long goodsId) {
try {
// 查询商品基本信息和商品扩展信息
TbGoods tbGoods = goodsMapper.selectByPrimaryKey(goodsId);
TbGoodsDesc tbGoodsDesc = goodsDescMapper.selectByPrimaryKey(goodsId);
// 将查询到的数据封装到Map
Map data = new HashMap<>();
data.put("goods", tbGoods);
data.put("goodsDesc", tbGoodsDesc);
// 创建文件输出流
Writer out = new FileWriter(PAGE_DIR + goodsId + ".html");
// 使用模板生成商品详细页
Configuration configuration = FreeMarkerConfigurer.getConfiguration();
Template template = configuration.getTemplate("item.ftl");
template.process(data, out);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
(1)拷贝静态原型的item.html作为模板
(2)修改模板中的商品标题为插值:${goods.goodsName}
(3)测试
(1)将item.ftl中的头部和尾部分别放入另外两个模板中,在item.ftl中使用<#include>导入
(2)将商品基本信息使用插值替换到模板中
(1)因为图片列表为json串,我们需要将json串转换为对象
<#-- 处理图片列表Json串 -->
<#assign imageList = goodsDesc.itemImages?eval>
(2)遍历生成的对象,展示图片,注意限制图片放大镜的最大宽高
(3)效果
(1)将扩展信息的json串转换为对象
<#-- 处理扩展属性Json串 -->
<#if goodsDesc.customAttributeItems??>
<#assign attrList = goodsDesc.customAttributeItems?eval>
#if>
(2)遍历展示扩展信息
(3)效果
(1)将规格列表信息的json串转换为对象
<#-- 处理规格列表Json串 -->
<#if goodsDesc.specificationItems??>
<#assign specList = goodsDesc.specificationItems?eval>
#if>
(2)展示规格信息
(3)效果
(1)服务层实现(page-interface),修改ItemPageServiceImpl中的代码,主要为:查询分类信息
package com.pinyougou.page.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.mapper.TbGoodsDescMapper;
import com.pinyougou.mapper.TbGoodsMapper;
import com.pinyougou.mapper.TbItemCatMapper;
import com.pinyougou.page.service.ItemPageService;
import com.pinyougou.pojo.TbGoods;
import com.pinyougou.pojo.TbGoodsDesc;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import java.io.FileWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
/**
* 商品详情页静态化实现
* Author xushuai
* Description
*/
@Service
@Transactional
public class ItemPageServiceImpl implements ItemPageService {
@Autowired
private FreeMarkerConfigurer FreeMarkerConfigurer;
@Autowired
private TbGoodsMapper goodsMapper;
@Autowired
private TbGoodsDescMapper goodsDescMapper;
@Autowired
private TbItemCatMapper itemCatMapper;
@Value("${PAGE_DIR}")
private String PAGE_DIR;
@Override
public boolean genItemPage(Long goodsId) {
try {
// 获取数据
Map data = getDataMap(goodsId);
// 创建文件输出流
Writer out = new FileWriter(PAGE_DIR + goodsId + ".html");
// 使用模板生成商品详细页
Configuration configuration = FreeMarkerConfigurer.getConfiguration();
Template template = configuration.getTemplate("item.ftl");
template.process(data, out);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 获取模板需要的数据
*
* @param goodsId 商品ID
* @return java.util.Map
*/
private Map getDataMap(Long goodsId) {
Map data = new HashMap<>();
// 查询商品基本信息和商品扩展信息
TbGoods tbGoods = goodsMapper.selectByPrimaryKey(goodsId);
TbGoodsDesc tbGoodsDesc = goodsDescMapper.selectByPrimaryKey(goodsId);
// 查询商品分类信息
String category1 = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory1Id()).getName();
String category2 = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory2Id()).getName();
String category3 = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory3Id()).getName();
// 将查询到的数据封装到Map
data.put("goods", tbGoods);
data.put("goodsDesc", tbGoodsDesc);
data.put("category1", category1);
data.put("category2", category2);
data.put("category3", category3);
return data;
}
}
(2)替换模板中的文本
(3)效果
(1)拷贝相关的JS文件到存放商品详情页的目录中
(2)在js目录中新建controller文件夹,编写itemController.js文件
app.controller('itemController', function ($scope) {
// 数量
$scope.num = 1;
// 增加数量
$scope.incrNum = function() {
$scope.num ++;
}
// 减少数量
$scope.decrNum = function() {
if($scope.num > 1) {
$scope.num --;
}
}
});
(2)修改模板,在模板中引入相关JS文件和基本指令
(3)数量输入框绑定变量,+号和-号绑定单击事件
(1)在itemController.js中新增方法
// 选中的规格数据
$scope.specificationItems = {};
// 点击选中规格
$scope.selectSpec = function(name, value) {
// 设置给规格数据
$scope.specificationItems[name] = value;
}
// 判断当前规格是否被选中
$scope.isSelected = function(name, value) {
if ($scope.specificationItems[name] == value) {
return true;
} else {
return false;
}
}
(2)模板中绑定变量和单击事件
(3)效果
(1)修改ItemPageServiceImpl中的getDataMap方法,新增获取SKU列表数据的逻辑
(2)在页面中将获取到的SKU列表数据,生成变量,以后访问都通过访问这个变量
(3)测试效果
(1)在itemController.js中新增方法
// 当前选择SKU信息
$scope.sku={};
// 加载默认的SKU信息
$scope.loadSku = function() {
// 将skuList中的默认SKU信息赋给当前选择的SKU信息
$scope.sku = skuList[0];
}
(2)在模板标题和价格处,绑定变量
(3)默认选中默认的规格,修改loadSku方法
(4)效果
(1)在itemController.js中新增方法
// 判断两个对象是否内容相同
$scope.matchObject = function(object1, object2) {
for (var key in object1) {
// 校验当前键的值是否一致
if (object1[key] != object2[key]) {
return false;
}
}
for (var key in object2) {
// 校验当前键的值是否一致
if (object2[key] != object1[key]) {
return false;
}
}
// 内容相同
return true;
}
// 获取当前选中的SKU
findSku = function() {
for (var i = 0; i < skuList.length; i++) {
if ($scope.matchObject($scope.specificationItems,skuList[i].spec)) {// 规格匹配
// 当前循环到的sku为选中的sku
$scope.sku = skuList[i];
return ;
}
}
}
(2)在选择规格时,改变标题和价格,即在selectSpec中调用findSku方法
(3)效果
(1)在itemController.js中新增方法
// 加入商品到购物车
$scope.addToCart = function() {
alert("SKU:" + $scope.sku.id + "加入购物车成功,购买数量为:" + $scope.num);
}
(2)加入购物车按钮,绑定事件
(3)效果
(1)修改GoodsController中的updateStatus方法,新增逻辑
(1)修改生成静态网页的位置(page-service)
## 商品详细页存放位置
PAGE_DIR=E:\\temp\\freemarker\\
(2)将资源文件放入该文件夹中
(3)配置nginx.conf配置文件(windows版,测试用)
(1)修改图片链接地址