前言
商品分类模块给它设计的接口暂时不多,这一章节也比较简单,除了一个递归获取所有子节点的接口外~
接下来就一个一个文件的放出来,大家直接参考和看代码中的注释就好了。
controller设计
这里在controller/backend包下创建CategoryManageController.java,专门用来控制对商品分类的操作。
功能大部分还是在service中实现的,这里主要是把session中的相关信息拿出来,并做一些简单的判断,如是否登录,是否是管理员等。
注意使用了一个新的注解@RequestParam(value = “parentId”,defaultValue = “0”)可以实现不传参数情况下的默认参数(感觉这些注解实现了很强大的功能,有时间要好好了解一下具体的实现原理)。
package top.winxblast.happymall.controller.backend;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import top.winxblast.happymall.common.Const;
import top.winxblast.happymall.common.ResponseCode;
import top.winxblast.happymall.common.ServerResponse;
import top.winxblast.happymall.pojo.User;
import top.winxblast.happymall.service.CategoryService;
import top.winxblast.happymall.service.UserService;
import javax.servlet.http.HttpSession;
/**
* 目录管理模块
*
* @author winxblast
* @create 2017/10/28
**/
@Controller
@RequestMapping(value = "/manage/category")
public class CategoryManageController {
@Autowired
private UserService userService;
@Autowired
private CategoryService categoryService;
/**
* 添加商品类别
* 添加这个注解@RequestParam(value = "parentId",defaultValue = "0")主要是为了没有传这个参数时有个默认值。
* @param session 通过session验证用户是否已登录以及是否为管理员
* @param categoryName
* @param parentId
* @return
*/
@RequestMapping(value = "add_category.do", method = RequestMethod.GET)
@ResponseBody
public ServerResponse<String> addCategory(HttpSession session, String categoryName, @RequestParam(value = "parentId",defaultValue = "0") int parentId) {
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null) {
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(), "用户未登录,请登录");
}
//校验是否为管理员,功能放到service中
if(userService.checkAdminRole(user).isSuccess()) {
//是管理员
//增加处理分类的逻辑
return categoryService.addCategory(categoryName, parentId);
} else {
return ServerResponse.createByErrorMessage("无权限,需要管理员权限");
}
}
/**
* 修改品类名称
* @param session
* @param categoryId
* @param categoryName
* @return
*/
@RequestMapping(value = "set_category_name.do", method = RequestMethod.GET)
@ResponseBody
public ServerResponse<String> setCategoryName(HttpSession session, Integer categoryId, String categoryName) {
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null) {
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(), "用户未登录,请登录");
}
//校验是否为管理员,功能放到service中
if(userService.checkAdminRole(user).isSuccess()) {
//是管理员
//更新categoryName
return categoryService.updateCategoryName(categoryId, categoryName);
}else {
return ServerResponse.createByErrorMessage("无权限,需要管理员权限");
}
}
/**
* 通过父类id获取下一级子分类
* @param session
* @param categoryId 不传参数默认为0
* @return
*/
@RequestMapping(value = "get_category.do", method = RequestMethod.GET)
@ResponseBody
public ServerResponse getChildrenParallelCategory(HttpSession session, @RequestParam(value = "categoryId", defaultValue = "0")Integer categoryId) {
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null) {
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(), "用户未登录,请登录");
}
//校验管理员
if(userService.checkAdminRole(user).isSuccess()) {
//查询子节点的category信息,并且不递归,只查询下一级
return categoryService.getChildrenParallelCategory(categoryId);
} else {
return ServerResponse.createByErrorMessage("无权限,需要管理员权限");
}
}
/**
* 通过递归查询该分类下所有子分类
* 这个方法的名字取的我也是醉了···最讨厌取名字了,这里老师取名感觉也是随意
* @param session
* @param categoryId 不传参数默认为0
* @return
*/
@RequestMapping(value = "get_deep_category.do", method = RequestMethod.GET)
@ResponseBody
public ServerResponse getCategoryAndDeepChildrenCategory(HttpSession session, @RequestParam(value = "categoryId", defaultValue = "0")Integer categoryId) {
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null) {
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(), "用户未登录,请登录");
}
//校验管理员
if(userService.checkAdminRole(user).isSuccess()) {
//查询当前节点及递归子节点的id
return categoryService.selectCategoryAndChildrenById(categoryId);
} else {
return ServerResponse.createByErrorMessage("无权限,需要管理员权限");
}
}
}
service设计
先给出接口的定义把CategoryService。后来我按照阿里巴巴Java规约的一些习惯把接口命名前面的大写“I”去掉了,直接使用xxService,然后在实现类后面加上Impl。
package top.winxblast.happymall.service;
import top.winxblast.happymall.common.ServerResponse;
import top.winxblast.happymall.pojo.Category;
import java.util.List;
/**
* 分类操作模块接口
*
* @author winxblast
* @create 2017/10/28
**/
public interface CategoryService {
ServerResponse<String> addCategory(String categoryName, Integer parentId);
ServerResponse<String> updateCategoryName(Integer categoryId, String categoryName);
ServerResponse<List<Category>> getChildrenParallelCategory(Integer categoryId);
ServerResponse selectCategoryAndChildrenById(Integer categoryId);
}
然后是接口的实现类。这里混用了google的guava和Apache的commons,网上讨论也比较多,暂时还没有精力去分辨那个好,这里就先按老师写的混用着。
至于以后工作中那就看同事用什么就用什么啦
package top.winxblast.happymall.service.impl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.winxblast.happymall.common.ServerResponse;
import top.winxblast.happymall.dao.CategoryMapper;
import top.winxblast.happymall.pojo.Category;
import top.winxblast.happymall.service.CategoryService;
import java.util.List;
import java.util.Set;
/**
* 分类模块接口实现类
*
* @author winxblast
* @create 2017/10/28
**/
@Service("categoryService")
public class CategoryServiceImpl implements CategoryService {
private Logger logger = LoggerFactory.getLogger(CategoryServiceImpl.class);
@Autowired
private CategoryMapper categoryMapper;
/**
* 添加品类
* @param categoryName 品类名称
* @param parentId 父品类id,SpringMVC中默认使用0,如果没有传入
* @return
*/
@Override
public ServerResponse<String> addCategory(String categoryName, Integer parentId) {
if(parentId == null || StringUtils.isBlank(categoryName)) {
return ServerResponse.createByErrorMessage("添加品类参数错误");
}
Category category = new Category();
category.setName(categoryName);
category.setParentId(parentId);
category.setStatus(true);//表示这个分类是可用的,有效的
int rowCount = categoryMapper.insert(category);
if(rowCount > 0) {
return ServerResponse.createBySuccessMessage("添加品类成功");
}
return ServerResponse.createByErrorMessage("添加品类失败");
}
/**
* 修改品类名字
* @param categoryId
* @param categoryName
* @return
*/
@Override
public ServerResponse<String> updateCategoryName(Integer categoryId, String categoryName) {
if(categoryId == null || StringUtils.isBlank(categoryName)) {
return ServerResponse.createByErrorMessage("更新品类参数错误");
}
Category category = new Category();
category.setId(categoryId);
category.setName(categoryName);
int rowCount = categoryMapper.updateByPrimaryKeySelective(category);
if(rowCount > 0) {
return ServerResponse.createBySuccessMessage("更改品类名字成功");
}
return ServerResponse.createByErrorMessage("更改品类名字失败");
}
/**
* 仅获取下一级分类
* @param categoryId
* @return
*/
@Override
public ServerResponse<List<Category>> getChildrenParallelCategory(Integer categoryId) {
List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
if(CollectionUtils.isEmpty(categoryList)) {
//这里没有子分类为什么打印日志比较好呢,应该可以有其他处理方法,这里按照老师的讲解来
logger.info("未找到当前分类的子分类");
}
return ServerResponse.createBySuccess(categoryList);
}
/**
* 递归查询本节点id和孩子节点id
* 和下面一个private方法联合使用
* @param categoryId
* @return
*/
@Override
public ServerResponse selectCategoryAndChildrenById(Integer categoryId) {
//这个跟我平时不一样,用的谷歌的一个包来初始化,里面也有很多方便的工具
Set<Category> categorySet = Sets.newHashSet();
findChildCatgory(categorySet,categoryId);
//同样是谷歌guava里的方法
List<Integer> categoryIdList = Lists.newArrayList();
if(categoryId != null) {
for (Category categoryItem : categorySet) {
categoryIdList.add(categoryItem.getId());
}
}
return ServerResponse.createBySuccess(categoryIdList);
}
/**
* 这里要重写Category的hashcode和equals方法
* 递归算法算出子节点
*/
private Set<Category> findChildCatgory(Set<Category> categorySet, Integer categoryId) {
Category category = categoryMapper.selectByPrimaryKey(categoryId);
if(category != null) {
categorySet.add(category);
}
//查找子节点,递归算法一定要有停止条件
List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
//由于mybatis的设计,如果没有查询结果,也不会返回null,所以下面不用进行null判断
for (Category categoryItem : categoryList) {
findChildCatgory(categorySet, categoryItem.getId());
}
return categorySet;
}
}
注意这里重写了category的hashcode和equals方法,因为要把按我们想要的方式放进hashset中。这两个方法的重写直接使用idea提供的方法就可以了。
dao层
对于数据库的操作,在mybatis generator的基础上,自己就添加了一个方法,以下为相关接口及xml配置内容,我自己添加的内容在两个文件的最后部分。
CategoryMapper.java
package top.winxblast.happymall.dao;
import top.winxblast.happymall.pojo.Category;
import java.util.List;
public interface CategoryMapper {
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table happymall_category
*
* @mbggenerated Sun Oct 08 14:03:47 CST 2017
*/
int deleteByPrimaryKey(Integer id);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table happymall_category
*
* @mbggenerated Sun Oct 08 14:03:47 CST 2017
*/
int insert(Category record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table happymall_category
*
* @mbggenerated Sun Oct 08 14:03:47 CST 2017
*/
int insertSelective(Category record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table happymall_category
*
* @mbggenerated Sun Oct 08 14:03:47 CST 2017
*/
Category selectByPrimaryKey(Integer id);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table happymall_category
*
* @mbggenerated Sun Oct 08 14:03:47 CST 2017
*/
int updateByPrimaryKeySelective(Category record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table happymall_category
*
* @mbggenerated Sun Oct 08 14:03:47 CST 2017
*/
int updateByPrimaryKey(Category record);
/**
* 通过父类Id查看下一级子分类的情况
* @param parentId
* @return
*/
List<Category> selectCategoryChildrenByParentId(Integer parentId);
}
CategoryMapper.xml大部分都是自动生成的,自己写的部分也没有特别大的难度。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="top.winxblast.happymall.dao.CategoryMapper" >
<resultMap id="BaseResultMap" type="top.winxblast.happymall.pojo.Category" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Oct 08 14:03:47 CST 2017.
-->
<constructor >
<idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="parent_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="name" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="status" jdbcType="BIT" javaType="java.lang.Boolean" />
<arg column="sort_order" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="create_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
<arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
</constructor>
</resultMap>
<sql id="Base_Column_List" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Oct 08 14:03:47 CST 2017.
-->
id, parent_id, name, status, sort_order, create_time, update_time
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Oct 08 14:03:47 CST 2017.
-->
select
<include refid="Base_Column_List" />
from happymall_category
where id = #{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Oct 08 14:03:47 CST 2017.
-->
delete from happymall_category
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="top.winxblast.happymall.pojo.Category" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Oct 08 14:03:47 CST 2017.
-->
insert into happymall_category (id, parent_id, name,
status, sort_order, create_time,
update_time)
values (#{id,jdbcType=INTEGER}, #{parentId,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
#{status,jdbcType=BIT}, #{sortOrder,jdbcType=INTEGER}, now(),
now())
</insert>
<insert id="insertSelective" parameterType="top.winxblast.happymall.pojo.Category" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Oct 08 14:03:47 CST 2017.
-->
insert into happymall_category
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="id != null" >
id,
</if>
<if test="parentId != null" >
parent_id,
</if>
<if test="name != null" >
name,
</if>
<if test="status != null" >
status,
</if>
<if test="sortOrder != null" >
sort_order,
</if>
<if test="createTime != null" >
create_time,
</if>
<if test="updateTime != null" >
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="id != null" >
#{id,jdbcType=INTEGER},
</if>
<if test="parentId != null" >
#{parentId,jdbcType=INTEGER},
</if>
<if test="name != null" >
#{name,jdbcType=VARCHAR},
</if>
<if test="status != null" >
#{status,jdbcType=BIT},
</if>
<if test="sortOrder != null" >
#{sortOrder,jdbcType=INTEGER},
</if>
<if test="createTime != null" >
now(),
</if>
<if test="updateTime != null" >
now(),
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="top.winxblast.happymall.pojo.Category" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Oct 08 14:03:47 CST 2017.
-->
update happymall_category
<set >
<if test="parentId != null" >
parent_id = #{parentId,jdbcType=INTEGER},
</if>
<if test="name != null" >
name = #{name,jdbcType=VARCHAR},
</if>
<if test="status != null" >
status = #{status,jdbcType=BIT},
</if>
<if test="sortOrder != null" >
sort_order = #{sortOrder,jdbcType=INTEGER},
</if>
<if test="createTime != null" >
create_time = #{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateTime != null" >
now(),
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="top.winxblast.happymall.pojo.Category" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Oct 08 14:03:47 CST 2017.
-->
update happymall_category
set parent_id = #{parentId,jdbcType=INTEGER},
name = #{name,jdbcType=VARCHAR},
status = #{status,jdbcType=BIT},
sort_order = #{sortOrder,jdbcType=INTEGER},
create_time = #{createTime,jdbcType=TIMESTAMP},
update_time = now()
where id = #{id,jdbcType=INTEGER}
</update>
<select id="selectCategoryChildrenByParentId" resultMap="BaseResultMap" parameterType="int">
SELECT
<include refid="Base_Column_List"/>
FROM happymall_category
WHERE parent_id = #{parentId}
</select>
</mapper>
接口测试
还是继续使用我们的chrome插件restlet client,这里我就上几张图给大家参考一下就好了,还是比较容易使用的,不过测试要尽可能覆盖一些特殊情况,虽然我们不是专业的测试,但是也要考虑的周到一些。
这次的4个接口都是使用GET方法就可以了,带默认参数的可以试试不传入参数。
get_category.do接口
add_category.do
set_category_name.do
get_deep_category.do
同时由于是使用GET方法提交参数,我们也不需要写HTML表单,可以直接在浏览器中输入相应的地址来查看返回值,这里json格式化的效果是由插件FE助手完成的,大家要是使用火狐浏览器的话可以浏览器直接支持格式化json的。