本节主要是开发Product和ProductImg的添加Service方法。有异议的地方在于:Product类中的List< ProductImg >属性的存在是否必要。
public interface ProductService {
/**
*
* @param product 前端传入的Product对象,里面的信息不包括创建时间、商品状态、缩略图、详细图、productId,这些需要在方法内部进行赋值(详细图未赋值)
* @param thumbnail 缩略图封装
* @param productImgHolderList 详细图列表封装
* @return
* @throws ProductOperationException
*/
ProductExecution addProduct(Product product, ImageHolder thumbnail,
List<ImageHolder> productImgHolderList)
throws ProductOperationException;
}
新定义了ProductExecution、ImageHolder以及ProductOperationException。
ProcutExecution:Product服务层的返回类。
ImageHolder:利用ImageHolder对前面开发的ShopService层的add和modify,以及ImageUtil工具类中的generateThumbnail方法进行了重构。
ProductOperationException:继承RuntimeException类,以支持Service层的事务回滚。
dto/ProductExecution.java
//setter/getter省略
public class ProductExecution {
// 结果状态
private int state;
// 状态标识
private String stateInfo;
// 商品数量
private int count;
// 操作的product(增删改商品的时候用)
private Product product;
// 获取的product列表(查询商品列表的时候用)
private List<Product> productList;
public ProductExecution() {
}
// 失败的构造器
public ProductExecution(ProductStateEnum stateEnum) {
this.state = stateEnum.getState();
this.stateInfo = stateEnum.getStateInfo();
}
// 成功的构造器
public ProductExecution(ProductStateEnum stateEnum, Product product) {
this.state = stateEnum.getState();
this.stateInfo = stateEnum.getStateInfo();
this.product = product;
}
// 成功的构造器
public ProductExecution(ProductStateEnum stateEnum, List<Product> productList) {
this.state = stateEnum.getState();
this.stateInfo = stateEnum.getStateInfo();
this.productList = productList;
}
}
ProductExecution类专门用于Service层的返回值,这个返回值里包括了操作的状态码、信息以及需要返回的实体对象。
那么最好要有一个Enum枚举类定义和存储多个状态状态码和状态信息。
ProductStateEnum.java
//setter/getter省略
public enum ProductStateEnum {
OFFLINE(-1, "非法商品"), DOWN(0, "下架"), SUCCESS(1, "操作成功"), INNER_ERROR(-1001, "操作失败"), EMPTY(-1002, "商品为空");
private int state;
private String stateInfo;
private ProductStateEnum(int state, String stateInfo) {
this.state = state;
this.stateInfo = stateInfo;
}
public static ProductStateEnum stateOf(int index) {
for (ProductStateEnum state : values()) {
if (state.getState() == index) {
return state;
}
}
return null;
}
}
定义好枚举类之后,Service层就可以很方便的利用枚举名( 例如ProductStateEnum.SUCCESS)将当前的状态存储到Execution中返回给上一层调用,不需要记录很多的状态码和状态信息。
ImageHolder.java
//setter/getter省略
public class ImageHolder {
private String imageName;
private InputStream image;
}
ImageHolder很简单,它只是将文件的输入流和名字封装在一个类中,减少了方法的参数数量。
ProductOperationException.java
public class ProductOperationException extends RuntimeException {
public ProductOperationException(String msg){
super(msg);
}
}
@Override
@Transactional
// 1.处理缩略图,获取缩略图相对路径并赋值给product
// 2.往tb_product写入商品信息,获取productId
// 3.结合productId批量处理商品详情图
// 4.将商品详情图列表批量插入tb_product_img中
public ProductExecution addProduct(Product product,
ImageHolder thumbnail,
List<ImageHolder> productImgHolderList)
throws ProductOperationException {
// 空值判断
if (product != null && product.getShop() != null && product.getShop().getShopId() != null) {
// 给商品设置上默认属性
product.setCreateTime(new Date());
product.setLastEditTime(new Date());
// 默认为上架的状态
product.setEnableStatus(1);
// 若商品缩略图不为空则添加
if (thumbnail != null) {
addThumbnail(product, thumbnail);
}
try {
// 创建商品信息
int effectedNum = productDao.insertProduct(product);
if (effectedNum <= 0) {
throw new ProductOperationException("创建商品失败");
}
} catch (Exception e) {
throw new ProductOperationException("创建商品失败:" + e.toString());`
}
// 若商品详情图不为空则添加
if (productImgHolderList != null && productImgHolderList.size() > 0) {
addProductImgList(product, productImgHolderList);
}
return new ProductExecution(ProductStateEnum.SUCCESS, product);
} else {
// 传参为空则返回空值错误信息
return new ProductExecution(ProductStateEnum.EMPTY);
}
}
Service内部实现主要做了什么工作呢?
首先确保前端js表单封装传入的Product对象以及类中的Shop对象以及Shop对象中的shopid值是否为空,如果为空,直接向Controller层抛出ProductStateEnum.EMPTY空值状态异常。(也说明应用允许店主创建商品时,不上传缩略图和详细图)
接着,向Product对象中添加商品创建时间、修改时间、商品上架状态,接着判断用户是否已上传商品的缩略图。
如果上传则调用addThumbnail方法(见下面),利用工具类Util的方法getShopImagePath获得该商品所属店铺的图片目录相对路径,然后调用工具类Util的方法generateThumbnail将缩略图放在服务器对应店铺的图片目录下,并把缩略图的相对地址放到传入的Product对象的imgAddr属性中。
然后调用DAO层的insertProduct方法,将Product对象传入,由于Dao层的配置(商品添加之Dao层实现),Dao层SQL语句执行完毕后,会将数据库中获得的product_id的主键赋值给Product对象中的productId。获得productId是因为,接下来添加的商品的详细图的数据库表依赖于商品id号(外键),所以在添加详细图之前,一定要获得商品的id号。
接下来便进行添加商品详情图操作,首先判断详情图是否存在,然后调用addProductImgList方法(见下面),通过商品的店铺Id获得店铺的图片相对路径,通过循环的方式,将图片存入到图片路径中,并且封装ProductImg对象,不断地加入到List中,最后调用Dao层的batchInsertProductImg(productImgList)方法将详细图的相对路径存入到数据库中。
最后返回给Controller层ProductExecution类型的操作结果。
总的来说,进行了两次Dao操作,一次是Product表信息,一次是Product_img表信息。Product_img表依赖于Product的id号,所以在后面操作。
特殊的是Product表信息处理时需要将缩略图存入到服务器后,获得相对路径存入到数据库中。所以使用了Util工具类中的方法。Product_img表同理。
/**
* 添加缩略图
* @param product
* @param thumbnail
*/
private void addThumbnail(Product product, ImageHolder thumbnail) {
String dest = PathUtil.getShopImagePath(product.getShop().getShopId());
String thumbnailAddr = ImageUtil.generateThumbnail(thumbnail, dest);
product.setImgAddr(thumbnailAddr);
}
/**
* 批量添加图片
*
* @param product
* @param productImgHolderList
*/
private void addProductImgList(Product product, List<ImageHolder> productImgHolderList) {
// 获取图片存储路径,这里直接存放到相应店铺的文件夹底下
String dest = PathUtil.getShopImagePath(product.getShop().getShopId());
List<ProductImg> productImgList = new ArrayList<ProductImg>();
// 遍历图片一次去处理,并添加进productImg实体类里
for (ImageHolder productImgHolder : productImgHolderList) {
String imgAddr = ImageUtil.generateNormalImg(productImgHolder, 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.toString());
}
}
}
ProductServiceTest.java
public class ProductServiceTest extends BaseTest {
@Autowired
private ProductService productService;
@Test
public void testAddProduct() throws ShopOperationException, FileNotFoundException {
// 创建shopId为1且productCategoryId为1的商品实例并给其成员变量赋值
Product product = new Product();
Shop shop = new Shop();
shop.setShopId(1L);
ProductCategory pc = new ProductCategory();
pc.setProductCategoryId(1L);
product.setShop(shop);
product.setProductCategory(pc);
product.setProductName("测试商品1");
product.setProductDesc("测试商品1");
product.setPriority(20);
product.setCreateTime(new Date());
// 创建缩略图文件流
File thumbnailFile = new File("/home/zjc/xiaohuangren.jpg");
InputStream is = new FileInputStream(thumbnailFile);
ImageHolder thumbnail = new ImageHolder(thumbnailFile.getName(), is);
// 创建两个商品详情图文件流并将他们添加到详情图列表中
File productImg1 = new File("/home/zjc/xiaohuangren.jpg");
InputStream is1 = new FileInputStream(productImg1);
File productImg2 = new File("/home/zjc/xiaohuangren.jpg");
InputStream is2 = new FileInputStream(productImg2);
List<ImageHolder> productImgList = new ArrayList<ImageHolder>();
productImgList.add(new ImageHolder(productImg1.getName(), is1));
productImgList.add(new ImageHolder(productImg2.getName(), is2));
// 添加商品并验证
ProductExecution pe = productService.addProduct(product, thumbnail, productImgList);
assertEquals(ProductStateEnum.SUCCESS.getState(), pe.getState());
}
}