商品类别:
首先往数据库的商品类别表添加数据,以便进行测试。
实体类
然后编写dao层
mapper实现dao
junit测试dao
编写service
编写controller层
我们可以用modelMap,StringObject来表示方法的返回值,但是由于我们这里只返回一个对象,所以我们换一种知识,使用Result把json封装起来去返回,在dto里编写result类,跟shopExecution差不多,只不过它是泛型。
package com.imooc.o2o.dto;
/**
* 封装json对象,所有返回结果都使用它
*
*/
public class Result<T> {
private boolean success;// 是否成功标志
private T data;// 成功时返回的数据
private String errorMsg;// 错误信息
private int errorCode;// 错误码
public Result() {
}
// 成功时的构造器
public Result(boolean success, T data) {
this.success = success;
this.data = data;
}
// 错误时的构造器
public Result(boolean success, String errorMsg, int errorCode) {
this.success = success;
this.errorMsg = errorMsg;
this.errorCode = errorCode;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
}
再回去编写controller
在这之前得编写枚举类
package com.imooc.o2o.enums;
public enum ProductCategoryStateEnum {
OFFLINE(-1, "非法商品类别"), SUCCESS(1, "操作成功"), PASS(2, "通过认证"), INNER_ERROR(-1001,
"内部系统错误"), NULL_SHOPID(-1002, "ShopId为空"), NULL_PRODUCTCATEGORY(-1003, "productcategory信息为空");
private int state;
private String stateInfo;
// 枚举被设计为单例模式
private ProductCategoryStateEnum(int state, String stateInfo) {
this.state = state;
this.stateInfo = stateInfo;
}
/**
* 根据state返回相应的enum
*
* @param state
* @return
*/
public ProductCategoryStateEnum stateOf(int state) {
for (ProductCategoryStateEnum stateEnum : values()) {
if (stateEnum.state == state)
return stateEnum;
}
return null;
}
public int getState() {
return state;
}
public String getStateInfo() {
return stateInfo;
}
}
然后debug启动tomcat,浏览器输入
http://localhost:8080/o2o/shopadmin/getproductcategorylist
会发现类别并没有按priority从高到低排列,因为我们sql语句没有orderby
等一会刷新页面就行了。
商品类别列表前端
商品类别列表的controller方法里的模拟登录可以去掉,因为我们商品类别管理是从商品管理页面跳转过来,session中已经存在shopId了。
批量添加商品类别
批量所以参数是List
dao层
/**
* 批量添加商品类别
* @param productCategoryList
* @return
*/
int batchInsertProductCategory(List<ProductCategory> productCategoryList);
实现
参数是List类型,mybatis的循环跟java语法差不多,其中list和productCategory可以自己取名字,就是一productCategory变量名去遍历list,index是计数器,我也不清楚这个是什么意思,index++呢?我把它去掉,ut还是测试成功的,然后sql语句values后是用逗号连接的,例如Value(xxx,xxx),(xxxx,xxx),所以separator是逗号。
<insert id="batchInsertProductCategory" parameterType="java.util.List">
INSERT INTO
tb_product_category(product_category_name, priority,
create_time, shop_id)
VALUES
<foreach collection="list" item="productCategory" index="index"
separator=",">
(
#{productCategory.productCategoryName},
#{productCategory.priority},
#{productCategory.createTime},
#{productCategory.shopId}
)
</foreach>
</insert>
service层
在dto里实现返回类型
public class ProductCategoryExecution {
// 结果状态
private int state;
// 结果标识
private String stateInfo;
private List<ProductCategory> productCategoryList;
public ProductCategoryExecution() {
}
//操作成功时使用的构造函数
public ProductCategoryExecution(ProductCategoryStateEnum stateEnum, List<ProductCategory> productCategoryList) {
this.state = stateEnum.getState();
this.stateInfo = stateEnum.getStateInfo();
this.productCategoryList = productCategoryList;
}
//操作失败时使用的构造函数
public ProductCategoryExecution(ProductCategoryStateEnum stateEnum) {
this.state = stateEnum.getState();
this.stateInfo = stateEnum.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;
}
}
在定义exception
这里要注意序列号的与前面的exception不同,选择生成序列号而不是默认序列号。
最后实现,前面dao测试,这里不测试
@Override
public ProductCategoryExecution batchAddProductCategory(List<ProductCategory> productCategoryList)
throws ProductCategoryOperationException {
if (productCategoryList != null && productCategoryList.size() > 0) {
try {
int effectedNum = productCategoryDao.batchInsertProductCategory(productCategoryList);
if (effectedNum <= 0) {
throw new ProductCategoryOperationException("店铺类别创建失败");
} else {
return new ProductCategoryExecution(ProductCategoryStateEnum.SUCCESS);
}
} catch (Exception e) {
throw new ProductCategoryOperationException("batchAddProductCategory error:" + e.getMessage());
}
} else {
return new ProductCategoryExecution(ProductCategoryStateEnum.EMPTY_LIST);
}
}
controller层
添加商品类别前端开发
首先看下效果图,点击添加就会自动生成一行可以编辑的
所以我们需要在js代码里编写一个方法,首先给新增按钮一个id,当点击新增按钮就调用方法,自动生成一行,
$('#new')
.click(
function() {
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>';
$('.category-wrap').append(tempHtml);
});
可以看到我们新生成的行的class最后是temp,而之前的都是now,因为当我们点击提交的时候,只需要获取新添加的,所以就是通过class来区分
之后就是提交按钮了,获取class为temp的行,然后map遍历添加到与后端规定好的productCategoryList中
最后再使用ajax提交到后台,其中addUrl是上面写好的url,然后使用json.stringify把数据变成字符串提交到后台
开始测试,首先debug启动tomcat,在controller里设置断点,从shoplist进去这样才能获取currentShop,新增两个,然后点击提交之前先f12,在提交方法设置断点
之后点击提交,f10调试下一行,确认有值之后按f8直接调到后台debug,下一步之后,就可以看到前台已经添加进来了
这里需要注意一下,为什么提交创建的商品类别之后,它的顺序就能立马更新过来呢?原因是我们在ajax里重新调用了getList方法。
商品类别的删除功能
dao层,因为传入两个参数,mybatis不认识所以我们需要param注解
/**
* 删除指定商品类别
* @param productCategoryId
* @param shopId
* @return
*/
int deleteProductCategory(@Param("productCategoryId") long productCategoryId, @Param("shopId") long shopId);
dao实现,这里使用注解,所以不用设置参数类型,我们不仅要判断商品类别id是否相同还要判断商店id是否相同,为了更安全的控制,就是你的店铺信息得对才能删除,为了预防删除了不是本店铺的商品类别id,就不好了。
<delete id="deleteProductCategory">
DELETE FROM
tb_product_category
WHERE
product_category_id = #{productCategoryId}
AND shop_id = #{shopId}
</delete>
ut测试
这里我们的effected是在循环里面的,所以每次都只是一条。所以ass是1而不是遍历的数量。
这里注意,我们可以使用ignore注解去把上面的插入和查询都忽略掉,因为test测试是随机测试的,但是当我们想三个同时测试,因为我们测试了删除,想再重新测试添加就得去删掉ignore,比较麻烦,有什么三个方法按顺序测试吗?就是插入,查询,然后再删除,构成测试循环,我们可以这样做,并且在测试方法名字使用ABC来安排测试顺序
可以看到无论你测多少次调用顺序都是一样的,并且这样数据库没有任何影响,形成回环。
service
如果该商品类别下有商品,需要先把商品的商品类别id设置为空
/**
* 将此类别下的商品里的类别id设置为空,再删除该商品类别
* @param productCategoryId
* @param shopId
* @return
* @throws ProductCategoryOperationException
*/
ProductCategoryExecution deleteProductCategory(long productCategoryId, long shopId)
throws ProductCategoryOperationException;
实现,因为我们这里是有两步要操作的,所以需要事务@Transactional,第一步就是把商品的商品类别id设为空,第二步是删除商品类别,两步都成功才执行,任何一步出错都回滚。
controller
这里参数Long productategoryId如果写成long下面!=null是会报错的,原因是Long是long的包装类,Long是对象才能判断null,而long是0做判断
前端
删除我们分两种,一种是数据库里的,一种是海没存入数据库里的,那么当点击删除按钮的时候怎么区分是调用那个方法呢,答案还是通过class,结果按钮class和列的class就行了
所以我们的js代码需要写两个删除方法
接着测试跟前面一样。这里我们删除数据库里的数据会发现没删掉,报错,原因是我们的js代码里多了个shopId:shopId,但是我们的controll方法参数只有productCategoryId而没有shopId。可以在方法加这个参数,或者在js里去掉,但是后面肯定是要加上的,安全性。