shop第三天
前端模板doT.js简介与安装
doT.js是什么?
是一个前端模板引擎。模板用于提取公共部分,变化的部分通过传入的数据,进行替换。
为什么使用前端模板引擎?
手动拼接字符串-复杂
手动拼接字符串页面响应速度比较慢,js模板加快页面响应速度
手动拼接字符串不安全
使用doT.js能够提高开发速度
为什么选择doT.js模板?
如何使用?
引入doT.js(doT.js不依赖于jQuery往往和jQuery结合,提高开发速度)
编写模板
调用模板
Demo.html
{{ for(var i=0;i
{{=it[i]}}
{{ } }}
{{ for(var i=0;i
{{=it[i].id}}
{{=it[i].name}}
{{ } }}
//模拟通过ajax从后台得到的数据
vardata=["1","2","3"];
// 获取模板
vartemp=doT.template($("#temp_01").text());
// 填充数据
$("#content_01").html(temp(data));
//模拟通过ajax从后台得到的数据
vardata2=[{"id":1,"name":"zhangsan"},{"id":2,"name":"lisi"},{"id":3,"name":"wangwu"}];
// 获取模板
vartemp2=doT.template($("#temp_02").text());
// 填充数据
$("#content_02").html(temp2(data2));
测试结果
商品列表-分页查询
Service服务层
GoodsCategoryService.java
/**
* 商品分类-查询所有商品分类
* @return
*/
ListselectCategoryList();
GoodsCategoryServiceImpl.java
/**
* 商品分类-查询所有商品分类
* @return
*/
@Override
publicListselectCategoryList() {
returngoodsCategoryMapper.selectByExample(newGoodsCategoryExample());
}
BrandService.java
/**
* 查询所有商品品牌
* @return
*/
ListselectBrandList();
BrandServiceImpl.java
/**
* 查询所有商品品牌
* @return
*/
@Override
publicListselectBrandList() {
returnbrandMapper.selectByExample(newBrandExample());
}
Controller控制层
GoodsController.java
/**
* 商品-列表-页面跳转
*
* @return
*/
@RequestMapping("list")
publicStringGoodsList(Modelmodel) {
//返回所有商品分类
ListgcList=goodsCategoryService.selectCategoryList();
model.addAttribute("gcList",gcList);
//返回所有商品品牌
ListbrandList=brandService.selectBrandList();
model.addAttribute("brandList",brandList);
return"goods/goods-list";
}
ftl页面
goods-list.ftl
删除页面无用的代码
处理分类列表和品牌列表,修改form表单中的域名称与对象属性一一对应
所有分类
<#listgcListasgc>
${gc.name}
#list>
class="form-control">
所有品牌
<#listbrandListasbrand>
${brand.name}
#list>
添加每页显示*条,处理搜索词和筛选,修改form表单中的域名称与对象属性一一对应
每页显示
class="form-control">
10
20
50
100
条
关键词
id="button-filter search-order"class="btn btn-primary">
筛选
修改BaseResult.java添加成功返回分页对象的方法
//成功返回的对象-带分页对象
public static BaseResult success(PageInfo> pageInfo) {
BaseResult result = new BaseResult();
result.setCode(BaseResultEnum.SUCCESS.getCode());
result.setMessage(BaseResultEnum.SUCCESS.getMessage());
result.setPageInfo(pageInfo);
return result;
}
Service服务层
GoodsService.java
/**
* 商品-列表-分页查询
* @param goods
* @param pageNum
* @param pageSize
* @return
*/
BaseResult selectGoodsListByPage(Goods goods,Integer pageNum,Integer pageSize);
GoodsServiceImpl.java
/**
* 商品-列表-分页查询
* @param goods
* @param pageNum
* @param pageSize
* @return
*/
@Override
public BaseResult selectGoodsListByPage(Goods goods, Integer pageNum, Integer pageSize) {
//构建分页对象
PageHelper.startPage(pageNum,pageSize);
//创建查询对象
GoodsExample example = new GoodsExample();
GoodsExample.Criteria criteria = example.createCriteria();
//设置查询条件
//分类参数
if (null!=goods.getCatId()&&0!=goods.getCatId()){
criteria.andCatIdEqualTo(goods.getCatId());
}
//品牌参数
if (null!=goods.getBrandId()&&0!=goods.getBrandId()){
criteria.andBrandIdEqualTo(goods.getBrandId());
}
//关键词
if (!StringUtils.isEmpty(goods.getGoodsName())){
criteria.andGoodsNameLike("%"+goods.getGoodsName()+"%");
}
//查询
List list = goodsMapper.selectByExample(example);
//将查询结果设置至分页对象
if (!CollectionUtils.isEmpty(list)){
PageInfo pageInfo = new PageInfo<>(list);
return BaseResult.success(pageInfo);
}
return BaseResult.error();
}
Controller层
/**
* 商品-列表-分页查询
* @param goods
* @param pageNum
* @param pageSize
* @return
*/
@RequestMapping("listForPage")
@ResponseBody
public BaseResult selectGoodsListByPage(Goods goods,Integer pageNum,Integer pageSize){
return goodsService.selectGoodsListByPage(goods,pageNum,pageSize);
}
ftl页面
head.ftl引入doT.min.js文件(项目对应包下引入doT.min.js文件)
在分类、品牌、每页显示、筛选上添加onchange="ajax_get_table(1);"
进入goods-list.ftl页面提交分页查询
$(document).ready(function () {
// ajax 加载商品列表
ajax_get_table(1);
});
//ajax抓取页面 page为当前第几页
function ajax_get_table(page) {
$.ajax({
url: "${ctx}/goods/listForPage",
type: "POST",
data: {
catId: $("#catId").val(),
brandId: $("#brandId").val(),
goodsName: $("#goodsName").val(),
pageNum: page,
pageSize: $("#pageSize").val()
},
dataType: "JSON",
success: function (result) {
if (200 == result.code) {
if (result.pageInfo.list.length > 0) {
//获取商品列表模板
var goodsTemp = doT.template($("#goodsTemplate").text());
//填充数据
$("#goodsContent").html(goodsTemp(result.pageInfo.list));
//获取分页模板
var pageTemp = doT.template($("#pageTemplate").text());
//填充数据
$("#pageContent").html(pageTemp(result.pageInfo));
} else {
layer.msg("该分类或品牌暂无商品信息!");
}
} else {
layer.msg("该分类或品牌暂无商品信息!");
}
},
error: function (result) {
console.log(result)
}
});
}
使用doT.js模板处理goods-list.ftl商品列表信息和分页
{{ for(var i = 0; i < it.length; i++){ }}
{{=it[i].goodsId}}
{{=it[i].goodsName}}
{{=it[i].goodsSn}}
{{=it[i].catId}}
{{=it[i].shopPrice}}
onpaste="this.value=this.value.replace(/[^\d.]/g,'')"
onchange="ajaxUpdateField(this);" name="store_count" size="4"
data-table="goods" data-id="143" value="{{=it[i].storeCount}}" type="text">
onpaste="this.value=this.value.replace(/[^\d]/g,'')"
onchange="updateSort('goods','goods_id','143','sort',this)" size="4"
value="{{=it[i].sort}}" type="text">
class="btn btn-info" title="查看详情">
class="fa fa-pencil">
title="删除">
{{ } }}
{{ if(it.hasPreviousPage){ }}
上一页
{{ } }}
{{ for(var i = 1; i <= it.pages; i++){ }}
{{=i}}
{{ } }}
{{ if(it.hasNextPage){ }}
下一页
{{ } }}
goods-list.ftl最终修改部分结果如下
商城项目中集成Redis实现缓存
shop-manager引入依赖
pom.xml
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
application.yml
# Redis配置
redis:
timeout: 10000ms # 连接超时时间
host: 192.168.10.100 # Redis服务器地址
port: 6379 # Redis服务器端口
database: 0 # 选择哪个库,默认0库
lettuce:
pool:
max-active: 1024 # 最大连接数,默认 8
max-wait: 10000ms # 最大连接阻塞等待时间,单位毫秒,默认 -1
max-idle: 200 # 最大空闲连接,默认 8
min-idle: 5 # 最小空闲连接,默认 0
# Redis Key
# 商品分类列表 Key
goods.category.list.key: goods:category:list:goodsCategoryList
RedisConfig.java
package com.xxxx.manager.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis配置类
*
* @author zhoubin
* @since 1.0.0
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(LettuceConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate<>();
//为string类型key设置序列器
redisTemplate.setKeySerializer(new StringRedisSerializer());
//为string类型value设置序列器
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//为hash类型key设置序列器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//为hash类型value设置序列器
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
shop-common添加Json工具类
JsonUtil.java
package com.xxxx.common.util;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
/**
* @see(功能介绍) : Json转换工具类
* @version(版本号) : 1.0
* @author(创建人) : zhoubin
*/
public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
/**
* 将对象转换成json字符串
*
* @param obj
* @return
*/
public static String object2JsonStr(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
//打印异常信息
e.printStackTrace();
}
return null;
}
/**
* 将字符串转换为对象
*
* @param 泛型
*/
public static T jsonStr2Object(String jsonStr, Class clazz) {
try {
return objectMapper.readValue(jsonStr.getBytes("UTF-8"), clazz);
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
*
Title: jsonToList
*
Description:
*
* @param jsonStr
* @param beanType
* @return
*/
public static List jsonToList(String jsonStr, Class beanType) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, beanType);
try {
List list = objectMapper.readValue(jsonStr, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Service层实现Redis缓存
商品列表查询
GoodsServiceImpl.java
/**
* 商品-列表-分页查询
*
* @param goods
* @param pageNum
* @param pageSize
* @return
*/
@Override
public BaseResult selectGoodsListByPage(Goods goods, Integer pageNum, Integer pageSize) {
//构建分页对象
PageHelper.startPage(pageNum, pageSize);
//创建查询对象
GoodsExample example = new GoodsExample();
GoodsExample.Criteria criteria = example.createCriteria();
//商品列表RedisKey
/*
分析:
此功能查询分为七种(所有条件都包含分页参数):
a. 无条件查询
b. 根据分类查询
c. 根据品牌查询
d. 根据关键词查询
e. 根据分类_品牌查询
f. 根据分类_关键词查询
g. 根据品牌_关键词查询
无条件查询key
goods:list:pageNum_:pageSize_:catId_:brandId_:goodsName_
条件查询key
goods:pageNum_:pageSize_:catId_123:brandId_:goodsName_(根据分类查询)
goods:pageNum_:pageSize_:catId_:brandId_123:goodsName_(根据品牌查询)
goods:pageNum_:pageSize_:catId_:brandId_:goodsName_华为(根据关键词查询)
goods:pageNum_:pageSize_:catId_123:brandId_123:goodsName_(根据分类_品牌查询)
goods:pageNum_:pageSize_:catId_123:brandId_:goodsName_华为(根据分类_关键词查询)
goods:pageNum_:pageSize_:catId_:brandId_123:goodsName_华为(根据品牌_关键词查询)
*/
String goodsKeyArr[] = new String[]{"goods:pageNum_" + pageNum + ":pageSize_" + pageSize + ":",
"catId_:",
"brandId_:",
"goodsName_:"};
//设置查询条件
//分类参数
if (null != goods.getCatId() && 0 != goods.getCatId()) {
criteria.andCatIdEqualTo(goods.getCatId());
goodsKeyArr[1] = "catId_" + goods.getCatId() + ":";
}
//品牌参数
if (null != goods.getBrandId() && 0 != goods.getBrandId()) {
criteria.andBrandIdEqualTo(goods.getBrandId());
goodsKeyArr[2] = "brandId" + goods.getBrandId() + ":";
}
//关键词
if (!StringUtils.isEmpty(goods.getGoodsName())) {
criteria.andGoodsNameLike("%" + goods.getGoodsName() + "%");
goodsKeyArr[3] = "goodsName_" + goods.getGoodsName() + ":";
}
ValueOperations valueOperations = redisTemplate.opsForValue();
//拼接Redis key
String goodsListKey = Arrays.stream(goodsKeyArr).collect(Collectors.joining());
//查询缓存,如果缓存中存在数据,直接返回
String pageInfoGoodsJson = valueOperations.get(goodsListKey);
if (!StringUtils.isEmpty(pageInfoGoodsJson)) {
return BaseResult.success(JsonUtil.jsonStr2Object(pageInfoGoodsJson, PageInfo.class));
}
//查询
List list = goodsMapper.selectByExample(example);
//将查询结果设置至分页对象
if (!CollectionUtils.isEmpty(list)) {
PageInfo pageInfo = new PageInfo<>(list);
//放入缓存
valueOperations.set(goodsListKey, JsonUtil.object2JsonStr(pageInfo));
return BaseResult.success(pageInfo);
} else {
//没有数据,将空数据缓存,设置失效时间60s
valueOperations.set(goodsListKey, JsonUtil.object2JsonStr(new PageInfo<>(new ArrayList())), 60,
TimeUnit.SECONDS);
}
return BaseResult.error();
}
商品分类查询
GoodsCategoryServiceImpl.java
/**
* 商品分类-列表
*
* @return
*/
@Override
public List selectCategoryListForView() {
ValueOperations valueOperations = redisTemplate.opsForValue();
//查询缓存,如果缓存中有数据,直接返回
String gcListJson = valueOperations.get(goodsCategoryListKey);
if (!StringUtils.isEmpty(gcListJson)){
return JsonUtil.jsonToList(gcListJson,GoodsCategoryVo.class);
}
//创建查询对象
GoodsCategoryExample example = new GoodsCategoryExample();
//查询所有的商品分类list
List list = goodsCategoryMapper.selectByExample(example);
//将List转成List
List gcvList = list.stream().map(e -> {
GoodsCategoryVo gcv = new GoodsCategoryVo();
BeanUtils.copyProperties(e, gcv);
return gcv;
}).collect(Collectors.toList());
/**
* 将List转成Map>
* 按parentId分组,key为parentId,value为parentId对应的List
*/
Map> map =
gcvList.stream().collect(Collectors.groupingBy(GoodsCategoryVo::getParentId));
/**
* 循环,将childrenList赋值
*/
gcvList.forEach(e -> e.setChildrenList(map.get(e.getId())));
/**
* 拦截器,返回Level为1的List,也就是顶级分类
*/
List gcvList01 = gcvList.stream().filter(e -> 1 == e.getLevel()).collect(Collectors.toList());
//放入缓存
valueOperations.set(goodsCategoryListKey, JsonUtil.object2JsonStr(gcvList01));
return gcvList01;
}
测试Redis缓存
GoodsServiceTest.java
package com.xxxx.manager;
import com.xxxx.common.result.BaseResult;
import com.xxxx.common.util.JsonUtil;
import com.xxxx.manager.pojo.Goods;
import com.xxxx.manager.service.GoodsService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 商品测试
*
* @author zhoubin
* @since 1.0.0
*/
@SpringBootTest(classes = ManagerApplication.class)
public class GoodsServiceTest {
@Autowired
private GoodsService goodsService;
@Test
public void testGoodsRedis(){
Goods goods = new Goods();
goods.setGoodsName("华为");
BaseResult baseResult = goodsService.selectGoodsListByPage(goods, 1, 10);
System.out.println(JsonUtil.object2JsonStr(baseResult.getPageInfo()));
}
}
缓存更新
新增或者修改或者删除成功以后,删除Redis缓存的信息
/**
* 商品新增-保存
*
* @param goods
* @return
*/
@Override
public BaseResult saveGoods(Goods goods) {
BaseResult baseResult = BaseResult.error();
//如果goodsId不为空直接返回错误
if (null != goods.getGoodsId()) {
return baseResult;
}
//goodsName必填
if (StringUtils.isEmpty(goods.getGoodsName())) {
return baseResult;
}
// 写操作中,清除Redis缓存数据,再次查询时新的数据将会放入缓存
redisTemplate.delete(redisTemplate.keys("goods*"));
//如果有详情,先转义
if (!StringUtils.isEmpty(goods.getGoodsContent())) {
goods.setGoodsContent(HtmlUtils.htmlEscape(goods.getGoodsContent(), "UTF-8"));
}
int result = goodsMapper.insertSelective(goods);
//如果成功返回信息里加入插入商品的主键
if (result > 0) {
baseResult = BaseResult.success();
baseResult.setMessage(String.valueOf(goods.getGoodsId()));
}
return baseResult;
}
/**
* 商品分类-新增分类-保存
*
* @param goodsCategory
* @return
*/
@Override
public int categorySave(GoodsCategory goodsCategory) {
//写操作中,清除Redis中的数据,再次查询时将新数据放入缓存中
redisTemplate.delete(redisTemplate.keys("goods*"));
return goodsCategoryMapper.insertSelective(goodsCategory);
}