8-1 商品添加之Dao层的实现
ProductDao:
/**
* 插入商品
* @param product
* @return
*/
int insertProduct(Product product);
<insert id="insertProduct" parameterType="com.doit.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>
8-2 商品添加之Service层的实现上
ProductService:
/**
* 添加商品信息以及图片处理
* @param product
* @param thumbnail
* @param productImgs
* @param productImgList
* @param productImgNameList
* @return
* @throws ProductOperationException
*/
ProductExecution addProduct(Product product,ImageHolder thumbnail,
List<ImageHolder> productImgHolderList) throws ProductOperationException;
@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 affectedNum = productDao.insertProduct(product);
if(affectedNum<=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);
}
}
/**
* 添加缩略图
* @param product
* @param thumbnail
*/
private void addThumbnail(Product product,ImageHolder thumbnail){
//图片存储的目标目录
String dest = PathUtil.getShopImagePath(product.getShop().getShopId());
String imgAddr = ImageUtil.generateThumbnail(thumbnail, dest);
product.setImgAddr(imgAddr);
}
这里修改了ImageUtil.generateThumbnail方法:
public static String generateThumbnail(ImageHolder thumbnail, String targetAddr){
//随机生成文件名
String realFileName = getRandomFileName();
//获取图片扩展名
String extension = getFileExtension(thumbnail.getImageName());
//创建目标目录路径
makeDirPath(targetAddr);
//目标图片相对路径名
String relativeAddr = targetAddr+realFileName+extension;
File dest = new File(PathUtil.getImgBasePath()+relativeAddr);
//创建缩略图
try {
Thumbnails.of(thumbnail.getImage()).size(200, 200)//设置大小
.watermark(Positions.BOTTOM_RIGHT,ImageIO.read(new File(basePath+"/watermark.png")),0.25f)//指定水印位置、图源、透明度
.outputQuality(0.8f).toFile(dest);//压缩图片、图片全路径
} catch (IOException e) {
e.printStackTrace();
}
return relativeAddr;
}
新建ImageHolder类封装了文件名和输入流:
public class ImageHolder {
private String imageName;
private InputStream image;
public ImageHolder(String imageName, InputStream image) {
super();
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;
}
}
8-3 商品添加之Service层的实现下
/**
* 批量添加图片
* @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>();
long productId = product.getProductId();
for(ImageHolder productImgHolder:productImgHolderList){
String imgAddr = ImageUtil.generateNormalImg(productImgHolder, dest);
ProductImg productImg = new ProductImg();
productImg.setImgAddr(imgAddr);
productImg.setProductId(productId);
productImg.setCreateTime(new Date());
productImgList.add(productImg);
}
//
if(productImgList.size()>0){
try{
int affectedNum = productImgDao.batchInsertProductImg(productImgList);
if(affectedNum<=0){
throw new ProductOperationException("创建商品详情图片失败");
}
}catch(Exception e){
throw new ProductOperationException("创建商品详情图片失败"+e.toString());
}
}
}
productImgDao:
/**
* 批量添加商品详情图片
* @param productImgList
* @return
*/
int batchInsertProductImg(List<ProductImg> productImgList);
<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.imgDesc},
#{productImg.priority},
#{productImg.createTime},
#{productImg.productId}
)
</foreach>
</insert>
8-4 商品添加之Controller层的实现
@RequestMapping(value="/addproduct",method=RequestMethod.POST)
@ResponseBody
private Map<String,Object> addProduct(HttpServletRequest request){
Map<String,Object> modelMap = new HashMap<String,Object>();
//验证码校验
if(!CodeUtil.checkVerifyCode(request)){
modelMap.put("success", false);
modelMap.put("erMsg", "输入了错误的验证码");
return modelMap;
}
//接收前端参数的变量的初始化,包括商品、缩略图、详情图列表实体类
ObjectMapper mapper = new ObjectMapper();
Product product = null;
String productStr = HttpServletRequestUtil.getString(request, "productStr");
ImageHolder thumbnail = null;
List<ImageHolder> productImageHolderList= new ArrayList<ImageHolder>();
CommonsMultipartResolver multipartResolver= new CommonsMultipartResolver(
request.getSession().getServletContext());
try{
//若请求中存在文件流,则取出相关文件(包括缩略图和详情图)
if(multipartResolver.isMultipart(request)){
thumbnail = handleImage(request, thumbnail,productImageHolderList);
}else{
modelMap.put("success", false);
modelMap.put("errMsg", "上传图片不能为空");
return modelMap;
}
}catch(Exception e){
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
try{
//尝试获取前端传过来的表单string流并将其转换成Product实体类
product = mapper.readValue(productStr, Product.class);
}catch(Exception e){
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
//若Product信息,缩略图以及详情图列表为非空,则开始进行商品添加操作
if(product!=null&&thumbnail!=null&&productImageHolderList.size()>0){
try{
//从session中获取当前店铺的Id并赋值给product,减少对前端数据的依赖
Shop currentShop = (Shop) request.getSession().getAttribute("currentShop");
// Shop shop = new Shop();
// shop.setShopId(currentShop.getShopId());
product.setShop(currentShop);
//执行添加操作
ProductExecution pe = productService.addProduct(product, thumbnail, productImageHolderList);
if(pe.getState()==ProductStateEnum.SUCCESS.getState()){
modelMap.put("success", true);
}else{
modelMap.put("success", false);
modelMap.put("errMsg", pe.getStateInfo());
}
}catch(RuntimeException e){
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
}else{
modelMap.put("success", false);
modelMap.put("errMsg", "请输入商品信息");
}
return modelMap;
}
private ImageHolder handleImage(HttpServletRequest request, ImageHolder thumbnail,List<ImageHolder> productImageHolderList)
throws IOException {
MultipartHttpServletRequest multipartRequest;
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 productImageHolder = new ImageHolder(productImgFile.getOriginalFilename(),productImgFile.getInputStream());
productImageHolderList.add(productImageHolder);
}else{
//若取出的第i个详情图文件流为空,则终止循环
break;
}
}
return thumbnail;
}
8-5 商品添加之前端实现上
productoperation.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>SUI Mobile Demo</title>
<meta name="description"
content="MSUI: Build mobile apps with simple HTML, CSS, and JS components.">
<meta name="author" content="阿里巴巴国际UED前端">
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<link rel="shortcut icon" href="/favicon.ico">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="format-detection" content="telephone=no">
<!-- Google Web Fonts -->
<link rel="stylesheet"
href="//g.alicdn.com/msui/sm/0.6.2/css/sm.min.css">
<link rel="stylesheet"
href="//g.alicdn.com/msui/sm/0.6.2/css/sm-extend.min.css">
<link rel="stylesheet"
href="../resources/css/shop/productmanagement.css">
</head>
<body>
<header class="bar bar-nav">
<h1 class="title">商品编辑</h1>
</header>
<div class="content">
<div class="list-block">
<ul>
<!-- 商品名称 text -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-name"></i>
</div>
<div class="item-inner">
<div class="item-title label">商品名称</div>
<div class="item-input">
<input type="text" id="product-name" placeholder="商品名称">
</div>
</div>
</div>
</li>
<!-- 目录 下拉列表 -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-email"></i>
</div>
<div class="item-inner">
<div class="item-title label">目录</div>
<div class="item-input">
<select id="category">
</select>
</div>
</div>
</div>
</li>
<!-- 优先级 下拉列表 -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-name"></i>
</div>
<div class="item-inner">
<div class="item-title label">优先级</div>
<div class="item-input">
<input type="number" id="priority" placehlder="数字越大越排前面">
</div>
</div>
</div>
</li>
<!-- 原价 text -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-email"></i>
</div>
<div class="item-inner">
<div class="item-title label">原价</div>
<div class="item-input">
<input type="number" id="normal-price" placeholder="可选">
</div>
</div>
</div>
</li>
<!-- 现价 text -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-email"></i>
</div>
<div class="item-inner">
<div class="item-title label">现价</div>
<div class="item-input">
<input type="number" id="promotion-price" placeholder="可选">
</div>
</div>
</div>
</li>
<!-- 缩略图 text -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-email"></i>
</div>
<div class="item-inner">
<div class="item-title label">缩略图</div>
<div class="item-input">
<input type="file" id="small-img">
</div>
</div>
</div>
</li>
<!-- -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-email"></i>
</div>
<div class="item-inner detail-img-div">
<div class="item-title label">详情图片</div>
<div class="item-input" id="detail-img">
<input type="file" class="detail-img">
</div>
</div>
</div>
</li>
<!-- 店铺简介 textarea -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-email"></i>
</div>
<div class="item-inner">
<div class="item-title label">商品描述</div>
<div class="item-input">
<textarea id="product-desc" placeholder="商品描述"></textarea>
</div>
</div>
</div>
</li>
<!-- 验证码 -->
<li>
<div class="item-content">
<div class="item-media">
<i class="icon icon-form-email"></i>
</div>
<div class="item-inner">
<label for="j_captcha" class="item-title label">验证码</label>
<input type="text" id="j_captcha" name="j_captcha"
class="form_control in" placeholder="验证码"/>
<div class="item-input">
<img id="captcha_img" alt="点击更换" title="点击更换"
onclick="changeVerifyCode(this)" src="../Kaptcha" />
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="content-block">
<div class="row">
<div class="col-50">
<a href="/o2o/shopadmin/productmanagement" class="button button-big button-fill button-danger">返回商品管理</a>
</div>
<div class="col-50">
<a href="#" class="button button-big button-fill" id="submit">提交</a>
</div>
</div>
</div>
</div>
<script type='text/javascript'
src='//g.alicdn.com/sj/lib/zepto/zepto.min.js' charset='utf-8'></script>
<script type='text/javascript'
src='//g.alicdn.com/msui/sm/0.6.2/js/sm.min.js' charset='utf-8'></script>
<script type='text/javascript'
src='//g.alicdn.com/msui/sm/0.6.2/js/sm-extend.min.js' charset='utf-8'></script>
<script type='text/javascript' src='../resources/js/common/common.js'
charset='utf-8'></script>
<script type='text/javascript'
src='../resources/js/shop/productoperation.js' charset='utf-8'></script>
</body>
</html>
8-6 商品添加之前端实现下
productoperation.js:
$(function() {
//从URL里获取productId参数的值
var productId = getQueryString('productId');
//通过productId获取商品信息的URL
var infoUrl = '/o2o/shopadmin/getproductbyid?productId=' + productId;
//获取当前店铺设定的商品类别列表的URL
var categoryUrl = '/o2o/shopadmin/getproductcategorylist';
//更新商品信息的URL
var productPostUrl = '/o2o/shopadmin/modifyproduct';
//由于商品添加和编辑使用的是同一个页面
//该标识符用来标明本次是添加还是编辑操作
var isEdit = false;
if (productId) {
//若有productId则为编辑操作
getInfo(productId);
isEdit = true;
} else {
getCategory();
productPostUrl = '/o2o/shopadmin/addproduct';
}
//获取需要编辑的商品的商品信息,并赋值给表单
function getInfo(id) {
$
.getJSON(
infoUrl,
function(data) {
if (data.success) {
//从返回的JSON当中获取product对象的信息,并赋值给表单
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;
//生成前端的HTML商品类别列表,并默认选择编辑前的商品类别
optionArr
.map(function(item, index) {
var isSelect = optionSelected === item.productCategoryId ? 'selected'
: '';
optionHtml += '<option data-value="'
+ item.productCategoryId
+ '"'
+ isSelect
+ '>'
+ item.productCategoryName
+ '</option>';
});
$('#category').html(optionHtml);
}
});
}
//为商品添加操作提供该店铺下所有的商品类别列表
function getCategory() {
$.getJSON(categoryUrl, function(data) {
if (data.success) {
var productCategoryList = data.data;
var optionHtml = '';
productCategoryList.map(function(item, index) {
optionHtml += '<option data-value="'
+ item.productCategoryId + '">'
+ item.productCategoryName + '</option>';
});
$('#category').html(optionHtml);
}
});
}
//针对商品详情图控件组,若该控件组的最后一个元素发生变化(即上传了图片)
//且控=控件总数未达到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">');
}
});
//提交按钮的事件响应,分别对商品添加和编辑做出不同的响应
$('#submit').click(
function() {
//创建商品json对象,并从表单里面获取对应的属性值
var product = {};
product.productName = $('#product-name').val();
product.productDesc = $('#product-desc').val();
product.priority = $('#priority').val();
product.normalPrice = $('#normal-price').val();
product.promotionPrice = $('#promotion-price').val();
//获取选定的商品类别值
product.productCategory = {
productCategoryId : $('#category').find('option').not(
function() {
return !this.selected;
}).data('value')
};
product.productId = productId;
//获取缩略图文件流
var thumbnail = $('#small-img')[0].files[0];
console.log(thumbnail);
//生成表单对象,用于接收参数并传递给后台
var formData = new FormData();
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对象转成字符流保存至表单对象key为productStr的键值对里
formData.append('productStr', JSON.stringify(product));
//获取表单里输入的验证码
var verifyCodeActual = $('#j_captcha').val();
if (!verifyCodeActual) {
$.toast('请输入验证码!');
return;
}
//将数据提交至后台处理相关操作
formData.append("verifyCodeActual", verifyCodeActual);
$.ajax({
url : productPostUrl,
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();
}
}
});
});
});
8-7 商品编辑之后端开发上
dao层:
/**
* 更新商品信息
* @param product
* @return
*/
int updateProduct(Product product);
<update id="updateProduct" parameterType="com.doit.o2o.entity.Product"
keyProperty="product_id" useGeneratedKeys="true">
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层:
/**
* 修改商品信息以及图片处理
* @param product
* @param thumbnail
* @param productImageHolderList
* @return
* @throws ProductOperationException
*/
ProductExecution modifyProduct(Product product,ImageHolder thumbnail,List<ImageHolder> productImageHolderList)
throws ProductOperationException;
@Override
@Transactional
//1.若缩略图参数有值,则处理缩略图,
//若原先存在缩略图则先删除再添加新图,之后获取缩略图相对路径并赋值给product
//2.若商品详情图列表参数有值,对商品详情图片列表进行同样的操作
//3.将tb_product_img下面的该商品原先的商品详情图记录全部清除
//4.更新tb_product的信息
public ProductExecution modifyProduct(Product product, ImageHolder thumbnail,
List<ImageHolder> productImageHolderList) throws ProductOperationException {
//空值判断
if(product!=null&&product.getShop()!=null&&product.getShop().getShopId()!=null){
//给商品设置默认属性
product.setLastEditTime(new Date());
//若商品缩略图不为空且原有缩略图不为空则删除原有缩略图并添加
if(thumbnail!=null){
//先获取一遍原有信息,因为原来的信息里有原图片地址
Product tempProduct = productDao.queryProductById(product.getProductId());
if (tempProduct.getImgAddr() != null) {
FileUtil.deleteFileOrPath(tempProduct.getImgAddr());
}
addThumbnail(product, thumbnail);
}
//如果有新存入的商品详情图,则将原有的删除,并添加新的图片
if (productImageHolderList != null && productImageHolderList.size() > 0) {
deleteProductImgList(product.getProductId());
addProductImgList(product, productImageHolderList);
}
try {
//更新商品信息
int affectedNum = productDao.updateProduct(product);
if (affectedNum <= 0) {
throw new ProductOperationException("更新商品信息失败");
}
return new ProductExecution(ProductStateEnum.SUCCESS, product);
} catch (Exception e) {
throw new ProductOperationException("更新商品信息失败:" + e.toString());
}
}else {
return new ProductExecution(ProductStateEnum.EMPTY);
}
}
8-8 商品编辑之后端开发下
Controller层:
/**
* 商品编辑
* @param request
* @return
*/
@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;
}
//接收前端参数的变量的初始化,包括商品,缩略图,详情图片列表实体类
ObjectMapper mapper = new ObjectMapper();
Product product = null;
ImageHolder thumbnail = null;
List<ImageHolder> productImageHolderList = new ArrayList<ImageHolder>();
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
request.getSession().getServletContext());
//若请求中存在文件流,则取出相关的文件(包括缩略图和详情图)
try{
if (multipartResolver.isMultipart(request)) {
thumbnail = handleImage(request, thumbnail,productImageHolderList);
}
}catch (Exception e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
try {
String productStr = HttpServletRequestUtil.getString(request, "productStr");
//尝试获取前端传过来的表单String流并将其转换成Product实体类
product = mapper.readValue(productStr, Product.class);
} catch (Exception e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
if (product != null) {
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.modifyProduct(product,
thumbnail, productImageHolderList);
if (pe.getState() == ProductStateEnum.SUCCESS.getState()) {
modelMap.put("success", true);
} else {
modelMap.put("success", false);
modelMap.put("errMsg", pe.getStateInfo());
}
} catch (RuntimeException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
} else {
modelMap.put("success", false);
modelMap.put("errMsg", "请输入商品信息");
}
return modelMap;
}
8-9 商品编辑之前端实现
productmanagement.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>商品管理</title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<link rel="shortcut icon" href="/favicon.ico">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="stylesheet" href="//g.alicdn.com/msui/sm/0.6.2/css/sm.min.css">
<link rel="stylesheet" href="//g.alicdn.com/msui/sm/0.6.2/css/sm-extend.min.css">
<link rel="stylesheet" href="../resources/css/shop/productmanagement.css">
</head>
<body>
<header class="bar bar-nav">
<h1 class="title">商品管理</h1>
</header>
<div class="content">
<div class="content-block">
<div class="row row-product">
<div class="col-30">商品名称</div>
<div class="col-20">优先级</div>
<div class="col-50">操作</div>
</div>
<div class="product-wrap">
<!-- <div class="row row-product">
<div class="col-40">商品名称</div>
<div class="col-60">
<a href="#">编辑</a>
<a href="#">删除</a>
<a href="#">预览</a>
</div>
</div> -->
</div>
</div>
<div class="content-block">
<div class="row">
<div class="col-50">
<a href="/o2o/shopadmin/shopmanagement"
class="button button-big button-fill button-danger">返回</a>
</div>
<div class="col-50">
<a href="/o2o/shopadmin/productoperation" class="button button-big button-fill button-success" id="new">新增</a>
</div>
</div>
</div>
</div>
<script type='text/javascript' src='//g.alicdn.com/sj/lib/zepto/zepto.min.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/sm.min.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/sm-extend.min.js' charset='utf-8'></script>
<script type='text/javascript' src='../resources/js/common/common.js'
charset='utf-8'></script>
<script type='text/javascript' src='../resources/js/shop/productmanagement.js' charset='utf-8'></script>
</body>
</html>
productmanagement.js:
$(function() {
//获取此店铺下的商品列表的URL
var listUrl = '/o2o/shopadmin/getproductlistbyshop?pageIndex=1&pageSize=999';
//商品下架的URL
var statusUrl = '/o2o/shopadmin/modifyproduct';
getList();
/**
* 获取此店铺下的商品列表
* @returns
*/
function getList() {
//从后台获取此店铺的商品列表
$.getJSON(listUrl, function(data) {
if (data.success) {
var productList = data.productList;
var tempHtml = '';
//遍历每条商品信息,拼接成一行显示,列信息包括
//商品名称,优先级,上架\下架(含productId),编辑按钮(含productId)
//预览(含productId)
productList.map(function(item, index) {
var textOp = "下架";
var contraryStatus = 0;
if (item.enableStatus == 0) {
//若状态值为0,表明是已上架的商品,操作变为上架(即点击上架按钮商家相关商品)
textOp = "上架";
contraryStatus = 1;
} else {
contraryStatus = 0;
}
//拼接每件商品的行信息
tempHtml += '' + '<div class="row row-product">'
+ '<div class="col-33">'
+ item.productName
+ '</div>'
+ '<div class="col-20">'
+ item.priority
+ '</div>'
+ '<div class="col-40">'
+ '<a href="#" class="edit" data-id="'
+ item.productId
+ '" data-status="'
+ item.enableStatus
+ '">编辑</a>'
+ '<a href="#" class="status" data-id="'
+ item.productId
+ '" data-status="'
+ contraryStatus
+ '">'
+ textOp
+ '</a>'
+ '<a href="#" class="preview" data-id="'
+ item.productId
+ '" data-status="'
+ item.enableStatus
+ '">预览</a>'
+ '</div>'
+ '</div>';
});
//将拼接好的信息赋值进html控件中
$('.product-wrap').html(tempHtml);
}
});
}
//将class为product-wrap里面的a标签绑定上点击的事件
$('.product-wrap')
.on(
'click',
'a',
function(e) {
var target = $(e.currentTarget);
if (target.hasClass('edit')) {
//如果有class edit则点击就进入店铺信息编辑页面,并带有productId参数
window.location.href = '/o2o/shopadmin/productoperation?productId='
+ e.currentTarget.dataset.id;
} else if (target.hasClass('status')) {
//如果有class status则调用后台功能上/下架相关商品,并带有productId参数
changeItemStatus(e.currentTarget.dataset.id,
e.currentTarget.dataset.status);
} else if (target.hasClass('preview')) {
window.location.href = '/o2o/frontend/productdetail?productId='
+ e.currentTarget.dataset.id;
}
});
function changeItemStatus(id, enableStatus) {
//定义product json对象并添加productId以及状态(上架/下架)
var product = {};
product.productId = id;
product.enableStatus = enableStatus;
$.confirm('确定嘛?', function() {
//上下架相关商品
$.ajax({
url : statusUrl,
type : 'POST',
data : {
productStr : JSON.stringify(product),
statusChange : true
},
dataType : 'json',
success : function(data) {
if (data.success) {
$.toast('操作成功!');
getList();
} else {
$.toast('操作失败!');
}
}
});
});
}
// $('#new').click(function() {
// window.location.href = '/o2o/shopadmin/productedit';
// });
});
productmanagement.css:
.row-product {
border: 1px solid #999;
padding: .5rem;
border-bottom: none;
}
.row-product:last-child {
border-bottom: 1px solid #999;
}
.product-name {
white-space: nowrap;
overflow-x: scroll;
}
.product-wrap a {
margin-right: 1rem;
}
8-10 商品列表展示之后端开发
Dao层:
/**
* 查询商品列表并分页,可输入的条件有:商品名(模糊),商品状态,店铺Id,商品类别
* @param productCondition
* @param rowIndex
* @param pageSize
* @return
*/
List<Product> queryProductList(@Param("productCondition") Product productCondition
,@Param("rowIndex") int rowIndex,@Param("pageSize") int pageSize);
/**
* 查询对应的商品总数
* @param productCondition
* @return
*/
int queryProductCount(@Param("productCondition") Product productCondition);
<resultMap id="productMap" type="com.doit.o2o.entity.Product">
<id column="product_id" property="productId"/>
<result column="product_name" property="productName" />
<result column="product_desc" property="productDesc" />
<result column="img_addr" property="imgAddr" />
<result column="normal_price" property="normalPrice" />
<result column="promotion_price" property="promotionPrice" />
<result column="priority" property="priority" />
<result column="create_time" property="createTime" />
<result column="last_edit_time" property="lastEditTime" />
<result column="enable_status" property="enableStatus" />
<association property="productCategory" column="product_category_id"
javaType="com.doit.o2o.entity.ProductCategory">
<id column="product_category_id" property="productCategoryId" />
<result column="product_category_name" property="productCategoryName" />
</association>
<association property="shop" column="shop_id"
javaType="com.doit.o2o.entity.Shop">
<id column="shop_id" property="shopId" />
<result column="owner_id" property="ownerId" />
<result column="shop_name" property="shopName" />
</association>
<collection property="productImgList" column="product_id"
ofType="com.doit.o2o.entity.ProductImg">
<id column="product_img_id" property="productImgId" />
<result column="detail_img_addr" property="imgAddr" />
<result column="img_desc" property="imgDesc" />
<result column="priority" property="priority" />
<result column="create_time" property="createTime" />
<result column="product_id" property="productId" />
</collection>
</resultMap>
<select id="queryProductList" resultType="com.doit.o2o.entity.Product">
SELECT
product_id,
product_name,
product_desc,
img_addr,
normal_price,
promotion_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 '% %' 在mybatis里面写就是应该是 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 '% %' 在mybatis里面写就是应该是 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>
service层:
/**
* 查询商品列表分页,可输入的条件有:商品名(模糊),商品状态,店铺Id,商品类别
* @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层:
@RequestMapping(value="/getproductlistbyshop",method=RequestMethod.GET)
@ResponseBody
private Map<String,Object> getProductListByShop(HttpServletRequest request){
Map<String,Object> modelMap = new HashMap<String,Object>();
//获取前台传过来的页码
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)&&(currentShop.getShopId()!=null)){
//获取传入的需要检索的条件,包括是否需要从某个商品类别以及模糊查找商品名去筛选某个店铺下的商品列表
//筛选的条件可以进行排列组合
long productCategoryId = HttpServletRequestUtil.getLong(request, "productCategoryId");
String productName = HttpServletRequestUtil.getString(request, "productName");
Product productCondition = compactProductCondition(currentShop.getShopId(), productCategoryId, productName);
//传入查询条件以及分页信息进行查询,返回相应商品列表以及总数
ProductExecution pe = productService.getProductList(productCondition, pageIndex, pageSize);
modelMap.put("success", true);
modelMap.put("productList", pe.getProductList());
modelMap.put("count", pe.getCount());
}else{
modelMap.put("success", false);
modelMap.put("errMsg", "empty pageSize or pageIndex or shopId");
}
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!=-1){
ProductCategory pc = new ProductCategory();
pc.setProductCategoryId(productCategoryId);
productCondition.setProductCategory(pc);
}
//若有商品名模糊查询的要求则添加进去
if(productName!=null){
productCondition.setProductName(productName);
}
return productCondition;
}
8-11 商品列表展示之前端开发
见前几章前端代码
8-12 解除商品与某商品类别的关联的实现
Dao层:
/**
* 删除商品类别之前,将商品类别ID置为空
* @param productCategoryId
* @return
*/
int updateProductCategoryToNull(long productCategoryId);
<update id="updateProductCategoryToNull" parameterType="Long"
keyProperty="product_id" useGeneratedKeys="true">
UPDATE tb_product
SET
product_category_id = null
WHERE product_category_id =
#{productCategoryId}
</update>
Service层:修改ProductCategoryServiceImpl的deleteProductCategory方法:
@Override
@Transactional
public ProductCategoryExecution deleteProductCategory(long productCategoryId, long shopId)
throws ProductCategoryOperationException{
try{
int affectedNum = productDao.updateProductCategoryToNull(productCategoryId);
if(affectedNum<0){
throw new RuntimeException("商品类别更新失败");
}
}catch(Exception e){
throw new RuntimeException("deleteProductCategory error:"+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());
}
}