第8章 商品模块
2018-05-06 本文已影响0人
cuzz_
商品添加
Dao层的实现
- ProductDao
public interface ProductDao {
/**
* 插入商品
* @param product
* @return
*/
int insertProduct(Product product);
}
- ProductImgDao
public interface ProductImgDao {
/**
* 批量添加图片
* @param productImgList
* @return
*/
int batchInsertProductImg(List<ProductImg productImgList>);
}
- ProductDao.xml
useGeneratedKeys可以产生一个主键赋值到实体类当中
<insert id="insertProduct" parameterType="com.imooc.o2o.entity.Product"
useGeneratedKeys="true" keyProperty="productId" keyColumn="product_id">
INSERT INTO
tb_product(product_name, product_desc, img_addr,
normal_price, promotion_price, priority, create_time,
last_edit_time, enable_status, product_category_id,
shop_id)
VALUES
(#{productName}, #{productDesc}, #{imgAddr},
#{normalPrice}, #{promotionPrice}, #{priority}, #{createTime},
#{lastEditTime}, #{enableStatus}, #{productCategory.productCategoryId},
#{shop.shopId})
</insert>
- ProductImgDao.xml
<mapper namespace="com.imooc.o2o.dao.ProductImgDao">
<insert id="batchInsertProductImg" parameterType="java.util.List">
INSERT INTO
tb_product_img(img_addr, img_desc, priority,
create_time, product_id)
VALUES
<foreach collection="list" item="productImg" index="index"
separator=",">
(#{productImg.imgAddr},
#{productImg.Desc},
#{productImg.priority},
#{productImg.createTime},
#{productImg.productId}
)
</foreach>
</insert>
- insertProduct测试
@Test
public void testAInsertProduct() throws Exception {
Shop shop1 = new Shop();
shop1.setShopId(5L);
ProductCategory pc1 = new ProductCategory();
pc1.setProductCategoryId(2L);
Product product1 = new Product();
product1.setProductName("测试1");
product1.setProductDesc("测试Desc1");
product1.setImgAddr("test1");
product1.setPriority(0);
product1.setEnableStatus(1);
product1.setCreateTime(new Date());
product1.setShop(shop1);
product1.setProductCategory(pc1);
int effectedNum = productDao.insertProduct(product1);
assertEquals(1, effectedNum);
}
- batchInsertProductImg测试
@Test
public void testABatchInsertProductImg() throws Exception {
ProductImg productImg1 = new ProductImg();
productImg1.setImgAddr("图片1");
productImg1.setImgDesc("测试图片1");
productImg1.setPriority(1);
productImg1.setCreateTime(new Date());
productImg1.setProductId(4L);
ProductImg productImg2 = new ProductImg();
productImg2.setImgAddr("图片2");
productImg2.setImgDesc("测试图片1");
productImg2.setPriority(1);
productImg2.setCreateTime(new Date());
productImg2.setProductId(4L);
List<ProductImg> productImgList = new ArrayList<ProductImg>();
productImgList.add(productImg1);
productImgList.add(productImg2);
int effectedNum = productImgDao.batchInsertProductImg(productImgList);
assertEquals(2, effectedNum);
service层的实现
- 接口
/**
* 添加商品以及图片处理
* @param product
* @param thumbnail
* @param thumbnailName
* @param productImgList
* @param productImgNameList
* @return
* @throws ProductOperationException
*/
ProductExecution addProduct(Product product,
InputStream thumbnail,
String thumbnailName,
List<InputStream> productImgList,
List<String> productImgNameList) throws ProductOperationException;
我们发现输出流和文件名,我封装一下
在dto中新建一个ImageHolder
package com.imooc.o2o.dto;
import java.io.InputStream;
public class ImageHolder {
private String imageName;
private InputStream image;
public ImageHolder(String imageName, InputStream image) {
this.imageName = imageName;
this.image = image;
}
public String getImageName() {
return imageName;
}
public void setImageName(String imageName) {
this.imageName = imageName;
}
public InputStream getImage() {
return image;
}
public void setImage(InputStream image) {
this.image = image;
}
}
修改一下,这样只需要传递3个参数,也更清新
/**
* 添加商品以及图片处理
* @param product
* @param thumbnail
* @param thumbnailName
* @param productImgList
* @param productImgNameList
* @return
* @throws ProductOperationException
*/
ProductExecution addProduct(Product product,
ImageHolder thumbnail,
List<ImageHolder> imageHolderList) throws ProductOperationException;
}
- 实现类
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Autowired
private ProductImgDao productImgDao;
// 1. 处理缩略图,获取缩略图相对路径并赋值给product
// 2. 往tb_product写入商品信息,获取productId
// 3. 结合productId批量处理商品详情图
// 4. 将商品的详情图列表批量插入tb_product_img中
@Override
// 事务管理
@Transactional
public ProductExecution addProduct(Product product, ImageHolder thumbnail, List<ImageHolder> imageHolderList)
throws ProductOperationException {
// 空值判断
if (product !=null && product.getShop().getShopId() != null) {
// 给商品设置默认属性
product.setCreateTime(new Date());
product.setLastEditTime(new Date());
// 默认为上架状态
product.setEnableStatus(1);
// 若商品缩略图不为空则添加
if (thumbnail != null) {
addThumbnail(product, thumbnail);
}
try {
// 创建商品信息
int effectNum = productDao.insertProduct(product);
if (effectNum <= 0) {
throw new ProductOperationException("创建商品失败");
}
} catch (Exception e) {
throw new ProductOperationException("创建商品失败" + e.getMessage());
}
// 若商品详情图不为空则添加
if (imageHolderList != null && imageHolderList.size() > 0) {
addProductImgList(product, imageHolderList);
}
return new ProductExecution(ProductStateEnum.SUCCESS, product);
} else {
// 传入的参数为空
return new ProductExecution(ProductStateEnum.EMPTY);
}
}
/**
* 批量添加图
* @param product
* @param imageHolderList
*/
private void addProductImgList(Product product, List<ImageHolder> imageHolderList) {
// 获取图片储存的路径,这里直接放在相应的店铺的文件夹底下
String dest = PathUtil.getShopImagePath(product.getShop().getShopId());
List<ProductImg> productImgList = new ArrayList<>();
// 遍历图片一次去处理,并添加进productImg实体类
for (ImageHolder imageHolder : imageHolderList) {
String imgAddr = ImageUtil.generateNormalImg(imageHolder.getImage(), imageHolder.getImageName(), dest);
ProductImg productImg = new ProductImg();
productImg.setImgAddr(imgAddr);
productImg.setProductId(product.getProductId());
productImg.setCreateTime(new Date());
productImgList.add(productImg);
}
// 如果确实是有图片需要添加的 就执行批量添加
if (productImgList.size() > 0) {
try {
int effectedNum = productImgDao.batchInsertProductImg(productImgList);
if (effectedNum <= 0) {
throw new ProductOperationException("创建商品详情图片失败");
}
} catch (Exception e) {
throw new ProductOperationException("创建商品详情图片失败" + e.getMessage());
}
}
}
/**
* 添加缩略图
* @param product
* @param thumbnail
*/
private void addThumbnail(Product product, ImageHolder thumbnail) {
// 获取根路径
String dest = PathUtil.getShopImagePath(product.getShop().getShopId());
String thumbnailAddr = ImageUtil.generateThumbnail(thumbnail.getImage(), thumbnail.getImageName(), dest);
product.setImgAddr(thumbnailAddr);
}
}
- 测试
@Test
public void testaddProduct() throws FileNotFoundException {
Product product = new Product();
Shop shop = new Shop();
shop.setShopId(4L);
ProductCategory productCategory = new ProductCategory();
productCategory.setProductCategoryId(1L);
product.setShop(shop);
product.setProductCategory(productCategory);
product.setProductName("测试商品1");
product.setProductDesc("测试");
product.setPriority(0);
// 创建缩略图文件流
File thumbnailFile = new File("D:\\123.jpg");
InputStream thumbnail = new FileInputStream(thumbnailFile);
ImageHolder imageHolder = new ImageHolder(thumbnailFile.getName(), thumbnail);
// 创建详情图片列表
File thumbnailFile2 = new File("D:\\456.png");
InputStream thumbnail2 = new FileInputStream(thumbnailFile2);
ImageHolder imageHolder2 = new ImageHolder(thumbnailFile2.getName(), thumbnail2);
List<ImageHolder> list = new ArrayList<>();
// 不能使用同一个流, 报错!!!找了好久
// list.add(imageHolder);
list.add(imageHolder2);
// 添加商品并验证
ProductExecution productExecution = productService.addProduct(product, imageHolder, list);
assertEquals(ProductStateEnum.SUCCESS.getState(), productExecution.getState());
-
测试结果
image.png
Controller层的实现
- 显示编辑页面
@RequestMapping(value = "/productedit")
public String ProductEdit() {
return "shop/productedit";
}

- addProduct
@ResponseBody
private Map<String, Object> addProduct(HttpServletRequest request) throws IOException {
Map<String, Object> modelMap = new HashMap<>();
// 验证码验证
// 接受前端参数的变量的的初始化,包括商品,缩略图, 详情图片实体
ObjectMapper mapper = new ObjectMapper();
Product product = null;
String productStr = HttpServletRequestUtil.getString(request, "productStr");
System.out.println(productStr);
MultipartHttpServletRequest multipartRequest = null;
ImageHolder thumbnail = null;
List<ImageHolder> productImgList = new ArrayList<>();
// 获取文件流
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
request.getSession().getServletContext());
try {
// 如果请求中存在文件流,则取出相关文件(包括缩略图,和详情图)
if (multipartResolver.isMultipart(request)) {
multipartRequest = (MultipartHttpServletRequest) request;
// 取出缩略图并构建ImageHolder对象
CommonsMultipartFile thumbnailFile = (CommonsMultipartFile) multipartRequest.getFile("thumbnail");
thumbnail = new ImageHolder(thumbnailFile.getOriginalFilename(),thumbnailFile.getInputStream());
// 取出详情图片列表并构建List<ImageHolder>列表,最多支持六张图片
for (int i = 0; i < IMAGEMAXCOUNT; i++) {
CommonsMultipartFile productImgFile =
(CommonsMultipartFile) multipartRequest.getFile("productImg" + i);
if (productImgFile != null) {
// 如果取出第i个详情图片的文件流不为空 则将其加入列表
ImageHolder productImg = new ImageHolder(
productImgFile.getOriginalFilename(), productImgFile.getInputStream());
productImgList.add(productImg);
} else {
// 若取出的第i个详情图片文件流为空,则终止循环
break;
}
}
} else {
modelMap.put("success", false);
modelMap.put("errMsg", "上传图片不能空");
return modelMap;
}
} catch (Exception e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
try {
// 尝试获取前端传过来的表单string流并将其转化成Product实体类
product = mapper.readValue(productStr, Product.class);
} catch (Exception e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
// 如果Product信息,缩略图已经详情图片列表都不为空,则开始进行商品添加操作
if (product != null && thumbnail != null && productImgList.size() > 0) {
try {
// 从session中获取当前的店铺id并赋值给product 减少对前端的依赖
Shop currentShop = (Shop) request.getSession().getAttribute("currentShop");
Shop shop = new Shop();
shop.setShopId(currentShop.getShopId());
product.setShop(shop);
// 执行并添加操作
ProductExecution pe = productService.addProduct(product, thumbnail, productImgList);
if (pe.getState() == ProductStateEnum.SUCCESS.getState()) {
modelMap.put("success", true);
} else {
modelMap.put("success", false);
modelMap.put("errMsg", pe.getStateInfo());
}
} catch (ProductOperationException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
} else {
modelMap.put("success", false);
modelMap.put("errMsg", "请输入商品信息");
}
return modelMap;
}
-
测试
image.png
image.png
商品编辑
Dao层的实现
- 接口
/**
* 通过productId查询唯一商品信息
* @param productId
* @return
*/
Product queryProductById(long productId);
/**
* 更新商品的实体类
* @param product
* @return
*/
int updateProduct(Product product);
/**
* 删除指定商品下的所有详情图
* @param productId
* @return
*/
int deleteProductImgByProductId(long productId);
- mapper
商品列表展示
Dao层的实现
- 接口
/**
* 查询
* @param productCondition
* @param rowIndex
* @param pageSize
* @return
*/
List<Product> queryProductList(@Param("productCondition") Product productCondition,
@Param("rowIndex") int rowIndex,
@Param("pageSize") int pageSize);
/**
* 查询对应的商品总数
* @param product
* @return
*/
int queryProductCount(@Param("productCondition") Product product);
- mapper
<select id="queryProductList" resultType="com.imooc.o2o.entity.Product">
SELECT
product_id,
product_name,
product_desc,
img_addr,
normal_price,
priority,
create_time,
last_edit_time,
enable_status,
product_category_id,
shop_id
FROM
tb_product
<where>
<if test="productCondition.shop != null and productCondition.shop.shopId != null">
AND shop_id = #{productCondition.shop.shopId}
</if>
<if test="productCondition.productCategory != null and productCondition.productCategory.productCategoryId != null">
AND product_category_id = #{productCondition.productCategory.productCategoryId}
</if>
<!-- 写like语句的时候一般都会写成like '% %'在mybaits写就应该写是like '%${name}%'而不是
'%#{name}%', 因为#{name}是带引号的 而${name}是不带引号的 -->
<if test="productCondition.productName != null">
AND product_name LIKE '%${productCondition.productName}%'
</if>
<if test="productCondition.enableStatus != null">
AND enable_status = #{productCondition.enableStatus}
</if>
</where>
ORDER BY
priority DESC
LIMIT #{rowIndex}, #{pageSize}
</select>
<select id="queryProductCount" resultType="int">
SELECT count(1) FROM tb_product
<where>
<if test="productCondition.shop != null and productCondition.shop.shopId != null">
AND shop_id = #{productCondition.shop.shopId}
</if>
<if test="productCondition.productCategory != null and productCondition.productCategory.productCategoryId != null">
AND product_category_id = #{productCondition.productCategory.productCategoryId}
</if>
<!-- 写like语句的时候一般都会写成like '% %'在mybaits写就应该写是like '%${name}%'而不是
'%#{name}%', 因为#{name}是带引号的 而${name}是不带引号的 -->
<if test="productCondition.productName != null">
AND product_name LIKE '%${productCondition.productName}%'
</if>
<if test="productCondition.enableStatus != null">
AND enable_status = #{productCondition.enableStatus}
</if>
</where>
</select>
- 测试
@Test
public void testBQueryProductList() throws Exception {
Product product = new Product();
List<Product> productList = productDao.queryProductList(product, 0, 3);
System.out.println(productList.size());
}
service层的实现
- 接口
/**
* 查询商品列表并且分页
* @param productCondition
* @param pageIndex
* @param pageSize
* @return
*/
ProductExecution getProductList(Product productCondition, int pageIndex, int pageSize);
- 实现类
@Override
public ProductExecution getProductList(Product productCondition, int pageIndex, int pageSize) {
// 页面转化成数据库的行码 并调用dao层取回指定页码的商品列表
int rowIndex = PageCalculator.calculateRowIndex(pageIndex, pageSize);
List<Product> productList = productDao.queryProductList(productCondition, rowIndex, pageSize);
// 基于同样的查询条件返回该条件下的商品总数
int count = productDao.queryProductCount(productCondition);
ProductExecution pe = new ProductExecution();
pe.setProductList(productList);
pe.setCount(count);
return pe;
}
Controller层的实现
- getproductlistbyshop
@RequestMapping(value = "/getproductlistbyshop", method = RequestMethod.GET)
@ResponseBody
private Map<String, Object> getProductListByShop(HttpServletRequest request) {
Map<String, Object> modelMap = new HashMap<>();
// 获取前台传过来的页面
int pageIndex = HttpServletRequestUtil.getInt(request, "pageindex");
// 获取前台穿过来的每页要求返回带商品上线
int pageSize = HttpServletRequestUtil.getInt(request, "pageSize");
// 从当前session中获取店铺信息 主要获取shopId
Shop currentShop = (Shop) request.getSession().getAttribute("currentShop");
// 空值判断
if ((pageIndex > -1) && (pageSize > -1) && (currentShop != null)) {
// 获取传入的需要的检索条件
long productCategoryId = HttpServletRequestUtil.getLong(request, "categoryId");
String productName = HttpServletRequestUtil.getString(request, "productName");
Product productCadition = compactProductCondition(currentShop.getShopId(), productCategoryId, productName);
// 传入查询条件已经分页查询 返回相应的商品列表已经总数
ProductExecution productExecution = productService.getProductList(productCadition, pageIndex, pageSize);
System.out.println("************************");
System.out.println(productExecution.getProductList().size());
System.out.println("************************");
modelMap.put("productList", productExecution.getProductList());
modelMap.put("count", productExecution.getCount());
modelMap.put("success", true);
} else {
modelMap.put("success", false);
modelMap.put("errMsg", "empty pageSize or pageIndex or currentShop!!!");
}
return modelMap;
}
private Product compactProductCondition(Long shopId, long productCategoryId, String productName) {
Product productCondition = new Product();
Shop shop = new Shop();
shop.setShopId(shopId);
productCondition.setShop(shop);
// 如果有指定类别的要求添加进去
if (productCategoryId != -1L) {
ProductCategory productCategory = new ProductCategory();
productCategory.setProductCategoryId(productCategoryId);
productCondition.setProductCategory(productCategory);
}
// 如果有商品模糊查询添加进去
if (productName != null) {
productCondition.setProductName(productName);
}
return productCondition;
}
-
测试结果
image.png
测试页面

解除商品与商品类别的联系
- 前面我们删除productCategory的时候没有把,相应的product删除
@Override
@Transactional
public ProductCategoryExecution deleteProductCategory(long productCategoryId, long shopId) throws ProductCategoryOperationException {
// TODO 将此商品类别下的商品的类别Id置为空
try {
int effectedNum = productCategoryDao.deleteProductCategory(productCategoryId, shopId);
if (effectedNum <= 0) {
throw new ProductCategoryOperationException("店铺类别删除失败");
} else {
return new ProductCategoryExecution(ProductCategoryStateEnum.SUCCESS);
}
} catch (Exception e) {
throw new ProductCategoryOperationException("deleteProductCategory error:" + e.getMessage());
}
}
- Dao层
/**
* 删除商品类别之前 将商品列表Id设置类空
* @param categoryId
* @return
*/
int updateProductCategoryToNull(long categoryId);
- mapper
<update id="updateProductCategoryToNull">
UPDATE
tb_product
SET
product_category_id = NULL
WHERE
product_category_id = #{productCategory_id}
</update>
- service层
@Override
@Transactional
public ProductCategoryExecution deleteProductCategory(long productCategoryId, long shopId) throws ProductCategoryOperationException {
// TODO 将此商品类别下的商品的类别Id置为空
try {
int effectNum = productDao.updateProductCategoryToNull(productCategoryId);
if (effectNum < 0) {
throw new ProductCategoryOperationException("店铺类别删除失败");
}
} catch (Exception e) {
throw new ProductCategoryOperationException("店铺类别删除失败" + e.getMessage());
}
try {
int effectedNum = productCategoryDao.deleteProductCategory(productCategoryId, shopId);
if (effectedNum <= 0) {
throw new ProductCategoryOperationException("店铺类别删除失败");
} else {
return new ProductCategoryExecution(ProductCategoryStateEnum.SUCCESS);
}
} catch (Exception e) {
throw new ProductCategoryOperationException("deleteProductCategory error:" + e.getMessage());
}
}
-
测试
image.png
image.png
product_category_id变为null了