电商项目day12(商品详情页面静态化&freemarker的入门)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangwei_620/article/details/85097686

今日目标:

掌握页面静态化技术

完成入门demo

熟练使用常用的demo

商品详情页面的展示

静态页面动态效果实现

一.freemarker入门Demo

1.首先为什么要使用freemarker技术?

FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出。
FreeMarker 与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成 XML,JSP 或 Java 等。

解决高并发访问的问题,因为这样非常消耗性能,这样做肯定不对

            1、商品数据量巨大,并且从三张表中获取数据。所以查询性能很差,用户体验很差
            2、商品详情查看功能是频繁操作,如果每次从数据库中获取数据,给数据库造成很大的访问压力。

那我们如何解决呢?

            1、将数据缓存到redis  数据量小,并且经常查询,很少发生变化的数据。
            2、页面静态化。

像我们这么多的商品数据是不能存到redis数据库中的,我们只能采用一种页面静态化的技术,

我们考虑什么时候生成页面呢?

           商品上架的时候。
            tb_goods完成商品上架

*****注意:商品上架时,同步上架商品到索引库,并同时生成该商品对应的静态html页面。
                商品下架时,同步删除索引库下架商品,并同时删除该商品对应的静态html页面。

freemarker模板的后缀是ftl

搭建一个工程实现freemarker入门demo

代码:


//        第一步:创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是
        Configuration configuration = new Configuration(Configuration.getVersion());
//        freemarker 的版本号。
//        第二步:设置模板文件所在的路径。
        configuration.setDirectoryForTemplateLoading(new File("D:\\IdeaProjects\\projectAll\\FreemarkDemo\\src\\main\\resources"));
//        第三步:设置模板文件使用的字符集。一般就是 utf-8.
        configuration.setDefaultEncoding("utf-8");
//        第四步:加载一个模板,创建一个模板对象。
        Template template = configuration.getTemplate("test.ftl");
//        第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
        Map map = new HashMap();
        map.put("name","张三");
        map.put("message","来自freemarker的世界");
        map.put("success",false);
        List goodList = new ArrayList();
        Map goods1 = new HashMap();
        goods1.put("name","苹果");
        goods1.put("price",5.8);
        Map goods2 = new HashMap();
        goods2.put("name","香蕉");
        goods2.put("price",9.9);
        goodList.add(goods1);
        goodList.add(goods2);
        map.put("goodList",goodList);
        //日期格式化
        map.put("today",new Date());
        //数字转化字符串
        map.put("point",123456789);
        map.put("aaa",null);
        map.put("bbb",null);
//        第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
        Writer out = new FileWriter(new File("F:\\test.html"));
//        第七步:调用模板对象的 process 方法输出文件。
        template.process(map,out);
//        第八步:关闭流
        out.close();

ftl的模板:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <#--我是注釋標簽-->
    ${name} 你好 ,我是 ${message}
</body>
</html>

结果如图:

二.freemarker的常用指令

1.FTL指令

 第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
        Map map = new HashMap();
        map.put("name","张三");
        map.put("message","来自freemarker的世界");
        map.put("success",false);
        List goodList = new ArrayList();
        Map goods1 = new HashMap();
        goods1.put("name","苹果");
        goods1.put("price",5.8);
        Map goods2 = new HashMap();
        goods2.put("name","香蕉");
        goods2.put("price",9.9);
        goodList.add(goods1);
        goodList.add(goods2);
        map.put("goodList",goodList);
        //日期格式化
        map.put("today",new Date());
        //数字转化字符串
        map.put("point",123456789);
        map.put("aaa",null);
        map.put("bbb",null);
//        第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。

模板的配置:

 第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
        Map map = new HashMap();
        map.put("name","张三");
        map.put("message","来自freemarker的世界");
        map.put("success",false);
        List goodList = new ArrayList();
        Map goods1 = new HashMap();
        goods1.put("name","苹果");
        goods1.put("price",5.8);
        Map goods2 = new HashMap();
        goods2.put("name","香蕉");
        goods2.put("price",9.9);
        goodList.add(goods1);
        goodList.add(goods2);
        map.put("goodList",goodList);
        //日期格式化
        map.put("today",new Date());
        //数字转化字符串
        map.put("point",123456789);
        map.put("aaa",null);
        map.put("bbb",null);
//        第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。

算符运算符:

1.逻辑运算符:

逻辑运算符有如下几个:
逻辑与:&&
逻辑或:||
逻辑非:!
逻辑运算符只能作用于布尔值,否则将产生错误

2.比较运算符

1 =或者==:判断两个值是否相等.
2 !=:判断两个值是否不等.
3 >或者 gt:判断左边值是否大于右边值
4 >=或者 gte:判断左边值是否大于等于右边值
5 <或者 lt:判断左边值是否小于右边值
6 <=或者 lte:判断左边值是否小于等于右边值
注意: =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且 FreeMarker 是精确比较,"x","x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker 会把>解释成 FTL 标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>

注意:一般用()把需要的逻辑判断的扩展起来

三.商品详情页面的分析以及实现

1.首先创建需要生成的freemarker的工程

page_web  page_service  page_interface    

2.分为两部分,组装数据和组装模板,首先组装数据

分析:我们分析商品详情页可知,需要三张表的数据   tb_goods   tb_goodsdesc    tb_item  我们通过组合实体类获得的三张表的数据

后台代码:

@Service
@Transactional
public class PageServiceImpl implements PageService {

    //一次注入三个表得数据
    @Autowired
    private TbGoodsMapper tbGoodsMapper;
    @Autowired
    private TbGoodsDescMapper tbGoodsDescMapper;
    @Autowired
    private TbItemMapper itemMapper;
    @Override
    public Goods findOne(Long goodsId) {
        //获取goods表的数据
        TbGoods tbGoods = tbGoodsMapper.selectByPrimaryKey(goodsId);
        //获得goodsdesc表的数据
        TbGoodsDesc tbGoodsDesc = tbGoodsDescMapper.selectByPrimaryKey(goodsId);
        //获得item表的数据
        TbItemExample example = new TbItemExample();
        TbItemExample.Criteria criteria = example.createCriteria();
        criteria.andGoodsIdEqualTo(goodsId);
        List<TbItem> itemList = itemMapper.selectByExample(example);
        Goods goods = new Goods();
        goods.setTbGoods(tbGoods);
        goods.setTbGoodsDesc(tbGoodsDesc);
        goods.setItems(itemList);
        return goods;
    }
@RestController
@RequestMapping("/page")
public class PageController {

    @Reference
    private PageService pageService;
    @Autowired
    private FreeMarkerConfigurer freeMarkerConfigurer;

    @RequestMapping("/genHtml")
    public String genHtml(Long goodsId){

        try {
//        第一步:创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是
            Configuration configuration = freeMarkerConfigurer.getConfiguration();
//        freemarker 的版本号。
//        第四步:加载一个模板,创建一个模板对象。
            Template template = configuration.getTemplate("item.ftl");
//        第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
           Goods goods = pageService.findOne(goodsId);
            List<TbItem> items = goods.getItemList();
            for (TbItem item : items) {
    //      在这呢我们不要将组合实体类返回,因为返回还的遍历,取值,我们直接在这封装给map就可以了
                Map<String,Object> map = new HashMap<>();
                map.put("goods", goods);
                map.put("item",item);
//        第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
                Writer out = new FileWriter("F:\\item\\"+item.getId()+".html");
//        第七步:调用模板对象的 process 方法输出文件。
                template.process(map,out);
//        第八步:关闭流
                out.close();
            }
                return "success.............";
        } catch (Exception e) {
            e.printStackTrace();
            return "fail............";
        }
    }
}

模板的抽取:

抽取完一部分,记得及时引入刚刚抽取的代码

页面展示完后,我们添加需要的静态资源就能看到,

四.页面的展示

1.需求分析:

如下图所示:

我们知道goods表中有

category1Id  category2Id   category2Id

后台代码:

 List<TbItem> itemList = itemMapper.selectByExample(example);
        //组装分类的信息
        String category1Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory1Id()).getName();
        String category2Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory2Id()).getName();
        String category3Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory3Id()).getName();
        Goods goods = new Goods();
        //获取封装的的map集合
        Map<String,String> categoryMap = new HashMap<>();
        //将数据封装到map集合中
        categoryMap.put("category1Name",category1Name);
        categoryMap.put("category2Name",category2Name);
        categoryMap.put("category3Name",category3Name);
 goods.setCategoryMap(categoryMap);

注意我们一定要在goods的组合实体类中添加categoryMap的Map集合,最后注意打包到仓库

2.替换照片

{"color":"金色","url":"http://192.168.25.133/group1/M00/00/01/wKgZhVwbIGmABW-LAACnGz_9YE0694.jpg"}]
					-->
						<#assign imageList=goods.tbGoodsDesc.itemImages?eval>
					<!--放大镜效果-->
					<div class="zoom">
						<#if (imageList?size>0)>
							<!--默认第一个预览-->
							<div id="preview" class="spec-preview">
								<span class="jqzoom"><img jqimg="${imageList[0].url}" src="${imageList[0].url}" width="400px" height="1000px"/></span>
							</div>

3.实现规格的选项

<#--
							[{"attributeValue":["移动4G","联通3G"],"attributeName":"网络"},
							{"attributeValue":["32G","128G"],"attributeName":"机身内存"}]
							-->
								<#assign specList=goods.tbGoodsDesc.specificationItems?eval>
								<#list specList as spec>
									<dl>
										<dt>
											<div class="fl title">
											<i>${spec.attributeName}</i>
										</div>
										</dt>
										<#list spec.attributeValue as value>
										<dd><a href="javascript:;" class="selected">${value}<span title="点击取消选择">&nbsp;</span>
												</a></dd>
										</#list>
									</dl>
								</#list>

五.静态页面的动态效果实现

1.需求:

页面:
<div class="controls">
										<input autocomplete="off" type="text" value="{{num}}" minnum="1" class="itxt" />
										<a href="javascript:void(0)" class="increment plus" ng-click="addNum(num+1)">+</a>
										<a href="javascript:void(0)" class="increment mins" ng-click="addNum(num-1)">-</a>
									</div>

pageController.js

app.controller("pageController",function($scope,$controller){

	//继承代码
	$controller("baseController",{$scope:$scope});

	//商品数量的加减动态数量
	$scope.num = 1;
	$scope.addNum = function(num){
		$scope.num = num;

		if($scope.num<1){
			$scope.num=1;
		}
	
	}
	
})

注意:一定要在模板中映入资源

<#--映入资源-->
    <script type="text/javascript" src="plugins/angularjs/angular.min.js">  </script>
    <script type="text/javascript" src="js/base.js">  </script>
    <script type="text/javascript" src="js/controller/baseController.js">  </script>
    <script type="text/javascript" src="js/controller/pageController.js">  </script>

2.实现规格选项的动态切换

思路:通过获得规格列表,判断是否相等,如果都相等,获取它的id值,然后拼接成url地址,重新刷新页面通过location.href

app.controller("pageController",function($scope,$controller){
	
	//继承代码
	$controller("baseController",{$scope:$scope});
	
	//商品数量加减动态效果
	$scope.num=1;
	
	$scope.addNum=function(num){
		$scope.num=num;
		
		if($scope.num<1){
			$scope.num=1;
		}
		
	}
	
	//当前商品记录规格对象 var spec={"网络":"移动3G","机身内存":"64G"};
	
	//当前商品规格选项是否选择的方法
	$scope.isSelected=function(specName,specOption){
		if(spec[specName]==specOption){
			return true;
		}else{
			return false;
		}
	}
	
	//规格选项切换动态效果实现
	$scope.updateSpecAttribute=function(specName,specOption){
		//更新当前商品规格数据
		spec[specName]=specOption;
		
		//构建规格组合列表
		/*var specList=[
		        {id:1369284,spec:{"网络":"移动3G","机身内存":"64G"}},
		        {id:1369285,spec:{"网络":"移动3G","机身内存":"128G"}},
		        {id:1369286,spec:{"网络":"移动4G","机身内存":"64G"}},
		        {id:1369287,spec:{"网络":"移动4G","机身内存":"128G"}},
		];*/

		for(var i=0;i<specList.length;i++){
			if(matchObject(specList[i].spec,spec)){
				location.href=specList[i].id+".html";
			}
		}

		
	}
	
	//map1={"网络":"移动3G","机身内存":"64G"}
	//map2={"网络":"移动3G","机身内存":"64G","颜色":"red"}
	matchObject=function(map1,map2){
		
		for(var k in map1){
			if(map1[k]!=map2[k]){
				return false;
			}
		}
		//反向遍历,可以对于第二个多的在判断
		for(var k in map2){
			if(map2[k]!=map1[k]){
				return false;
			}
		}
		
		return true;
		
	}
	
	
	
});

页面代码:

 <script type="text/javascript">
        //当前商品记录规格对象
        var spec=${item.spec};

        //构建规格组合列表
        var specList=[
		    <#list goods.itemList as item>
		        {id:${item.id?c},spec:${item.spec}},
            </#list>
        ];

    </script>
<#--class="selected"-->
                                    <#list spec.attributeValue as value>
										<dd><a href="javascript:;" ng-click="updateSpecAttribute('${spec.attributeName}','${value}')" class="{{isSelected('${spec.attributeName}','${value}')?'selected':''}}">${value}<span title="点击取消选择">&nbsp;</span></a></dd>
                                    </#list>
									</dl>
                                </#list>

 

六.总结

猜你喜欢

转载自blog.csdn.net/wangwei_620/article/details/85097686