基于SSM框架大型分布式电商系统开发(7-8)

目录

前言

出差中,先定好标题,回头补上。。。
因为是根据大佬的项目点滴做起,如果看到此博客侵犯利益,请告知立即删除
商品修改与审核+广告管理与缓存解决方案

第七章 商家后台-商品修改与审核

1.商家后台-商品管理【商品列表】

1.1 需求分析

在商家后台,显示该商家的商品列表信息,如下图
在这里插入图片描述

1.2 查询商家商品列表
1.2.1 后端代码

修改pinyougou-shop-web工程的GoodsController.java的search方法

@RequestMapping("/search")
	public PageResult search(@RequestBody TbGoods goods, int page, int rows  ){
		//获取商家ID
		String sellerId = SecurityContextHolder.getContext().getAuthentication().getName();
		//添加查询条件 
		goods.setSellerId(sellerId);		
		return goodsService.findPage(goods, page, rows);		
	}

修改pinyougou-sellergoods-service 工程com.pinyougou.sellergoods.service.impl 的findPage方法,修改条件构建部分代码,将原来的模糊匹配修改为精确匹配

if(goods.getSellerId()!=null && goods.getSellerId().length()>0){
	//criteria.andSellerIdLike("%"+goods.getSellerId()+"%");
	criteria.andSellerIdEqualTo(goods.getSellerId());
}
1.2.2 前端代码

修改goods.html. 引入js

script type="text/javascript" src="../plugins/angularjs/angular.min.js"></script>
<!-- 分页组件开始 -->
<script src="../plugins/angularjs/pagination.js"></script>
<link rel="stylesheet" href="../plugins/angularjs/pagination.css">
<!-- 分页组件结束 -->
<script type="text/javascript" src="../js/base_pagination.js"></script>	
<script type="text/javascript" src="../js/service/goodsService.js"></script>
<script type="text/javascript" src="../js/service/itemCatService.js"></script>
<script type="text/javascript" src="../js/service/uploadService.js"></script>
<script type="text/javascript" src="../js/service/typeTemplateService.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
<script type="text/javascript" src="../js/controller/goodsController.js"></script>

添加指令

<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController">

在页面上放置分页控件

<tm-pagination conf="paginationConf"></tm-pagination>

循环列表

<tr ng-repeat="entity in list">
    <td><input  type="checkbox"></td>			                              
   	<td>{{entity.id}}</td>
    <td>{{entity.goodsName}}</td>
    <td>{{entity.price}}</td>
    <td>{{entity.category1Id}}</td>
    <td>{{entity.category2Id}}</td>
    <td>{{entity.category3Id}}</td>
    <td>
    	{{entity.auditStatus}}
    </td>		                                  
    <td class="text-center">                                          
   	  <button type="button" class="btn bg-olive btn-xs">修改</button>                  
    </td>
</tr>

显示效果如下:
在这里插入图片描述

1.3 显示状态

修改goodsController.js,添加state数组

$scope.status=['未审核','已审核','审核未通过','关闭'];//商品状态,按照对应的状态码去排列

修改列表显示
{{status[entity.auditStatus]}}
显示效果如下:
在这里插入图片描述

1.4 显示分类

在这里插入图片描述
我们现在的列表中的分类仍然显示ID
如何才能显示分类的名称呢?
方案一:在后端代码写关联查询语句,返回的数据中直接有分类名称。
方案二:在前端代码用ID去查询后端,异步返回商品分类名称。
我们目前采用方案二:
(1)修改goodsController

$scope.itemCatList=[];//商品分类列表
//加载商品分类列表
$scope.findItemCatList=function(){		
	itemCatService.findAll().success(
			function(response){							
				for(var i=0;i<response.length;i++){
					$scope.itemCatList[response[i].id]=response[i].name;
				}
			}
	);
}

代码解释:因为我们需要根据分类ID得到分类名称,所以我们将返回的分页结果以数组形式再次封装。
(2)修改goods.html ,增加初始化调用

<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController" 
ng-init="findItemCatList()">

(3)修改goods.html , 修改列表

<td>{{itemCatList[entity.category1Id]}}</td>
<td>{{itemCatList[entity.category2Id]}}</td>
<td>{{itemCatList[entity.category3Id]}}</td>
1.5条件查询

根据状态和商品名称进行查询
修改goods.html

<div class="has-feedback">
    	状态:<select ng-model="searchEntity.auditStatus">
      	      <option value="">全部</option>      
               <option value="0">未审核</option>    
               <option value="1">已审核</option>    
               <option value="2">审核未通过</option>    
               <option value="3">关闭</option>                                     
           </select>
		商品名称:<input ng-model="searchEntity.goodsName">						
		<button class="btn btn-default" ng-click="reloadList()">查询</button>                                    
</div>

2.商家后台-商品管理【商品修改】

2.1需求分析

我们首选读取商品分类、商品名称、品牌,副标题,价格等信息
在这里插入图片描述

2.2.1后端代码

(1)修改pinyougou-sellergoods-interface的GoodsService.java

/**
	 * 根据ID获取实体
	 * @param id
	 * @return
	 */
	public Goods   findOne(Long id);

(2)修改pinyougou-sellergoods-service的GoodsServiceImpl.java

@Override
public Goods findOne(Long id) {
	Goods goods=new Goods();
	TbGoods tbGoods = goodsMapper.selectByPrimaryKey(id);
	goods.setGoods(tbGoods);
	TbGoodsDesc tbGoodsDesc = goodsDescMapper.selectByPrimaryKey(id);
	goods.setGoodsDesc(tbGoodsDesc);
	return goods;
}

(3)修改pinyougou-shop-web(和pinyougou-manager-web)的GoodsController.java

/**
 * 获取实体
 * @param id
 * @return
 */
@RequestMapping("/findOne")
public Goods findOne(Long id){
	return goodsService.findOne(id);		
}
2.2.2 前端代码

(1)在goodsController中引入$location服务

//商品控制层(商家后台)
app.controller('goodsController',function($scope,$controller,$location,goodsService,uploadService,item_catService,
type_templateService){
......

(2)修改goodsController 添加代码:

//查询实体 
	$scope.findOne=function(){			
		var id= $location.search()['id'];//获取地址栏上的参数值
		if(id==null){
			return ;
		}
		goodsService.findOne(id).success(
			function(response){
				$scope.entity= response;					
			}
		);				
	}

在goods_edit.html页面上添加指令

<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController"
 ng-init="selectItemCat1List();findOne()">

测试:
地址栏输入
http://localhost:9102/admin/goods_edit.html#?id=149187842867969
注意:?前要加# ,则是angularJS的地址路由的书写形式
在这里插入图片描述

2.3读取商品介绍(富文本编辑器)

修改前端代码 goodsController
//查询实体

$scope.findOne=function(){			
		.................
		goodsService.findOne(id).success(
			function(response){
				$scope.entity= response;	
				//向富文本编辑器添加商品介绍
				editor.html($scope.entity.goodsDesc.introduction);
			}
		);				
}
2.4显示商品图片列表

修改goodsController.js ,在dataLogic方法添加代码,将图片列表由字符串转换为json集合对象

//查询实体 
$scope.findOne=function(){		
         ..............	
		//如果有ID,则查询实体
		goodsService.findOne(id).success(
			function(response){
				 $scope.entity= response;	
				 //向富文本编辑器添加商品介绍
				 editor.html($scope.entity.goodsDesc.introduction);
				 //显示图片列表
                 $scope.entity.goodsDesc.itemImages= JSON.parse($scope.entity.goodsDesc.itemImages);
			}
		);				
}
2.5读取商品扩展属性

修改goodsController.js

//查询实体 
	$scope.findOne=function(){			
		.........
		goodsService.findOne(id).success(
			function(response){
				.......................
				//显示扩展属性
				$scope.entity.goodsDesc.customAttributeItems=JSON.parse($scope.entity.goodsDesc.customAttributeItems);	
			}
		);				
	}	

经过测试,我们发现扩展属性值并没有读取出来,这是因为与下列代码发生冲突

$scope.$watch('entity.goods.typeTemplateId',function(newValue,oldValue){
     ......
$scope.entity.goodsDesc.customAttributeItems = JSON.parse($scope.typeTemplate.customAttributeItems);//扩展属性
}

我们读取出来的值被覆盖了,我们需要改写代码, 添加判断,当用户没有传递id参数时再执行此逻辑

//监控模板ID ,读取品牌列表
$scope.$watch('entity.goods.typeTemplateId',function(newValue,oldValue){
		//读取品牌列表和扩展属性
		typeTemplateService.findOne(newValue).success(
			function(response){
				.......
				//如果没有ID,则加载模板中的扩展数据
				if($location.search()['id']==null){
					$scope.entity.goodsDesc.customAttributeItems = JSON.parse($scope.typeTemplate.customAttributeItems);//扩展属性	
				}				
			}
		);
		.......
});
2.6读取商品规格属性

修改goodsController

//查询实体 
	$scope.findOne=function(){		
		......
		goodsService.findOne(id).success(
			function(response){
				$scope.entity= response;		
				editor.html($scope.entity.goodsDesc.introduction);//商品介绍
				$scope.entity.goodsDesc.itemImages= JSON.parse($scope.entity.goodsDesc.itemImages);//图片列表
				//扩展属性列表
				$scope.entity.goodsDesc.customAttributeItems =JSON.parse($scope.entity.goodsDesc.customAttributeItems);
				//规格				
				$scope.entity.goodsDesc.specificationItems=JSON.parse($scope.entity.goodsDesc.specificationItems);				
			}
		);				
	}
//根据规格名称和选项名称返回是否被勾选,这样可以动态的将复选框置为勾选状态
$scope.checkAttributeValue=function(specName,optionName){
	var items= $scope.entity.goodsDesc.specificationItems;
	//判断items集合对象里面,是否存在attributeName这个key,并且对应的值是否是specName
	var object= $scope.searchObjectByKey(items,'attributeName',specName);
	if(object==null){
		return false;
	}else{
		if(object.attributeValue.indexOf(optionName)>=0){
			return true;
		}else{
			return false;
		}
	}			
}

修改页面上规格面板的复选框,运用 ng-checked指令控制复选框的勾选状态

<input  type="checkbox"                          		
ng-click="updateSpecAttribute($event,pojo.text,p.optionName);createSKUTable()"          		
ng-checked="checkAttributeValue(pojo.text,p.optionName)">{{p.optionName}}
2.7读取SKU数据

显示SKU商品列表,并自动读取价格、库存等数据加载到列表中
在这里插入图片描述

2.7.1后端代码
在GoodsServiceImpl的findOne方法中加载SKU商品数据
		//查询SKU商品列表
		TbItemExample example=new TbItemExample();
		com.pinyougou.pojo.TbItemExample.Criteria criteria = example.createCriteria();
		criteria.andGoodsIdEqualTo(id);//查询条件:商品ID
		List<TbItem> itemList = itemMapper.selectByExample(example);		
		goods.setItemList(itemList);
2.7.2前端代码
在goodsController.js修改findOne方法的代码
//查询实体 
$scope.findOne=function(){			
	........
	goodsService.findOne(id).success(
		function(response){
			$scope.entity= response;	
			.........			
			//SKU列表规格列转换				
			for( var i=0;i<$scope.entity.itemList.length;i++ ){
			    $scope.entity.itemList[i].spec = JSON.parse( $scope.entity.itemList[i].spec);		
			}			
		}
	);				
}
2.8保存数据
2.8.1后端代码

修改 pinyougou-sellergoods-interface 的 GoodsService.java

public void update(Goods goods);

修改pinyougou-sellergoods-service的GoodsServiceImpl ,将SKU列表插入的代码提取出来,封装到私有方法中

/**
 * 插入SKU列表数据
 * @param goods
 */
private void saveItemList(Goods goods){		
	if("1".equals(goods.getGoods().getIsEnableSpec())){
		for(TbItem item :goods.getItemList()){
			.........中间代码略
		}		
	}else{			
		TbItem item=new TbItem();
		.........中间代码略
		itemMapper.insert(item);
	}		
}

在add方法中调用 此方法,修改如下:

public void add(Goods goods) {
	goods.getGoods().setAuditStatus("0");		
	goodsMapper.insert(goods.getGoods());	//插入商品表
	goods.getGoodsDesc().setGoodsId(goods.getGoods().getId());
	goodsDescMapper.insert(goods.getGoodsDesc());//插入商品扩展数据
	saveItemList(goods);//插入商品SKU列表数据
}

接下来,我们修改update方法,实现修改

public void update(Goods goods){
	goods.getGoods().setAuditStatus("0");//设置未申请状态:如果是经过修改的商品,需要重新设置状态
	goodsMapper.updateByPrimaryKey(goods.getGoods());//保存商品表
	goodsDescMapper.updateByPrimaryKey(goods.getGoodsDesc());//保存商品扩展表
	//删除原有的sku列表数据		
	TbItemExample example=new TbItemExample();
	com.pinyougou.pojo.TbItemExample.Criteria criteria = example.createCriteria();
	criteria.andGoodsIdEqualTo(goods.getGoods().getId());	
	itemMapper.deleteByExample(example);
	//添加新的sku列表数据
	saveItemList(goods);//插入商品SKU列表数据	
}	

修改pinyougou-manager-web工程的GoodsController.java

@RequestMapping("/update")
	public Result update(@RequestBody Goods goods){
		......
	}	

修改pinyougou-shop-web工程的GoodsController.java

/**
	 * 修改
	 * @param goods
	 * @return
	 */
	@RequestMapping("/update")
	public Result update(@RequestBody Goods goods){
		//校验是否是当前商家的id		
		Goods goods2 = goodsService.findOne(goods.getGoods().getId());
		//获取当前登录的商家ID
		String sellerId = SecurityContextHolder.getContext().getAuthentication().getName();
		//如果传递过来的商家ID并不是当前登录的用户的ID,则属于非法操作
		if(!goods2.getGoods().getSellerId().equals(sellerId) ||  !goods.getGoods().getSellerId().equals(sellerId) ){
			return new Result(false, "操作非法");		
		}		
		try {
			goodsService.update(goods);
			return new Result(true, "修改成功");
		} catch (Exception e) {
			e.printStackTrace();
			return new Result(false, "修改失败");
		}
	}

代码解释:出于安全考虑,在商户后台执行的商品修改,必须要校验提交的商品属于该商户

2.8.2前端代码

(1)修改goodsController.js ,新增保存的方法

//保存 
$scope.save=function(){			
	//提取文本编辑器的值
	$scope.entity.goodsDesc.introduction=editor.html();	
	var serviceObject;//服务层对象  				
	if($scope.entity.goods.id!=null){//如果有ID
		serviceObject=goodsService.update( $scope.entity ); //修改  
	}else{
		serviceObject=goodsService.add( $scope.entity  );//增加 
	}				
	serviceObject.success(
		function(response){
			if(response.success){
				alert('保存成功');					
				$scope.entity={};
				editor.html("");
			}else{
				alert(response.message);
			}
		}		
	);				
}

(2)修改goods_edit.html 调用

<button class="btn btn-primary" ng-click="save()"><i class="fa fa-save"></i>保存</button>
2.9页面跳转

(1)由商品列表页跳转到商品编辑页
修改goods.html表格行的修改按钮
<a href="goods_edit.html#?id={{entity.id}}" class="btn bg-olive btn-xs">修改</a>

(2)由商品编辑页跳转到商品列表
修改goods_edit.html 的返回列表按钮
<a href="goods.html" class="btn btn-default">返回列表</a>

(3)保存成功后返回列表页面

    //保存 
	$scope.save=function(){		
		.....		
		serviceObject.success(
			function(response){
				if(response.success){					
					location.href="goods.html";//跳转到商品列表页
				}else{
					alert(response.message);
				}
			}		
		);				
	}

3.运营商后台-商品管理【商品审核】

3.1待审核商品列表

需求:参照商家后台商品列表。代码:
(1)修改pinyougou-manager-web的goodsController.js,注入itemCatService,添加代码

$scope.status=['未审核','已审核','审核未通过','关闭'];//商品状态
$scope.itemCatList=[];//商品分类列表
//查询商品分类
$scope.findItemCatList=function(){
	itemCatService.findAll().success(
		function(response){
			for(var i=0;i<response.length;i++){
				$scope.itemCatList[response[i].id ]=response[i].name;		
			}					
		}		
	);		
}

(2)修改goods.html ,引入js

<script type="text/javascript" src="../plugins/angularjs/angular.min.js"></script>
<!-- 分页组件开始 -->
<script src="../plugins/angularjs/pagination.js"></script>
<link rel="stylesheet" href="../plugins/angularjs/pagination.css">
<!-- 分页组件结束 -->
<script type="text/javascript" src="../js/base_pagination.js"></script>	
<script type="text/javascript" src="../js/service/goodsService.js"></script>
<script type="text/javascript" src="../js/service/itemCatService.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
<script type="text/javascript" src="../js/controller/goodsController.js"></script>

(3)指令,完成初始调用

<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController" 
ng-init="searchEntity={auditStatus:'0'};findItemCatList()">

(4)循环列表

<tr ng-repeat="entity in list">
    <td><input  type="checkbox"></td>			                              
   	<td>{{entity.id}}</td>
    <td>{{entity.goodsName}}</td>
    <td>{{entity.price}}</td>
    <td>{{itemCatList[entity.category1Id]}}</td>
	<td>{{itemCatList[entity.category2Id]}}</td>
	<td>{{itemCatList[entity.category3Id]}}</td>
    <td>{{status[entity.auditStatus]}}</td>		                                  
    <td class="text-center"> </td>
</tr>

(5)分页控件
<tm-pagination conf="paginationConf"></tm-pagination>

3.2商品详情展示

需求:点击列表右侧的“详情”按钮,弹出窗口显示商品信息。代码略

3.3商品审核与驳回

需求:商品审核的状态值为1,驳回的状态值为2 。用户在列表中选中ID后,点击审核或驳回,修改商品状态,并刷新列表。

3.3.1后端代码

(1)在pinyougou-sellergoods-interface的GoodsService.java新增方法定义

   /**
	 * 批量修改状态
	 * @param ids
	 * @param status
	 */
	public void updateStatus(Long []ids,String status);

(2)在pinyougou-sellergoods-service的GoodsServiceImpl.java实现该方法

public void updateStatus(Long[] ids, String status) {
    //可以采用where id exits...的办法,TODO:待修改
	for(Long id:ids){
		TbGoods goods = goodsMapper.selectByPrimaryKey(id);
		goods.setAuditStatus(status);
		goodsMapper.updateByPrimaryKey(goods);
	}
}

(3)在pinyougou-shop-web的GoodsController.java新增方法

/**
	 * 更新状态
	 * @param ids
	 * @param status
	 */
	@RequestMapping("/updateStatus")
	public Result updateStatus(Long[] ids, String status){		
		try {
			goodsService.updateStatus(ids, status);
			return new Result(true, "成功");
		} catch (Exception e) {
			e.printStackTrace();
			return new Result(false, "失败");
		}
	}
3.3.2前端代码

(1)修改pinyougou-manager-web的goodsService.js,增加方法

//更改状态
this.updateStatus=function(ids,status){
	return $http.get('../goods/updateStatus.do?ids='+ids+"&status="+status);
}  

(2)修改pinyougou-manager-web的goodsController.js ,增加方法

//更改状态
	$scope.updateStatus=function(status){		
		goodsService.updateStatus($scope.selectIds,status).success(
			function(response){
				if(response.success){//成功
					$scope.reloadList();//刷新列表
					$scope.selectIds=[];//清空ID集合
				}else{
					alert(response.message);
				}
			}
		);		
	}

(3)修改pinyougou-manager-web的goods.html 页面,为复选框绑定事件指令
<input type="checkbox" ng-click="updateSelection($event,entity.id)" >
(4)修改页面上的审核通过和驳回按钮

<button type="button" class="btn btn-default" title="审核通过" ng-click="updateStatus('1')"><i class="fa fa-check">
</i> 审核通过</button>
<button type="button" class="btn btn-default" title="驳回" ng-click="updateStatus('2')" ><i class="fa fa-ban">
</i> 驳回</button>

4.运营商后台-商品管理【商品删除】

4.1需求分析

我们为商品管理提供商品删除功能,用户选中部分商品,点击删除按钮即可实现商品删除。注意,这里的删除并非是物理删除,而是修改tb_goods表的is_delete字段为1 ,我们可以称之为“逻辑删除”

4.2逻辑删除的实现

修改pinyougou-sellergoods-service工程的GoodsServiceImpl.java的delete方法

    /**
	 * 批量删除,TODO:待修改
	 */
	@Override
	public void delete(Long[] ids) {
		for(Long id:ids){
			TbGoods goods = goodsMapper.selectByPrimaryKey(id);
			goods.setIsDelete("1");
			goodsMapper.updateByPrimaryKey(goods);
		}		
	}
4.2.2前端代码

修改pinyougou-manager-web的goods.html上的删除按钮

<button type="button" class="btn btn-default" title="删除" ng-click="dele()"><i class="fa fa-trash-o"></i> 删除</button>
4.3排除已删除记录

修改pinyougou-sellergoods-service工程GoodsServiceImpl.java的findPage方法,添加以下代码:
criteria.andIsDeleteIsNull();//非删除状态

5.商家后台-【商品上下架】

5.1需求分析

什么是商品上下架?其实上下架也是商品的一个状态,但是不同于审核状态。审核状态的控制权在运营商手中,而上下架的控制权在商户手中。商户可以随时将一个已审核状态的商品上架或下架。上架表示正常销售,而下架则表示暂停销售。

5.2实现思路提示

其实商品的上下架就是对上下架状态的修改。字段为tb_goods表的is_marketable字段。1表示上架、0表示下架。

6.注解式事务配置

6.1事务异常测试

我们修改pinyougou-sellergoods-service工程GoodsServiceImpl.java的add方法

   /**
	 * 增加
	 */
	@Override
	public void add(Goods goods) {
		goods.getGoods().setAuditStatus("0");		
		goodsMapper.insert(goods.getGoods());	//插入商品表
		int x=1/0;
		goods.getGoodsDesc().setGoodsId(goods.getGoods().getId());
		goodsDescMapper.insert(goods.getGoodsDesc());//插入商品扩展数据
		saveItemList(goods);//插入商品SKU列表数据
	}

在插入商品表后,人为制造一个异常。我们运行程序,新增商品数据,观察运行结果。
通过观察,我们发现,程序发生异常 ,商品表仍然会存储记录,这是不符合我们要求的。这是因为我们目前的系统还没有配置事务。

6.2注解式事务解决方案
6.2.1配置文件

在pinyougou-sellergoods-service工程的spring目录下创建applicationContext-tx.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 事务管理器  -->  
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="dataSource" />  
    </bean>  
    <!-- 开启事务控制的注解支持 -->  
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
6.2.2在方法上添加注解
/**
 * 服务实现层
 * @author Administrator
 *
 */
@Service
@Transactional
public class GoodsServiceImpl implements GoodsService{
........
} 

经过测试,我们发现,系统发生异常,商品表不会新增记录,事务配置成功。
删除掉测试代码int x=1/0
我们需要将所有涉及多表操作的服务类添加事务注解,例如SpecificationServiceImpl类

第八章 网站前台-广告管理与缓存解决方案

章节目标
目标1:了解网站前台的页面以及广告相关表结构
目标2:完成运营商后台广告类型管理与广告管理
目标3:完成前台工程广告轮播图的展示
目标4:使用SpringDataRedis操作字符串、set、List、hash等类型缓存
目标5:使用SpringDataRedis实现广告数据的缓存

1.网站前台分析

1.1网站前台有哪些页面

(1)网站首页
(2)商家(店铺)首页
(3)商品详细页
(4)商品搜索页
(5)购物车列表页
(6)购物选项选择页
(7)支付页
(8)用户注册页
(9)用户登陆页
(10)用户中心页等

1.2网站首页广告

(1)首页海报(轮播图)
(2)今日推荐
(3)猜你喜欢
(4)楼层广告

1.3数据库表结构分析

tb_content_category 广告分类表

字段类型长度含义
idBigint主键
namevarchar255广告分类名称

tb_content 广告表

字段类型长度含义
idBigint主键
category_idBigint广告分类ID
titlevarchar200广告标题
urlvarchar500广告链接
picvarchar300图片地址
statusvarchar1状态
sort_orderint排序

2.运营商后台-广告类型及广告管理

2.1需求分析

实现广告类型表与广告表的增删改查

2.2准备工作

2.2.1构建工程

构建工程
(1)pinyougou-content-interface
引入依赖pinyougou-pojo
创建包com.pinyougou.content.service

(2)pinyougou-content-service (WAR)
引入依赖参见pinyougou-sellergoods-service
引入tomcat插件配置 ,指定tomcat的运行端口为9002
为pinyougou-content-service 工程添加web.xml
创建包 com.pinyougou.content.service.impl
添加spring相关配置文件
在这里插入图片描述
applicationContext-service.xml

<dubbo:protocol name="dubbo" port="20882"></dubbo:protocol>
<dubbo:application name="pinyougou-content-service"/>  
<dubbo:registry address="zookeeper://192.168.25.129:2181"/>    
<dubbo:annotation package="com.pinyougou.content.service.impl" />  

注意:我们目前有两个服务工程,当两个工程同时启动时会发生端口冲突,因为连接dubbox注册中心的端口默认是20880。所以我们需要配置一下pinyougou-content-service工程的dubbox端口

(3)pinyougou-manager-web工程引入依赖pinyougou-content-interface

2.2.2生成代码拷入工程在这里插入图片描述在这里插入图片描述在这里插入图片描述

拷贝JS代码到pinyougou-manager-web
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试运行广告分类管理 和广告管理页面

2.3广告管理

2.3.1广告图片上传

将pinyougou-shop-web的以下资源拷贝到pinyougou-manager-web
(1)UploadController.java
(2)uploadService.js
(3)application.properties
(4)fdfs_client.conf
在pinyougou-manager-web 的springmvc.xml中添加配置

<!-- 配置多媒体解析器 -->
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="UTF-8"></property>
		<!-- 设定文件上传的最大值 5MB,5*1024*1024 -->
		<property name="maxUploadSize" value="5242880"></property>
	</bean>

在contentController.js引入uploadService

//控制层 
app.controller('contentController' ,function($scope,$controller ,contentService,uploadService){	

在content.html 引入JS
<script type="text/javascript" src="../js/service/uploadService.js"> </script>
在contentController.js编写代码

       //上传广告图
     	$scope.uploadFile=function(){
     		uploadService.uploadFile().success(
     			function(response){
     				if(response.success){
     					$scope.entity.pic=response.message;					
     				}else{
     					alert("上传失败!");
     				}
     			}
     		).error(
     			function(){
     				alert("上传出错!");
     			}
     		);		
     	}

修改content.html实现上传功能

<tr>            
<td>图片</td>
    <td>
	     <input type="file" id="file">
	     <button ng-click="uploadFile()">上传</button>	                             
	     <img alt="" src="{{entity.pic}}" height="100px" width="200px">
     </td>
 </tr>

列表中显示图片

 <img alt="" src="{{entity.pic}}" height="50px" width="100px">
2.3.2广告类目选择

将contentCategoryService引入contentController
在content.html 引入contentCategoryService.js
在contentController.js中添加代码

//加载广告分类列表
	$scope.findContentCategoryList=function(){
		contentCategoryService.findAll().success(
			function(response){
				$scope.contentCategoryList=response;				
			}
		);
	}

在content.html 初始化调用此方法

<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="contentController" 
ng-init="findContentCategoryList()">

将广告分类改为下拉列表

<select  class="form-control" ng-model="entity.categoryId" ng-options="item.id as item.name  for item in 
contentCategoryList"></select>  
2.3.3广告状态

修改content.html

<input  type="checkbox" ng-model="entity.status" ng-true-value="1" ng-false-value="0">

修改contentController.js

$scope.status=["无效","有效"];

修改content.html的列表

{{status[entity.status]}}

3.网站首页-广告展示

3.1需求分析

修改首页,当其轮播广告图根据后台设置的广告列表动态产生。

3.2准备工作

3.2.1工程搭建

创建war模块pinyougou-portal-web ,此工程为网站前台的入口,参照其它war模块编写配置文件。不需要添加SpringSecurity框架
在这里插入图片描述
pom.xml中配置tomcat启动端口为9103

3.2.2前端

(1)拷贝资源:资源文件夹中 “前台页面”目录下的index.html以及相关目录拷贝到pinyougou-portal-web
在这里插入图片描述
(2)添加angularJS库
在这里插入图片描述
(3)在js文件夹创建base.js 和 base_pagination.js ,创建service 和controller文件夹

3.3后端代码

3.3.1服务接口层

在pinyougou-content-interface工程ContentService接口增加方法定义

/**
 * 根据广告类型ID查询列表
 * @param key
 * @return
 */
public List<TbContent> findByCategoryId(Long categoryId);
3.3.2服务实现层

在pinyougou-content-service工程ContentServiceImpl类增加方法

@Override
	public List<TbContent> findByCategoryId(Long categoryId) {
		//根据广告分类ID查询广告列表		
		TbContentExample contentExample=new TbContentExample();
		Criteria criteria2 = contentExample.createCriteria();
		criteria2.andCategoryIdEqualTo(categoryId);
		criteria2.andStatusEqualTo("1");//开启状态		
		contentExample.setOrderByClause("sort_order");//排序		
		return  contentMapper.selectByExample(contentExample);
	}
3.3.3控制层

在pinyougou-portal-web创建控制器类 ContentController

@RestController
@RequestMapping("/content")
public class ContentController {

	@Reference
	private ContentService contentService;
	
	/**
	 * 根据广告分类ID查询广告列表
	 * @param categoryId
	 * @return
	 */
	@RequestMapping("/findByCategoryId")
	public List<TbContent> findByCategoryId(Long categoryId) {
		return contentService.findByCategoryId(categoryId);
	}		
}

3.4 前端代码

3.4.1 服务层

在pinyougou-portal-web工程创建contentService.js

app.service("contentService",function($http){
	//根据分类ID查询广告列表
	this.findByCategoryId=function(categoryId){
		return $http.get("content/findByCategoryId.do?categoryId="+categoryId);
	}	
});
3.4.2 控制层

在pinyougou-portal-web创建contentController.js

//广告控制层(运营商后台)
app.controller("contentController",function($scope,contentService){	
	$scope.contentList=[];//广告集合	
	$scope.findByCategoryId=function(categoryId){
		contentService.findByCategoryId(categoryId).success(
			function(response){
				$scope.contentList[categoryId]=response;
			}
		);		
	}		
});
3.4.3页面

(1)修改pinyougou-portal-web工程的index.html 引入JS

<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/service/contentService.js">  </script>
<script type="text/javascript" src="js/controller/contentController.js">  </script>

在body上添加指令

<body ng-app="pinyougou" ng-controller="contentController" ng-init="findByCategoryId(1)">

(2)修改首页轮播图

<!--banner轮播-->
<div id="myCarousel" data-ride="carousel" data-interval="4000" class="sui-carousel slide">
	 <ol class="carousel-indicators">
		 <li data-target="#myCarousel" data-slide-to="{{$index}}" class="{{$index==0?'active':''}}" ng-repeat="item in contentList[1]" ></li>
	 </ol>
	 <div class="carousel-inner">
 <div class="{{$index==0?'active':''}} item" ng-repeat="item in contentList[1]">
			 <a href="{{item.url}}">
					<img src="{{item.pic}}"  />
			 </a>
	  </div>
</div>
<a href="#myCarousel" data-slide="prev" class="carousel-control left">
‹</a><a href="#myCarousel" data-slide="next" class="carousel-control right">›</a>
</div>

启动后地址栏输入 http://localhost:9103/index.html 即可看到首页效果
在这里插入图片描述

4. SpringDataRedis简介

4.1项目常见问题思考

我们目前的系统已经实现了广告后台管理和广告前台展示,但是对于首页每天有大量的人访问,对数据库造成很大的访问压力,甚至是瘫痪。那如何解决呢?我们通常的做法有两种:一种是数据缓存、一种是网页静态化。我们今天讨论第一种解决方案。

4.2 Redis

redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写。企业开发通常采用Redis来实现缓存。同类的产品还有memcache 、memcached 、MongoDB等。

4.3 Jedis

Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网下载,当然还有一些开源爱好者提供的客户端,如Jredis、SRP等等,推荐使用Jedis。

4.4 Spring Data Redis

Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。
spring-data-redis针对jedis提供了如下功能:
1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类
2.针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
ValueOperations:简单K-V操作
SetOperations:set类型数据操作
ZSetOperations:zset类型数据操作
HashOperations:针对map类型的数据操作
ListOperations:针对list类型的数据操作

5.网站首页-缓存广告数据

5.1需求分析

现在我们首页的广告每次都是从数据库读取,这样当网站访问量达到高峰时段,对数据库压力很大,并且影响执行效率。我们需要将这部分广告数据缓存起来。

5.2读取缓存

5.2.1公共组件层

因为缓存对于我们整个的系统来说是通用功能。广告需要用,其它数据可能也会用到,所以我们将配置放在公共组件层(pinyougou-common)中较为合理。

(1)pinyougou-common 引入依赖

 <!-- 缓存 -->
<dependency> 
	  <groupId>redis.clients</groupId> 
	  <artifactId>jedis</artifactId> 
</dependency> 
<dependency> 
	  <groupId>org.springframework.data</groupId> 
	  <artifactId>spring-data-redis</artifactId> 
</dependency>	

(2)创建配置文件
将资源中的redis-config.properties 和applicationContext-redis.xml 拷贝至pinyougou-common
(3)pinyougou-content-service依赖pinyougou-common

5.2.2后端服务实现层

修改 pinyougou-content-service的ContentServiceImpl

@Autowired
	private RedisTemplate redisTemplate;	
	@Override
	public List<TbContent> findByCategoryId(Long categoryId) {
		List<TbContent> contentList= (List<TbContent>) redisTemplate.boundHashOps("content").get(categoryId);
		if(contentList==null){
			System.out.println("从数据库读取数据放入缓存");
			//根据广告分类ID查询广告列表		
			TbContentExample contentExample=new TbContentExample();
			Criteria criteria2 = contentExample.createCriteria();
			criteria2.andCategoryIdEqualTo(categoryId);
			criteria2.andStatusEqualTo("1");//开启状态
			contentExample.setOrderByClause("sort_order");//排序
			contentList = contentMapper.selectByExample(contentExample);//获取广告列表
			redisTemplate.boundHashOps("content").put(categoryId, contentList);//存入缓存 
		}
		return  contentList;
	}

5.3更新缓存

TODO:并不满意这样的写法,待修改
当广告数据发生变更时,需要将缓存数据清除,这样再次查询才能获取最新的数据,

5.3.1新增广告后清除缓存

修改pinyougou-content-service工程ContentServiceImpl.java 的add方法

/**
 * 增加
 */
@Override
public void add(TbContent content) {
	contentMapper.insert(content);	
	//清除缓存
	redisTemplate.boundHashOps("content").delete(content.getCategoryId());
}
5.3.2修改广告后清除缓存

考虑到用户可能会修改广告的分类,这样需要把原分类的缓存和新分类的缓存都清除掉。

 */
@Override
public void update(TbContent content){
	//查询修改前的分类Id
	Long categoryId = contentMapper.selectByPrimaryKey(content.getId()).getCategoryId();
	redisTemplate.boundHashOps("content").delete(categoryId);
	contentMapper.updateByPrimaryKey(content);
	//如果分类ID发生了修改,清除修改后的分类ID的缓存
	if(categoryId.longValue()!=content.getCategoryId().longValue()){
		redisTemplate.boundHashOps("content").delete(content.getCategoryId());
	}	
}	
5.3.3删除广告后清除缓存
/**
 * 批量删除
 */
@Override
public void delete(Long[] ids) {
	for(Long id:ids){
		//清除缓存
		Long categoryId = contentMapper.selectByPrimaryKey(id).getCategoryId();//广告分类ID
		redisTemplate.boundHashOps("content").delete(categoryId);
		contentMapper.deleteByPrimaryKey(id);
	}		
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值