我们来进行商品修改方法的实现。
我们呢先看成型图
其实和商铺修改相似,所以需要实现两个功能
第一个通过商品id获取商品初始类型
第二个接受前台输入的数据更新商品
我们两个方法一起说
DAO:
修改商品因为修改的是product额多个属性,所以用product包装
获取商品信息只需要获得商品的id,即可查出。
mapper.xml:
<!--通过传入的product修改相应的信息-->
<update id="modifyProduct" >
UPDATE tb_product
<set>
<if test="productName!=null">product_name=#{productName},</if>
<if test="productDesc!=null">product_desc=#{productDesc},</if>
<if test="imgAddr!=null">img_addr=#{imgAddr},</if>
<if test="normalPrice!=null">normal_price=#{normalPrice},</if>
<if test="promotionPrice!=null">promotion_price=#{promotionPrice},</if>
<if test="priority!=null">priority=#{priority},</if>
<if test="lastEditTime!=null">last_edit_time=#{lastEditTime},</if>
<if test="enableStatus!=null">enable_status=#{enableStatus},</if>
<if test="productCategory != null and productCategory.productCategoryId != null ">product_category_id=#{productCategory.productCategoryId}</if>
</set>
WHERE product_id=#{productId} AND shop_id=#{shop.shopId}
</update>
Service:
//通过商品id获得商品信息
Product getProductById(long productId);
//修改商品信息
ProductExecution modifyProduct(Product product,ImageHolder imageHolder, List<ImageHolder> prodImgDetailList) throws ProductOperationException;
修改商品信息的三个参数分别是修改的product,文件缩略图包装类,文件详情图包装类。
ServiceImpl:
//根据商品id获取商品信息
@Override
public Product getProductById(long productId) { return productDao.getProductById(productId);}
我们主要看修改页面的方法:
第一步:判断product和shopid是否为空(注意,这里只能判断这么多,下架商品也是通过修改界面实现的,修改商品上下架状态时只能传入product和shopid如果判断别的会造成无法成功执行操作)
第二步:判断传入的缩略图是否为空,如果不为空我们判断之前是否有缩略图,如果有删除原有的。然后加入新的图片
通过addProductImg方法将路径存入product的属性中
第三步:判断传入的商品详情图是否为空,如果不为空我们删除原来的存储的图片,之后调用addproductImgList方法将所有图片和传入服务器并将路径写入productImg属性供下面操作
第四步,完成操作后,我们通过获得的所有信息,调用修改的方法,进行修改,并且设置操作状态值并传给前端
/*
* 根据前端传入的信息我们进行修改
* 如果更改了product的里的属性我们就更改那些更改了的属性
* 如果传入文件缩略图,我们就对原来的照片采取删除操作重新存储,且把地址写入product表中
* 如果传入商品详情图,我们就把原来的商品详情图全部删除,存入新的并更新Img表
* */
@Override
public ProductExecution modifyProduct(Product product, ImageHolder imageHolder, List<ImageHolder> prodImgDetailList) throws ProductOperationException {
//先设置属性
if (product != null && product.getShop()!=null&&product.getShop().getShopId() != null ) {
product.setLastEditTime(new Date());
//如果传入的缩略图不为空我们更新缩略图
if (imageHolder!=null){
//通过商品id获取商品的信息
Product tempProduct=productDao.getProductById(product.getProductId());
if (tempProduct.getImgAddr()!=null){//删除旧的缩略图,因为缩略图不想商品详情图存入单独一个表,所以我们不用再写一个dao
ImageUtil.deleteFileOrPath(tempProduct.getImgAddr());
}
//添加缩略图
addProductImg(product,imageHolder);
}
//接下来我们完成对商品详情图的处理
if(prodImgDetailList!=null&&prodImgDetailList.size()>0){//如果也传入了详情图
//删除表中详情图片
deleteProductImgs(product.getProductId());//通过商品id删除详情图库中的记录,并且删除相应文件,还有product表中的详情图相应的属性
addProductImgList(product,prodImgDetailList);//存入新的详情图
}
try {
//更新product
int effectNum=productDao.modifyProduct(product);
if (effectNum<=0){
throw new ProductOperationException("商品更新失败");//抛出异常
}
return new ProductExecution(ProductStateEnum.SUCCESS,product);
}catch (Exception e){
throw new ProductOperationException("商品更新失败"+e.getMessage());
}
}
else {
return new ProductExecution(ProductStateEnum.NULL_PARAMETER);
}
}
private void addProductImg(Product product, ImageHolder imageHolder) {//存入图片的方法
// 根据shopId获取图片存储的相对路径
String relativePath = PathUtil.getProductImagePath(product.getProductId(),product.getShop().getShopId());
// 添加图片到指定的目录
String relativeAddr = ImageUtil.generateThumnail(imageHolder, relativePath);
// 将relativeAddr设置给product
product.setImgAddr(relativeAddr);
}
/**
* 批量添加商品图片
*
* @param product
* @param productImgHolderList
*/
private void addProductImgList(Product product, List<ImageHolder> productImgHolderList) {
// 获取图片存储路径,这里直接存到相应店铺的文件夹下
String desc = PathUtil.getProductDetaisImagePath(product.getProductId(),product.getShop().getShopId());
List<ProductImg> productImgList = new ArrayList<>();
// 遍历图片依次去处理,并添加进 productImg 实体类中
for (ImageHolder productImageHolder : productImgHolderList) {
String imgAddr = ImageUtil.generateNormalImg(productImageHolder, desc);
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());
}
}
}
private void deleteProductImgs(Long productId) {
// 获取该商铺下对应的productImg信息
List<ProductImg> productImgList = productDao.getProductById(productId).getProductImgList();
// 遍历删除该目录下的全部文件
for (ProductImg productImg : productImgList) {
ImageUtil.deleteFileOrPath(productImg.getImgAddr());
}
// 删除tb_product_img中该productId对应的记录
productImgDao.deleteProductImgById(productId);
}
}
controller:
通过商品id获得商品,没什么要说的
/**
* 通过商品id获得商品信息
* 我们不仅要加载商品信息,还要加载当前商铺的商品所有类型,使其显示在下拉框让客户完成选择
*/
@RequestMapping(value ="/getproductbyid" ,method = RequestMethod.GET)
@ResponseBody
private Map<String,Object> getProductById(@RequestParam long productId){
Map<String,Object> modelMap=new HashMap<String, Object>();//返回给前台使用
if (productId>0){
Product product=productService.getProductById(productId);//通过商品id获得商品数据
List<ProductCategory> productCategoryList=productCategoryService.getProductCategoryList(product.getShop().getShopId());//通过商铺id获取商铺所有商品类别
modelMap.put("product",product);
modelMap.put("productCategoryList",productCategoryList);
modelMap.put("success",true);
}else {
modelMap.put("success",false);
modelMap.put("errMsg","empty pageSize or pageIndex or shopId");
}
return modelMap;
}
通过传入的数据更改商品信息
第一步:通过验证码测试,注意可能我们修改的只是上下架,那么要在判断前判断是不是下架如果是,不进行验证码操作
第二步:获取前端传要修改的数据,用mapperObject解析
第三步:判断是否有文件流上传,如果有文件啊流上传我们需要特殊件处理
①.将request请求转为文件spring处理文件的requset
②.从multipartHttpServletRequest获取文件
③将获得的文件封装起来交给下步处理
第四步:和上一步的过程一样处理商品详情图,进行封装
第五步:从session获得当前店铺编号
第六步:传入前几步封装的信息进行更改
/*
* 我们完成修改product的controller操作
* */
@RequestMapping(value ="/modifyproduct" ,method = RequestMethod.POST)
@ResponseBody
private Map<String,Object> modifyProduct(HttpServletRequest request){
Map<String, Object> modelMap = new HashMap<String, Object>();
// 是商品编辑时候调用还是上下架操作的时候调用
// 若为前者则进行验证码判断,后者则跳过验证码判断
boolean statusChange = HttpServletRequestUtil.getBoolean(request, "statusChange");
// 验证码检验
if (!statusChange && !CodeUtil.checkVerifyCode(request)) {
modelMap.put("success", false);
modelMap.put("errMsg", "验证码错误!");
return modelMap;
}
Product product = null;
// 接收前端传递过来的product
String productStr = null;
// 商品图片缩略图(输入流和名称的封装类)
ImageHolder thumbnail = null;
// 将HttpServletRequest转型为MultipartHttpServletRequest,可以很方便地得到文件名和文件内容
MultipartHttpServletRequest multipartHttpServletRequest = null;
// 接收商品缩略图
CommonsMultipartFile thumbnailFile = null;
// 接收商品详情图片
List<ImageHolder> productDetailImgList = new ArrayList<ImageHolder>();
// 创建一个通用的多部分解析器
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
// Step2: 使用FastJson提供的api,实例化Product 构造调用service层的第一个参数
ObjectMapper mapper = new ObjectMapper();
// 获取前端传递过来的product,约定好使用productStr
try {
productStr = HttpServletRequestUtil.getString(request, "productStr");
product = mapper.readValue(productStr, Product.class);
} catch (Exception e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
// Step3: 商品缩略图 和 商品详情图 构造调用service层的第二个参数和第三个参数
try {
// 判断 request 是否有文件上传,即多部分请求
if (commonsMultipartResolver.isMultipart(request)) {
// 将request转换成多部分request
multipartHttpServletRequest = (MultipartHttpServletRequest) request;
// 得到缩略图的CommonsMultipartFile ,和前端约定好使用thumbnail 传递
// ,并构建ImageHolder对象
thumbnailFile = (CommonsMultipartFile) multipartHttpServletRequest.getFile("thumbnail");
// 转化为ImageHolder,使用service层的参数类型要求
thumbnail = new ImageHolder(thumbnailFile.getOriginalFilename(), thumbnailFile.getInputStream());
// 得到 商品详情的列表,和前端约定使用productImg + i 传递 ,并构建ImageHolder对象
for (int i = 0; i < IMAGEMAXCOUNT; i++) {
CommonsMultipartFile productDetailImgFile = (CommonsMultipartFile) multipartHttpServletRequest.getFile("productImg" + i);
if (productDetailImgFile != null) {
ImageHolder productDetailImg = new ImageHolder( productDetailImgFile.getOriginalFilename(),productDetailImgFile.getInputStream());
productDetailImgList.add(productDetailImg);
} else {
// 如果从请求中获取的到file为空,终止循环
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
// Step4 调用Service层
if (product != null ) {
try {
// 从session中获取shop信息,不依赖前端的传递更加安全
Shop currentShop = (Shop) request.getSession().getAttribute("currentShop");
product.setShop(currentShop);
// 调用addProduct
ProductExecution pe = productService.modifyProduct(product, thumbnail, productDetailImgList);
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.toString());
return modelMap;
}
} else {
modelMap.put("success", false);
modelMap.put("errMsg", "请输入商品信息");
}
return modelMap;
}
VIEW:
productoperation.js
* 因为商品的添加和编辑复用同一个页面,所以需要根据url中的商品Id来判断
*/
$(function(){
//通过url是否含有productId来判断是添加商品还是编辑
var productId = getQueryString('productId');
// 标示符 productId非空则为true即编辑,否则为添加商品
var isEdit = productId ? true : false ;
// 商品添加URL 用于提交
var addProductURL = '/storepro/shopadmin/addproduct';
// 商品编辑URL 用于提交
var editProductURL = '/storepro/shopadmin/modifyproduct?productId=' + productId;
// 获取商品初始化信息的URL 根据页面原型只需要获取productCategory即可,后台调用之前写好的路由方法即可
var categoryInfoURL = '/storepro/shopadmin/getproductcategorybyshopid';
// 商品编辑URL 用于从后台加载该product的基本信息,页面展示用
var productInitURL = '/storepro/shopadmin/getproductbyid?productId=' + productId;
// 通过标示符,确定调用的方法
if(isEdit){
// 为true,则根据productId调用获取product信息的方法
showEditProductPage(productId);
}else{
// 为false,则初始化新增product页面
showAddProductPage();
}
/**
* 始化新增product页面
*
* 根据页面原型和数据模型,需要加载该shop对应的productCategory信息(shop信息从服务端session中获取)
*/
function showAddProductPage(){
$.getJSON(categoryInfoURL,
function(data){
if(data.success){
// 设置product_category
var productCategoryList = data.data;
var productCategoryTempHtml = '';
productCategoryList.map(function(item, index) {
// productCategoryTempHtml += '<option data-id="'
// + item.productCategoryId + '">' + item.productCategoryName
// + '</option>';
productCategoryTempHtml += '<option data-value="'
+ item.productCategoryId + '">'
+ item.productCategoryName + '</option>';
});
$('#product-category').html(productCategoryTempHtml);
}else{
$.toast(data.errMsg)
}
});
};
/**
* 点击控件的最后一个且图片数量小于6个的时候,生成一个选择框
*/
$('.detail-img-div').on('change', '.detail-img:last-child', function() {
if ($('.detail-img').length < 6) {
$('#detail-img').append('<input type="file" class="detail-img">');
}
});
/**
* 编辑页面调用的function
*/
function showEditProductPage(productId){
$.getJSON(
productInitURL,
function(data) {
if (data.success) {
var product = data.product;
$('#product-name').val(product.productName);
$('#product-desc').val(product.productDesc);
$('#priority').val(product.priority);
$('#normal-price').val(product.normalPrice);
$('#promotion-price').val(
product.promotionPrice);
var optionHtml = '';
var optionArr = data.productCategoryList;
var optionSelected = product.productCategory.productCategoryId;
optionArr.map(function(item, index) {
var isSelect = optionSelected === item.productCategoryId ? 'selected'
: '';
optionHtml += '<option data-value="'
+ item.productCategoryId
+ '"'
+ isSelect
+ '>'
+ item.productCategoryName
+ '</option>';
});
$('#product-category').html(optionHtml);
}
});
};
/**
* 提交按钮的响应时间,分别对商品添加和商品编辑做不同的相应
*/
$('#submit').click(
function(){
// 创建商品Json对象,并从表单对象中获取对应的属性值
var product = {};
// 如果是编辑操作,需要传入productId
if(isEdit){
product.productId = productId;
}
product.productName = $('#product-name').val();
product.productDesc = $('#product-desc').val();
// 获取商品的特定目录值
product.productCategory = {
productCategoryId : $('#product-category').find('option').not(
function() {
return !this.selected;
}).data('value')
};
product.priority = $('#priority').val();
product.normalPrice = $('#normal-price').val();
product.promotionPrice = $('#promotion-price').val();
// 生成表单对象用于接收参数并传递给后台
var formData = new FormData();
// 缩略图 (只有一张),获取缩略图的文件流
var thumbnail = $('#small-img')[0].files[0];
formData.append('thumbnail',thumbnail);
// 图片详情
$('.detail-img').map(
function(index, item) {
// 判断该控件是否已经选择了文件
if ($('.detail-img')[index].files.length > 0) {
// 将第i个文件流赋值给key为productImgi的表单键值对里
formData.append('productImg' + index,
$('.detail-img')[index].files[0]);
}
});
// 将product 转换为json ,添加到forData
formData.append('productStr', JSON.stringify(product));
// 获取表单中的验证码
var verifyCodeActual = $('#j_captcha').val();
if (!verifyCodeActual) {
$.toast('请输入验证码!');
return;
}
formData.append("verifyCodeActual", verifyCodeActual);
// 使用ajax异步提交
$.ajax({
url: isEdit?editProductURL:addProductURL,
type: 'POST' ,
data : formData,
contentType : false,
processData : false,
cache : false,
success: function(data){
if (data.success) {
$.toast('提交成功!');
$('#captcha_img').click();
} else {
$.toast('提交失败!');
$('#captcha_img').click();
}
}
});
});
});
在前台中已经完成了相应的上下架操作,通过前台传入的数据我们我们后台能进行相应的上下架操作
试验:
我们必须先进入商品商铺管理页面,因为我们需要这里的shopid
通过这样的url访问修改页面