SSM项目之商铺系统(十六) 批量增加、删除商品类别从Dao到View层的开发


增加商品类别

Dao层

ProductCategoryDao接口

/**
	 * 
	 * 
	 * @Title: batchInsertProductCategory
	 * 
	 * @Description: 批量增加roductCategory
	 * 
	 * @param productCategoryList
	 * 
	 * @return: int
	 */
	int batchInsertProductCategory(List<ProductCategory> productCategoryList);
	

ProductCategoryDao SQL映射文件

<insert id="batchInsertProductCategory" parameterType="java.util.List">
		INSERT INTO
			tb_product_category(
				product_category_name,
				product_category_desc,
				priority,
				create_time,
				last_edit_time,
				shop_id)
		 VALUES 
		 <foreach collection="list" item="productCategory" index="index" separator=",">
		 	(
		 		#{productCategory.productCategoryName},
		 		#{productCategory.productCategoryDesc},
		 		#{productCategory.priority},
		 		#{productCategory.createTime},
		 		#{productCategory.lastEditTime},
		 		#{productCategory.shopId}
		 	)
		 </foreach>
	</insert>

单元测试

   /**
     * 测试删除
     *
     */
    @Test
    public void testCDeleteProductCategory() throws Exception {
        long shopId = 1;
        List<ProductCategory> productCategoryList = productCategoryDao.selectProductList(shopId);
        for (ProductCategory pc : productCategoryList) {
            if ("商品类别1".equals(pc.getProductCategoryName()) || "商品类别2".equals(pc.getProductCategoryName())) {
                int effectedNum = productCategoryDao.deleteProductCategory(pc.getProductCategoryId(),
                        shopId);
                assertEquals(1, effectedNum);
            }
        }
    }

单元测试OK。


## Service层

ProductCategoryExecution DTO类的开发

我们需要增加操作的状态及数量等信息,因此单独的Domain类已经无法满足需求了,因此我们使用DTO来扩展实体类的功能

package com.artisan.o2o.dto;

import java.util.List;

import com.artisan.o2o.entity.ProductCategory;
import com.artisan.o2o.enums.ProductCategoryStateEnum;

/**
 * 
 * 
 * @ClassName: ProductCategoryExecution
 * 
 * @Description: 封装操作ProductCategory的返回结果,包括操作状态和ProductCategory信息
 * 
 * @author: Mr.Yang
 * 
 * @date: 2018年6月21日 上午12:17:07
 */
public class ProductCategoryExecution {

	private int state;
	private String stateInfo;

	// 因为是批量操作,所以使用List
	private List<ProductCategory> productCategoryList;

	private int count;


	/**
	 * 
	 * 
	 * @Title:ProductCategoryExecution
	 * 
	 * @Description:空的构造函数
	 */
	public ProductCategoryExecution() {
		super();
	}


	/**
	 * 
	 * 
	 * @Title:ProductCategoryExecution
	 * 
	 * @Description:操作成功的时候使用的构造函数,返回操作状态和ProductCategory集合
	 * 
	 * @param productCategoryStateEnum
	 * @param productCategoryList
	 * @param count
	 */
	public ProductCategoryExecution(ProductCategoryStateEnum productCategoryStateEnum, List<ProductCategory> productCategoryList, int count) {
		this.state = productCategoryStateEnum.getState();
		this.stateInfo = productCategoryStateEnum.getStateInfo();
		this.productCategoryList = productCategoryList;
		this.count = count;
	}


	/**
	 * 
	 * 
	 * @Title:ProductCategoryExecution
	 * 
	 * @Description:操作失败的时候返回的信息,仅包含状态和状态描述即可
	 * 
	 * @param productCategoryStateEnum
	 */
	public ProductCategoryExecution(ProductCategoryStateEnum productCategoryStateEnum) {
		this.state = productCategoryStateEnum.getState();
		this.stateInfo = productCategoryStateEnum.getStateInfo();
	}

	public int getState() {
		return state;
	}

	public void setState(int state) {
		this.state = state;
	}

	public String getStateInfo() {
		return stateInfo;
	}

	public void setStateInfo(String stateInfo) {
		this.stateInfo = stateInfo;
	}

	public List<ProductCategory> getProductCategoryList() {
		return productCategoryList;
	}

	public void setProductCategoryList(List<ProductCategory> productCategoryList) {
		this.productCategoryList = productCategoryList;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

}


ProductCategoryStateEnum 增加几个标识

SUCCESS(1, "操作成功"), INNER_ERROR(-1001, "操作失败"), NULL_SHOP(-1002, "Shop信息为空"), EMPETY_LIST(-1003, "请输入商品目录信息");

封装特定异常类

批量添加,这里我们使用事务控制

package com.artisan.o2o.exception;

/**
 * 
 * 
 * @ClassName: ProductCategoryOperationException
 * 
 * @Description: 继承RuntimeException,便于异常时候的回滚。 保持所有的操作在一个事务中。
 * 
 *               这样在标注了@Transactional事务的方法中,出现了异常,才会回滚数据。
 * 
 *               默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring
 *               将回滚事务;除此之外,Spring 不会回滚事务。
 * 
 * 
 * @author: Mr.Yang
 * 
 * @date: 2018年6月21日 上午12:22:44
 */
public class ProductCategoryOperationException extends RuntimeException {

	private static final long serialVersionUID = 6500682256313143297L;

	public ProductCategoryOperationException(String message) {
		super(message);
	}

}


ProductCategoryService接口

/**
	 * 
	 * 
	 * @Title: addProductCategory
	 * 
	 * @Description: 批量插入ProductCategory
	 * 
	 * @param productCategoryList
	 * @throws ProductCategoryOperationException
	 * 
	 * @return: ProductCategoryExecution
	 */
	ProductCategoryExecution addProductCategory(List<ProductCategory> productCategoryList) throws ProductCategoryOperationException;


ProductCategoryServiceImpl实现类

/**
	 * 使用@Transactional控制事务
	 */
	@Override
	@Transactional
	public ProductCategoryExecution addProductCategory(List<ProductCategory> productCategoryList) throws ProductCategoryOperationException {
		// 非空判断
		if (productCategoryList != null && productCategoryList.size() > 0) {
			try {
				// 批量增加ProductCategory
				int effectNum = productCategoryDao.batchInsertProductCategory(productCategoryList);
				if (effectNum > 0) {
					return new ProductCategoryExecution(ProductCategoryStateEnum.SUCCESS, productCategoryList, effectNum);
				} else {
					return new ProductCategoryExecution(ProductCategoryStateEnum.INNER_ERROR);
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new ProductCategoryOperationException("batchAddProductCategory Error:" + e.getMessage());
			}
		} else {
			return new ProductCategoryExecution(ProductCategoryStateEnum.EMPETY_LIST);
		}
	}

单元测试

@Test
	public void testAddProductCategory() {
		ProductCategory productCategory1 = new ProductCategory();
		productCategory1.setProductCategoryName("ProductCategoryTest3");
		productCategory1.setProductCategoryDesc("ProductCategoryTest3-desc");
		productCategory1.setPriority(300);
		productCategory1.setCreateTime(new Date());
		productCategory1.setLastEditTime(new Date());
		productCategory1.setShopId(5L);

		ProductCategory productCategory2 = new ProductCategory();
		productCategory2.setProductCategoryName("ProductCategoryTest4");
		productCategory2.setProductCategoryDesc("ProductCategoryTest4-desc");
		productCategory2.setPriority(600);
		productCategory2.setCreateTime(new Date());
		productCategory2.setLastEditTime(new Date());
		productCategory2.setShopId(5L);

		List<ProductCategory> productCategoryList = new ArrayList<ProductCategory>();
		productCategoryList.add(productCategory1);
		productCategoryList.add(productCategory2);

		ProductCategoryExecution productCategoryExecution = productCategoryService.addProductCategory(productCategoryList);

		Assert.assertEquals(1, productCategoryExecution.getState());
		Assert.assertEquals(2, productCategoryExecution.getProductCategoryList().size());
	}



Controller层

ProductCategoryController增加addProductCategory方法

/**
	 * 
	 * 
	 * @Title: addProductCategory
	 * 
	 * @Description: 添加商铺目录 ,使用@RequestBody接收前端传递过来的productCategoryList
	 * 
	 * @param productCategoryList
	 * @param request
	 * 
	 * @return: Map<String,Object>
	 */
	@RequestMapping(value = "/addproductcategory", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, Object> addProductCategory(@RequestBody List<ProductCategory> productCategoryList, HttpServletRequest request) {
		Map<String, Object> modelMap = new HashMap<String, Object>();
		if (productCategoryList != null && productCategoryList.size() > 0) {
			// 从session中获取shop的信息
			Shop currentShop = (Shop) request.getSession().getAttribute("currentShop");
			if (currentShop != null && currentShop.getShopId() != null) {
				// 为ProductCategory设置shopId
				for (ProductCategory productCategory : productCategoryList) {
					productCategory.setShopId(currentShop.getShopId());
				}
				try {
					// 批量插入
					ProductCategoryExecution pce = productCategoryService.addProductCategory(productCategoryList);
					if (pce.getState() == ProductCategoryStateEnum.SUCCESS.getState()) {
						modelMap.put("success", true);
						// 同时也将新增成功的数量返回给前台
						modelMap.put("effectNum", pce.getCount());
					} else {
						modelMap.put("success", false);
						modelMap.put("errMsg", pce.getStateInfo());
					}
				} catch (ProductCategoryOperationException e) {
					e.printStackTrace();
					modelMap.put("success", false);
					modelMap.put("errMsg", e.getMessage());
					return modelMap;
				}
			} else {
				modelMap.put("success", false);
				modelMap.put("errMsg", ProductCategoryStateEnum.NULL_SHOP.getStateInfo());
			}
		} else {
			modelMap.put("success", false);
			modelMap.put("errMsg", "至少输入一个店铺目录信息");
		}
		return modelMap;
	}

单元测试

待前端页面完成,一并测试

View层

productcategorymanage.js

$(function () {
	// 后台从session中获取shop的信息,这里就不传shopId了
	//var shopId = getQueryString("shopId");
    //var productCategoryURL = '/o2o/shopadmin/getproductcategorybyshopId?shopId=' + shopId;
    
    var getProductCategoryURL = '/o2o/shopadmin/getproductcategorybyshopId';
    var addProductCategoryURL = '/o2o/shopadmin/addproductcategory';
    // 调用getProductCategoryList,加载数据
    getProductCategoryList();
    
    function getProductCategoryList() {
		$.getJSON(getProductCategoryURL,
					function(data) {
						if (data.success) {
							var dataList = data.data;
							$('.product-categroy-wrap').html('');
							var tempHtml = '';
							dataList
									.map(function(item, index) {
										tempHtml += ''
												+ '<div class="row row-product-category now">'
												+ '<div class="col-33 product-category-name">'
												+ item.productCategoryName
												+ '</div>'
												+ '<div class="col-33">'
												+ item.priority
												+ '</div>'
												+ '<div class="col-33"><a href="#" class="button delete" data-id="'
												+ item.productCategoryId
												+ '">删除</a></div>'
												+ '</div>';
									});
							$('.product-categroy-wrap').append(tempHtml);
						}
					});
	}
	
    
    
    //  新增按钮的点击事件
    $('#new').click(
    		function(){
    			// 新增数据 以 temp 为标识,便于和库表中的数据区分开来
    			var tempHtml = '<div class="row row-product-category temp">'
					+ '<div class="col-33"><input class="category-input category" type="text" placeholder="分类名"></div>'
					+ '<div class="col-33"><input class="category-input priority" type="number" placeholder="优先级"></div>'
					+ '<div class="col-33"><a href="#" class="button delete">删除</a></div>'
					+ '</div>';
    			$('.product-categroy-wrap').append(tempHtml);
    		});
    
    
	$('#submit').click(function() {
		// 通过temp 获取新增的行
		var tempArr = $('.temp');
		// 定义数组接收新增的数据
		var productCategoryList = [];
		tempArr.map(function(index, item) {
			var tempObj = {};
			tempObj.productCategoryName = $(item).find('.category').val();
			tempObj.priority = $(item).find('.priority').val();
			if (tempObj.productCategoryName && tempObj.priority) {
				productCategoryList.push(tempObj);
			}
		});
		$.ajax({
			url : addProductCategoryURL,
			type : 'POST',
			// 后端通过 @HttpRequestBody直接接收
			data : JSON.stringify(productCategoryList),
			contentType : 'application/json',
			success : function(data) {
				if (data.success) {
					$.toast('新增【' + data.effectNum + '】条成功!');
					// 重新加载数据
					getProductCategoryList();
				} else {
					$.toast(data.errMsg);
				}
			}
		});
	});
    
});

前后端联调

前端页面debug, 后端也可以加入断点,以debug的方式开启tomcat,逐步调测

效果如下:

在这里插入图片描述


删除商品类别

Dao层

ProductCategoryDao接口增加接口方法

/**
	 * 
	 * 
	 * @Title: deleteProductCategory
	 * 
	 * @Description: 删除特定shop下的productCategory
	 * 
	 * @param productCategoryId
	 * @param shopId
	 * 
	 * @return: int
	 */
	int deleteProductCategory(@Param("productCategoryId") Long productCategoryId, @Param("shopId") Long shopId);

ProductCategoryDao SQL映射文件


	<delete id="deleteProductCategory">
		DELETE FROM 
			tb_product_category
		WHERE 
			product_category_id = #{productCategoryId}
			and 
			shop_id = #{shopId}
	</delete>

闭环的单元测试

这里我们使用Junit 4.11里及其以后的版本中增加的@FixMethodOrder注解来实现. 具体见代码注释。

package com.artisan.o2o.dao;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;

import com.artisan.o2o.BaseTest;
import com.artisan.o2o.entity.ProductCategory;

/**
 * 
 * 
 * @ClassName: ProductCategoryTest
 * 
 * @Description: Junit 4.11里增加了指定测试方法执行顺序的特性 .
 * 
 *               测试类的执行顺序可通过对测试类添加注解@FixMethodOrder(value) 来指定,其中value 为执行顺序
 * 
 *               三种执行顺序可供选择:
 * 
 *               默认(MethodSorters.DEFAULT),
 *               默认顺序由方法名hashcode值来决定,如果hash值大小一致,则按名字的字典顺序确定
 *               由于hashcode的生成和操作系统相关
 *               (以native修饰),所以对于不同操作系统,可能会出现不一样的执行顺序,在某一操作系统上,多次执行的顺序不变
 * 
 *               按方法名( MethodSorters.NAME_ASCENDING)【推荐】,
 *               按方法名称的进行排序,由于是按字符的字典顺序,所以以这种方式指定执行顺序会始终保持一致;
 *               不过这种方式需要对测试方法有一定的命名规则,如 测试方法均以testNNN开头(NNN表示测试方法序列号 001-999)
 * 
 *               JVM(MethodSorters.JVM)
 *               按JVM返回的方法名的顺序执行,此种方式下测试方法的执行顺序是不可预测的,即每次运行的顺序可能都不一样
 * 
 * 
 * @author: Mr.Yang
 * 
 * @date: 2018年6月21日 下午11:55:45
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ProductCategoryTest extends BaseTest {
	
	@Autowired
	ProductCategoryDao productCategoryDao;
	
	@Test
	public void testB_SelectProductCategoryList() {
		long shopId = 5L;
		List<ProductCategory> productCategories = productCategoryDao.selectProductCategoryList(shopId);
		// shopId = 5 有2条测试数据,期望list中有2条
		assertEquals(2, productCategories.size());
		// SQL中按照权重排序, product1 priority 99 ,期望第一条数据是 product1
		assertEquals("product1", productCategories.get(0).getProductCategoryName());

		for (ProductCategory productCategory : productCategories) {
			System.out.println(productCategory.toString());
		}

		productCategories = productCategoryDao.selectProductCategoryList(6L);
		assertEquals(0, productCategories.size());

	}

	@Test
	public void testA_BatchInsertProductCategory() {

		ProductCategory productCategory1 = new ProductCategory();
		productCategory1.setProductCategoryName("product1");
		productCategory1.setProductCategoryDesc("product1_desc");
		productCategory1.setPriority(99);
		productCategory1.setCreateTime(new Date());
		productCategory1.setLastEditTime(new Date());
		productCategory1.setShopId(5L);

		ProductCategory productCategory2 = new ProductCategory();
		productCategory2.setProductCategoryName("product2");
		productCategory2.setProductCategoryDesc("product2_desc");
		productCategory2.setPriority(98);
		productCategory2.setCreateTime(new Date());
		productCategory2.setLastEditTime(new Date());
		productCategory2.setShopId(5L);

		List<ProductCategory> productCategoryList = new ArrayList<ProductCategory>();
		productCategoryList.add(productCategory1);
		productCategoryList.add(productCategory2);

		int effectNum = productCategoryDao.batchInsertProductCategory(productCategoryList);
		Assert.assertEquals(2, effectNum);

	}

	@Test
	public void testC_DeleteProductCategory() {
		// 查询出来shopId=5的商铺下面全部的商品目录
		List<ProductCategory> productCategoryList = productCategoryDao.selectProductCategoryList(5L);
		// 遍历循环删除
		for (ProductCategory productCategory : productCategoryList) {
			if ("product1".equals(productCategory.getProductCategoryName()) || "product2".equals(productCategory.getProductCategoryName())) {
				int effectNum = productCategoryDao.deleteProductCategory(productCategory.getProductCategoryId(), 5L);
				assertEquals(1, effectNum);
			}
		}
	}

}


Servie层

接口

/**
	 * 
	 * 
	 * @Title: deleteProductCategory
	 * 
	 * @Description: TODO 需要先将该商品目录下的商品的类别Id置为空,然后再删除该商品目录, 因此需要事务控制
	 * 
	 * @param productCategoryId
	 * @param shopId
	 * @throws ProductCategoryOperationException
	 * 
	 * @return: ProductCategoryExecution
	 */
	ProductCategoryExecution deleteProductCategory(long productCategoryId, long shopId) throws ProductCategoryOperationException;

接口实现

/**
	 * TODO 需要先将该商品目录下的商品的类别Id置为空,然后再删除该商品目录, 因此需要事务控制@Transactional
	 */
	@Override
	@Transactional
	public ProductCategoryExecution deleteProductCategory(long productCategoryId, long shopId) throws ProductCategoryOperationException {
		// TODO 第一步 需要先将该商品目录下的商品的类别Id置为空

		// 第二步 删除该商品目录
		try {
			int effectNum = productCategoryDao.deleteProductCategory(productCategoryId, shopId);
			if (effectNum > 0) {
				return new ProductCategoryExecution(ProductCategoryStateEnum.SUCCESS);
			} else {
				return new ProductCategoryExecution(ProductCategoryStateEnum.INNER_ERROR);
			}
		} catch (Exception e) {
			throw new ProductCategoryOperationException(e.getMessage());
		}
	}

单元测试

@Test
	public void testDeleteProductCategory() {

		ProductCategoryExecution productCategoryExecution = productCategoryService.deleteProductCategory(26, 5);
		Assert.assertEquals(1, productCategoryExecution.getState());
		ProductCategoryExecution productCategoryExecution2 = productCategoryService.deleteProductCategory(27, 5);
		Assert.assertEquals(1, productCategoryExecution2.getState());
	}

Controller层

路由方法

/**
	 * 
	 * 
	 * @Title: remooveProductCategory
	 * 
	 * @Description: 删除商品目录
	 * 
	 * @param productCategoryId
	 * @param request
	 * 
	 * @return: Map<String,Object>
	 */
	@RequestMapping(value = "/removeproductcategory", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, Object> remooveProductCategory(Long productCategoryId, HttpServletRequest request) {
		Map<String, Object> modelMap = new HashMap<String, Object>();
		if (productCategoryId != null && productCategoryId > 0) {
			// 从session中获取shop的信息
			Shop currentShop = (Shop) request.getSession().getAttribute("currentShop");
			if (currentShop != null && currentShop.getShopId() != null) {
				try {
					// 删除
					Long shopId = currentShop.getShopId();
					ProductCategoryExecution pce = productCategoryService.deleteProductCategory(productCategoryId, shopId);
					if (pce.getState() == ProductCategoryStateEnum.SUCCESS.getState()) {
						modelMap.put("success", true);
					} else {
						modelMap.put("success", false);
						modelMap.put("errMsg", pce.getStateInfo());
					}
				} catch (ProductCategoryOperationException e) {
					e.printStackTrace();
					modelMap.put("success", false);
					modelMap.put("errMsg", e.getMessage());
					return modelMap;
				}
			} else {
				modelMap.put("success", false);
				modelMap.put("errMsg", ProductCategoryStateEnum.NULL_SHOP.getStateInfo());
			}
		} else {
			modelMap.put("success", false);
			modelMap.put("errMsg", "请选择商品类别");
		}
		return modelMap;
	}

单元测试

前端完成后,一起测试

View层

productcategorymanage.js

增加如下代码

 var deleteProductCategoryUrl = '/o2o/shopadmin/removeproductcategory';

// 一种是需要提交到后台的删除  now  ,另外一种是 新增但未提交到数据库中的删除 temp
	
	$('.product-categroy-wrap').on('click', '.row-product-category.now .delete',
			function(e) {
				var target = e.currentTarget;
				$.confirm('确定么?', function() {
					$.ajax({
						url : deleteProductCategoryUrl,
						type : 'POST',
						data : {
							productCategoryId : target.dataset.id,
						},
						dataType : 'json',
						success : function(data) {
							if (data.success) {
								$.toast('删除成功!');
								// 重新加载数据
								getProductCategoryList();
							} else {
								$.toast('删除失败!');
							}
						}
					});
				});
			});

	$('.product-categroy-wrap').on('click', '.row-product-category.temp .delete',
			function(e) {
				$(this).parent().parent().remove();
			});


联调

前端页面debug, 后端也可以加入断点,以debug的方式开启tomcat,逐步调测

效果如下:

这里写图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值