品优购项目笔记 day07

目标

  • 完成商家后台商品列表的功能
  • 完成商家后台商品修改的功能
  • 完成运营商后台商品审核的功能
  • 完成运营商后台商品删除的功能
  • 掌握注解式事务的配置

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

1. 需求分析

在商家后台,显示该商家的商品列表信息,如图:
品优购项目笔记 day07

2. 查询商家商品列表

1. 后端

修改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);		
}

修改sellergoods-service工程对应goodsService的findPage方法,修改条件构建部分代码,将模糊匹配改为精确匹配

if(goods.getSellerId()!=null && goods.getSellerId().length()>0){
  criteria.andSellerIdEqualTo(goods.getSellerId());
}

2. 前端

修改goods.html,引入js

<script 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/uploadService.js"></script>
<script type="text/javascript" src="../js/service/itemCatService.js"></script>
<script type="text/javascript" src="../js/service/typeTemplateService.js"></script>
<script type="text/javascript" src="../js/controller/goodsController.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>

添加指令

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

在页面放置分页控件

<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>{{itemCatList[entity.category1Id]}}</td>
  <td>{{itemCatList[entity.category2Id]}}</td>
  <td>{{itemCatList[entity.category3Id]}}</td>
  <td>
    <span>
      {{status[entity.auditStatus]}}
    </span>
  </td>		 

3. 显示状态

修改goodsController.js,添加status数组

// 商品的状态
$scope.status = ["未审核","已审核","审核未通过","关闭"];

修改列表显示

<td>
  <span>
  {{status[entity.auditStatus]}}
    </span>
</td>

4. 显示分类

分类显示仍为id

如何显示分类?

方案1:在后端代码写关联查询语句,返回的数据中直接有分类名称

方案2:在前端代码用id去查询后端,异步返回商品分类名称。

采用方案2,对后端代码无侵入性。

1. 修改goodsController

$scope.itemCatList = [];//商品分类列表
// 加载商品分类列表 根据分类id得到分类名称
$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,增加初始化调用

见上上个代码

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

同上

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>

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

1. 需求分析

在商品列表页面点击修改,进入商品编辑页面good_edit.html,并传递参数商品id,商品编辑页面接受该参数后从数据库中读取商品信息,用户修改后保存信息。

2. 基本信息读取

首选读取商品分类、商品名称、品牌、副标题、价格等信息

1. 后端

修改sellergoods-interface的GoodsService.java,TbGoods改为Goods

public Goods findOne(Long id);

修改sellergoods-service的GoodsServiceImpl.java

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

再修改两个web的GoodsController.java的findOne方法,修改Goods

2. 前端

在goodsController中引入$location服务

app.controller('goodsController' ,function($scope,$controller,$location,goodsService,uploadService,itemCatService,typeTemplateService){...}

修改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的地址路由的书写形式

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

修改前端代码goodsController

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);
				// 商品规格 [{"attributeName":"网络制式","attributeValue":["移动3G","移动4G"]},{"attributeName":"屏幕尺寸","attributeValue":["6寸","5.5寸"]}]
				$scope.entity.goodsDesc.specificationItems = JSON.parse($scope.entity.goodsDesc.specificationItems);
				// sku列表规格列转换 {"网络":"联通3G","机身内存":"32G"}
				for(var i=0;i<$scope.entity.itemList.length;i++){
					$scope.entity.itemList[i].spec = JSON.parse($scope.entity.itemList[i].spec);
				}
			}
		);				
	}

4. 显示商品图片列表

修改goodsController.js,将图片列表从字符串转换为json集合对象

代码见上面

5. 读取商品扩展属性

修改goodsController.js

代码见上,但是并没有读取出来,原因是被最开始编辑代码冲突,覆盖,将其更新

// 模板id选择后,更新品牌列表 扩展属性 规格列表
$scope.$watch('entity.goods.typeTemplateId',function (newValue, oldValue) {
  typeTemplateService.findOne(newValue).success(
    function (response) {
      $scope.typeTemplate = response;//根据模板id获取类型模板
      $scope.typeTemplate.brandIds = JSON.parse($scope.typeTemplate.brandIds);//字符串转换为json集合
      if($location.search()['id']==null){
        $scope.entity.goodsDesc.customAttributeItems = JSON.parse($scope.typeTemplate.customAttributeItems);
      }
    }
  );

6. 读取商品规格属性

修改goodsController,将规格部分转换为json,然后根据规格名称和选项名称返回是否被勾选

// 根据规格名称和选项名称返回是否被勾选 [{"attributeName":"网络制式","attributeValue":["移动3G","移动4G"]},
$scope.checkAttributeValue = function (specName, optionName) {
  var items = $scope.entity.goodsDesc.specificationItems;
  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,option.optionName);createItemList()"
       ng-checked="checkAttributeValue(pojo.text,option.optionName)">{{option.optionName}}

7. 读取SKU数据

显示SKU商品列表,并自动读取价格、库存等数据加载到列表

1. 后端

GoodsServiceImpl的findOne方法加载SKU商品数据

@Override
public Goods findOne(Long id){
  Goods goods = new Goods();
  goods.setGoods(goodsMapper.selectByPrimaryKey(id));
  goods.setGoodsDesc(goodsDescMapper.selectByPrimaryKey(id));
  // 查询SKU商品列表
  TbItemExample example = new TbItemExample();
  TbItemExample.Criteria criteria = example.createCriteria();
  criteria.andGoodsIdEqualTo(id);
  List<TbItem> itemList = itemMapper.selectByExample(example);//goods里面的itemList
  goods.setItemList(itemList);
  return goods;
}

2. 前端

修改goodsController的findOne方法

见之前代码

8. 保存数据

1. 后端代码

修改sellergoods-interface的GoodsService.java的update的goods的类

修改sellergoods-service的GoodsServiceImpl,

@Override
public void update(Goods goods){
  goods.getGoods().setAuditStatus("0");//设置为未审核,如果修改过,需要重新设置
  goodsMapper.updateByPrimaryKey(goods.getGoods());
  goodsDescMapper.updateByPrimaryKey(goods.getGoodsDesc());
  // SKU部分
  // 先删除SKU列表数据
  TbItemExample example = new TbItemExample();
  TbItemExample.Criteria criteria = example.createCriteria();
  criteria.andGoodsIdEqualTo(goods.getGoods().getId());
  itemMapper.deleteByExample(example);

  // save 插入商品SKU列表数据
  saveItemList(goods);
}

需要共用插入列表的代码。将SKU列表插入的代码提取,封装为私有

private void saveItemList(Goods goods){
  if("1".equals(goods.getGoods().getIsEnableSpec())){
    // itemList
    for (TbItem item : goods.getItemList()) {
      // title eg.苹果(Apple) iPhone 4s 8GB 黑色 联通3G手机
      String title = goods.getGoods().getGoodsName();//商品名
      // {"机身内存":"16G","网络":"联通3G"}
      Map<String,Object> specMap =JSON.parseObject(item.getSpec());
      for (String key : specMap.keySet()) {
        title += " "+specMap.get(key);//完善title
      }
      item.setTitle(title);
      setItemValues(item,goods);
      itemMapper.insert(item);
    }
  }else{
    TbItem item = new TbItem();
    item.setTitle(goods.getGoods().getGoodsName());
    item.setPrice(goods.getGoods().getPrice());
    item.setStatus("1");//状态
    item.setIsDefault("1");//是否默认
    item.setNum(9999);//库存数量
    item.setSpec("{}");
    setItemValues(item,goods);
    itemMapper.insert(item);
  }
}

修改manager-web的GoodsController.java的update方法的goods的类

修改shop-web工程的GoodsController.java

必须保证商家后台提交的商品属于该商户的!

@RequestMapping("/update")
public Result update(@RequestBody Goods goods){
  // 必须确保是该商户提交该商户的商品
  // 需要先检验是否是当前商家的id
  Goods goods1 = goodsService.findOne(goods.getGoods().getId());
  // 获取商家的登录名
  String sellerId = SecurityContextHolder.getContext().getAuthentication().getName();
  // 如果传递的商家id和当前登录的id不一致,属于非法操作
  if(!goods1.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. 前端代码

修改goodsController.js,新增保存的方法,将add方法也并入到save中

//保存 
$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);
      }
    }		
  );				
}

修改goods_edit.html调用

<div class="btn-toolbar list-toolbar">
  <button class="btn btn-primary" ng-click="save()"><i class="fa fa-save" ></i>保存</button>
  <button class="btn btn-default" >返回列表</button>
</div>

9. 页面跳转

由商品列表页跳转到商品编辑页

button按钮转为超链接

<a href="goods_edit.html#?id={{entity.id}}" class="btn bg-olive btn-xs">修改</a>

由商品编辑页跳转到商品列表

修改goods_edit.html的返回列表按钮

<a class="btn btn-default" href="goods.html">返回列表</a>

保存成功后返回列表页面

修改save方法

//保存 
$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("保存成功");
        location.href = "goods.html";//跳转到商品列表页
      }else{
        alert(response.message);
      }
    }		
  );				
}

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

1. 待审核商品列表

需求:参照商家后台商品列表

  1. 修改manager-web的goodsController.js,注入itemCatService,添加代码
// 商品的状态
$scope.status = ["未审核","已审核","审核未通过","关闭"];

$scope.itemCatList = [];//商品分类列表
// 加载商品分类列表 根据分类id得到分类名称
$scope.findItemCatList = function () {
  itemCatService.findAll().success(
    function (response) {
      for(var i=0;i<response.length;i++){
        $scope.itemCatList[response[i].id]=response[i].name;
      }
    }
  );
}
  1. 修改goods.html,引入js
<script 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/goodsController.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
  1. 指令,完成初始调用
<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="goodsController" ng-init="findItemCatList();searchEntity={auditStatus:'0'}">
  1. 循环列表
<tr ng-repeat="entity in list">
  <td><input  type="checkbox" ng-click="updateSelection($event,entity.id)"></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>
    <span>
      {{status[entity.auditStatus]}}
    </span>
  </td>
  <td class="text-center">
    <a href="goods_edit.html#?id={{entity.id}}" class="btn bg-olive btn-xs">详情</a>
  </td>
</tr>
  1. 分页控件
<tm-pagination conf="paginationConf"></tm-pagination>

2. 商品详情展示

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

TODO(有空回头做)

3. 商品审核与驳回

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

1. 后端代码

  1. 在sellergoods-interface的GoodsService.java新增方法
// 批量修改状态
	public void updateStatus(Long[] ids,String status);
  1. sellergoods-service的GoodsServiceImpl.java实现该方法
@Override
public void updateStatus(Long[] ids, String status) {
  for (Long id : ids) {
    TbGoods goods = goodsMapper.selectByPrimaryKey(id);
    goods.setAuditStatus(status);
    goodsMapper.updateByPrimaryKey(goods);
  }
}
  1. manager-web的GoodsController.java新增方法
@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,"失败");
  }
}

2. 前端代码

  1. 修改manager-web的goodsService.js,增加方法
// 更新状态
this.updateStatus = function (ids, status) {
  return $http.get("../goods/updateStatus.do?ids="+ids+"&status="+status);
}
  1. 修改manager-web的goodsController.js,新增方法
// 更改状态
$scope.updateStatus=function (status) {
  goodsService.updateStatus($scope.selectIds,status).success(
    function (response) {
      if(response.success){
        $scope.reloadList();//刷新列表
        $scope.selectIds = [];//清空
      }else{
        alert(response.message);
      }
    }
  );
}
  1. 修改manager-web的goods.html页面,为复选框绑定事件指令
<td><input  type="checkbox" ng-click="updateSelection($event,entity.id)"></td>
  1. 修改页面的审核通过和驳回按钮
<button type="button" ng-click="updateStatus('1')" class="btn btn-default" title="审核通过" ><i class="fa fa-check"></i> 审核通过</button>
<button type="button" ng-click="updateStatus('2')" class="btn btn-default" title="驳回" ><i class="fa fa-ban"></i> 驳回</button>

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

1. 需求分析

为商品管理提供商品删除功能,数据宝贵,不能物理删除,而是修改tb_goods表的is_delete字段为1,称为逻辑删除。类似回收站

2. 逻辑删除的实现

1. 后端代码

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

@Override
public void delete(Long[] ids) {
  for(Long id:ids){//进行逻辑删除
    TbGoods goods = goodsMapper.selectByPrimaryKey(id);
    goods.setIsDelete("1");
    goodsMapper.updateByPrimaryKey(goods);
  }
}

2. 前端代码

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

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

3. 排除已删除的记录

修改sellergoods-service的GoodsServiceImpl.java的findPage方法

@Override
public PageResult findPage(TbGoods goods, int pageNum, int pageSize) {
  PageHelper.startPage(pageNum, pageSize);

  TbGoodsExample example=new TbGoodsExample();
  Criteria criteria = example.createCriteria();
  criteria.andIsDeleteIsNull();// 排除逻辑删除的商品
  //...

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

1. 需求分析

上下架是商品的一个状态,不同于审核,控制权在商家手中,自己决定是上下架。商家正常销售,下架暂停销售

2. 实现思路

上下架就是对上下架状态的修改,字段为tb_goods表的is_markable字段。1表示上架,0表示下架。

3. 代码实现

TODO

6. 注解式事务配置

1. 配置文件

在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">
 

    <!--<import resource="applicationContext-dao.xml"/>-->

    <!-- 事务管理器  -->  
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="dataSource" />  
    </bean>  
      
    <!-- 开启事务控制的注解支持 -->  
    <tx:annotation-driven transaction-manager="transactionManager"/>
   
</beans>

2. 在方法上添加注解

服务层,每个java类上都添加@Transactional