上一篇: 微服务(四)—— 用户模块(backend-user).
一、创建项目
首先创建一个SpringBoot项目,具体创建过程和 微服务(三)—— 用户模块(backend-user).一样。
二、项目结构
1.目录结构
项目结构和backend-user模块类似,由于产品需要上传图片文件,所以多了一个上传文件的UpLoadController,和前端的html,SpringBoot的前端页面需要放在resources/templates目录下,然后还有一个需要给前端封装数据的VO类
下面说一下具体的实现类,先看一下产品分类的CategoryManageController类
package com.aiun.product.controller;
/**
* 产品分类
*
* @author lenovo
*/
@Api(tags = "产品分类相关接口")
@RestController
@RequestMapping("/category/manage/")
public class CategoryManageController {
@Autowired
private ICategoryService iCategoryService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 增加品类
*
* @param request 请求
* @param categoryName 品类名称
* @param parentId 父 id
* @return 返回结果
*/
@GetMapping("add_category")
@ApiOperation(value = "增加品类")
public ServerResponse addCategory(HttpServletRequest request, String categoryName, @RequestParam(value = "parentId", defaultValue = "0") int parentId) {
ServerResponse hasLogin = loginHasExpired(request);
System.out.println("request : " + request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
System.out.println("user : " + user);
// 检查是否是管理员
if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
System.out.println("categoryName : " + categoryName);
// 是管理员,处理分类的逻辑
return iCategoryService.addCategory(categoryName, parentId);
} else {
return ServerResponse.createByErrorMessage("无权限操作,需要管理员权限");
}
}
return hasLogin;
}
/**
* 设置品类名称
*
* @param request 请求
* @param categoryId 品类 id
* @param categoryName 品类名
* @return 返回结果
*/
@PostMapping("set_category_name")
@ApiOperation(value = "设置品类名称")
public ServerResponse setCategoryName(HttpServletRequest request, Integer categoryId, String categoryName) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
//检查是否是管理员
if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
//是管理员,处理分类的逻辑
return iCategoryService.updateCategoryName(categoryId, categoryName);
} else {
return ServerResponse.createByErrorMessage("无权限操作,需要管理员权限");
}
}
return hasLogin;
}
/**
* 获取节点的并且是平级的品类
*
* @param request 请求
* @param categoryId 品类 id
* @return 返回结果
*/
@PostMapping("get_category")
@ApiOperation(value = "获取节点的并且是平级的品类")
@ResponseBody
public ServerResponse getChildrenParallelCategory(HttpServletRequest request, @RequestParam(value = "categoryId", defaultValue = "0") Integer categoryId) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
//检查是否是管理员
if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
//查询子节点的品种信息,并且不递归,保持平级
return iCategoryService.getChildrenParallelCategory(categoryId);
} else {
return ServerResponse.createByErrorMessage("无权限操作,需要管理员权限");
}
}
return hasLogin;
}
/**
* 获取节点并且递归获取子节点的品类
*
* @param request 请求
* @param categoryId 品类 id
* @return 返回结果
*/
@PostMapping("get_deep_category")
@ApiOperation(value = "获取节点并且递归获取子节点的品类")
@ResponseBody
public ServerResponse getCategoryAddDeepChildrenCategory(HttpServletRequest request, @RequestParam(value = "categoryId", defaultValue = "0") Integer categoryId) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
// 检查是否是管理员
if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
// 查询当前节点并且递归查询子节点
return iCategoryService.selectCategoryAndChildrenById(categoryId);
} else {
return ServerResponse.createByErrorMessage("无权限操作,需要管理员权限");
}
}
return hasLogin;
}
/**
* 判断用户登录是否过期
*
* @param request 请求
* @return 结果
*/
private ServerResponse<User> loginHasExpired(HttpServletRequest request) {
String key = request.getHeader(UserConst.AUTHORITY);
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
String value = valueOperations.get(key);
if (StringUtils.isEmpty(value)) {
return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
}
User user = JsonUtils.jsonStr2Object(value, User.class);
if (!key.equals(user.getUsername())) {
return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
}
valueOperations.set(key, value, 1, TimeUnit.HOURS);
return ServerResponse.createBySuccess(user);
}
}
它调用Service层的接口,下面看一下Service层的实现类CategoryServiceImpl
package com.aiun.product.service.impl;
/**
* @author lenovo
*/
@Service("iCategoryService")
public class CategoryServiceImpl implements ICategoryService {
private Logger logger = LoggerFactory.getLogger(CategoryServiceImpl.class);
@Autowired
private CategoryMapper categoryMapper;
@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 resultCount = categoryMapper.insert(category);
if (resultCount > 0) {
return ServerResponse.createBySuccessMessage("添加品类成功");
}
return ServerResponse.createByErrorMessage("添加品类失败");
}
@Override
public ServerResponse 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 resultCount = categoryMapper.updateByPrimaryKeySelective(category);
if (resultCount > 0) {
return ServerResponse.createBySuccessMessage("更新品类名字成功");
}
return ServerResponse.createByErrorMessage("更新品类名字失败");
}
@Override
public ServerResponse<List<Category>> getChildrenParallelCategory(Integer categoryId) {
List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
if (categoryList.isEmpty()) {
logger.info("未找到当前分类的子分类");
}
return ServerResponse.createBySuccess(categoryList);
}
/**
* 递归查询本节点的id及孩子节点的id
* @param categoryId
* @return
*/
@Override
public ServerResponse<List<Integer>> selectCategoryAndChildrenById(Integer categoryId) {
Set<Category> categorySet = new HashSet<>();
findChildCategory(categorySet, categoryId);
List<Integer> categoryIdList = new ArrayList<>();
if (categoryId != null) {
categorySet.forEach(e->categoryIdList.add(e.getId()));
}
return ServerResponse.createBySuccess(categoryIdList);
}
/**
* 递归找出子节点
* @return
*/
private Set<Category> findChildCategory(Set<Category> categorySet, Integer categoryId) {
Category category = categoryMapper.selectByPrimaryKey(categoryId);
if (category != null) {
categorySet.add(category);
}
//查找子节点,要有退出条件
List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
categoryList.forEach(e->findChildCategory(categorySet, e.getId()));
return categorySet;
}
}
Service层调用DAO层的Mapper接口CategoryMapper
package com.aiun.product.mapper;
/**
* @author lenovo
*/
@Mapper
@Component
public interface CategoryMapper {
/**
* 通过主键删除商品种类
* @param id 主键
* @return 操作影响行数
*/
int deleteByPrimaryKey(Integer id);
/**
* 插入商品种类
* @param record 商品种类类
* @return 操作影响行数
*/
int insert(@Param("record") Category record);
/**
* 有选择的插入
* @param record 商品种类类
* @return 操作影响行数
*/
int insertSelective(@Param("record") Category record);
/**
* 通过主键查询
* @param id 主键
* @return 分类实体类
*/
Category selectByPrimaryKey(Integer id);
/**
* 有选择的通过主键更新
* @param record 商品种类类
* @return 操作影响行数
*/
int updateByPrimaryKeySelective(@Param("record") Category record);
/**
* 通过主键更新
* @param record 商品种类类
* @return 操作影响行数
*/
int updateByPrimaryKey(@Param("record") Category record);
/**
* 通过父id查询商品子类
* @param parentId 父id
* @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="com.aiun.product.mapper.CategoryMapper" >
<resultMap id="BaseResultMap" type="com.aiun.product.pojo.Category" >
<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" >
id, parent_id, name, status, sort_order, create_time, update_time
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from trade_category
where id = #{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from trade_category
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.aiun.product.pojo.Category" >
insert into trade_category (id, parent_id, name,
status, sort_order, create_time,
update_time)
values (#{record.id,jdbcType=INTEGER}, #{record.parentId,jdbcType=INTEGER}, #{record.name,jdbcType=VARCHAR},
#{record.status,jdbcType=BIT}, #{record.sortOrder,jdbcType=INTEGER}, now(),
now())
</insert>
<insert id="insertSelective" parameterType="com.aiun.product.pojo.Category" >
insert into trade_category
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="record.id != null" >
id,
</if>
<if test="record.parentId != null" >
parent_id,
</if>
<if test="record.name != null" >
name,
</if>
<if test="record.status != null" >
status,
</if>
<if test="record.sortOrder != null" >
sort_order,
</if>
<if test="record.createTime != null" >
create_time,
</if>
<if test="record.updateTime != null" >
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="record.id != null" >
#{record.id,jdbcType=INTEGER},
</if>
<if test="record.parentId != null" >
#{record.parentId,jdbcType=INTEGER},
</if>
<if test="record.name != null" >
#{record.name,jdbcType=VARCHAR},
</if>
<if test="record.status != null" >
#{record.status,jdbcType=BIT},
</if>
<if test="record.sortOrder != null" >
#{record.sortOrder,jdbcType=INTEGER},
</if>
<if test="record.createTime != null" >
#{record.createTime,jdbcType=TIMESTAMP},
</if>
<if test="record.updateTime != null" >
now(),
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.aiun.product.pojo.Category" >
update trade_category
<set >
<if test="record.parentId != null" >
parent_id = #{record.parentId,jdbcType=INTEGER},
</if>
<if test="record.name != null" >
name = #{record.name,jdbcType=VARCHAR},
</if>
<if test="record.status != null" >
status = #{record.status,jdbcType=BIT},
</if>
<if test="record.sortOrder != null" >
sort_order = #{record.sortOrder,jdbcType=INTEGER},
</if>
<if test="record.createTime != null" >
create_time = #{record.createTime,jdbcType=TIMESTAMP},
</if>
<if test="record.updateTime != null" >
update_time = now(),
</if>
</set>
where id = #{record.id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.aiun.product.pojo.Category" >
update trade_category
set parent_id = #{record.parentId,jdbcType=INTEGER},
name = #{record.name,jdbcType=VARCHAR},
status = #{record.status,jdbcType=BIT},
sort_order = #{record.sortOrder,jdbcType=INTEGER},
create_time = #{record.createTime,jdbcType=TIMESTAMP},
update_time = now()
where id = #{record.id,jdbcType=INTEGER}
</update>
<select id="selectCategoryChildrenByParentId" resultMap="BaseResultMap" parameterType="int">
select
<include refid="Base_Column_List" />
from trade_category
where parent_id = #{parentId}
</select>
</mapper>
数据库的建表语句如下:
CREATE TABLE amall_category (
id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘类别Id’,
parent_id int(11) DEFAULT NULL COMMENT ‘父类别id当id=0时说明是根节点,一级类别’,
name varchar(50) DEFAULT NULL COMMENT ‘类别名称’,
status tinyint(1) DEFAULT ‘1’ COMMENT ‘类别状态1-正常,2-已废弃’,
sort_order int(4) DEFAULT NULL COMMENT ‘排序编号,同类展示顺序,数值相等则自然排序’,
create_time datetime DEFAULT NULL COMMENT ‘创建时间’,
update_time datetime DEFAULT NULL COMMENT ‘更新时间’,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=100032 DEFAULT CHARSET=utf8;
以上就是产品分类的具体实现,下面看看产品的实现,首先是controller层
package com.aiun.product.controller;
/**
* 产品控制层
* @author lenovo
*/
@Api(tags = "产品相关接口")
@RestController
@RequestMapping("/product/")
public class ProductController {
@Value("${image.localhost}")
private String mageHost;
@Autowired
private IProductService iProductService;
@Autowired
private IFileService iFileService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
/**
* 保存或更新产品
* @param product 产品信息
* @return 操作结果
*/
@PostMapping("manage/save")
@ApiOperation(value = "保存或更新产品")
public ServerResponse productSave(HttpServletRequest request, Product product) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
return iProductService.saveOrUpdateProduct(product);
} else {
return ServerResponse.createByErrorMessage("无权限操作");
}
}
return hasLogin;
}
/**
* 文件上传
* @param file 文件
* @param request 请求
* @return 返回给前端的信息
*/
@PostMapping("manage/upload")
@ApiOperation(value = "文件上传")
public ServerResponse upload(HttpServletRequest request, @RequestParam(value = "upload_file", required = false) MultipartFile file) {
ServerResponse hasLogin = loginHasExpired(request);
if (hasLogin.isSuccess()) {
User user = (User) hasLogin.getData();
if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
String targetFileName = iFileService.upload(file, mageHost);
String url = mageHost + targetFileName;
Map fileMap = Maps.newHashMap();
fileMap.put("uri", targetFileName);
fileMap.put("url", url);
return ServerResponse.createBySuccess(fileMap);
} else {
return ServerResponse.createByErrorMessage("无权限操作");
}
}
return hasLogin;
}
/**
* 获取产品详情
* @param productId 产品 id
* @return 返回的产品详情信息
*/
@PostMapping("detail")
@ApiOperation(value = "获取产品详情")
public ServerResponse<ProductDetailVO> detail(Integer productId) {
return iProductService.getProductDetail(productId);
}
/**
* 获取产品列表
* @param keyword 关键字
* @param categoryId 分类 id
* @param pageNum 当前页
* @param pageSize 页大小
* @param orderBy 排序规则
* @return 返回具体信息
*/
@PostMapping("list")
@ApiOperation(value = "获取产品列表")
public ServerResponse<PageInfo> list(@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "categoryId", required = false) Integer categoryId,
@RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") int pageSize,
@RequestParam(value = "orderBy", defaultValue = "") String orderBy) {
return iProductService.getProductBykeywordCategory(keyword, categoryId, pageNum, pageSize, orderBy);
}
/**
* 判断用户登录是否过期
* @param request 请求
* @return 结果
*/
private ServerResponse<User> loginHasExpired(HttpServletRequest request) {
String key = request.getHeader(UserConst.AUTHORITY);
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
String value = valueOperations.get(key);
if (StringUtils.isEmpty(value)) {
return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
}
User user = JsonUtils.jsonStr2Object(value, User.class);
if (!key.equals(user.getUsername())) {
return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
}
valueOperations.set(key, value, 1, TimeUnit.HOURS);
return ServerResponse.createBySuccess(user);
}
/**
* 对外提供的服务
* 根据ID查询Sku数据
* @param id 产品 id
* @return 产品信息类
*/
@GetMapping("{id}")
public Product findById(@PathVariable(name="id") Integer id){
return iProductService.findById(id);
}
/**
* 对外提供的服务
* 更新产品库存数量信息
* @param id 产品 id
* @param stock 产品库存
* @return 操作影响行数
*/
@GetMapping("update")
public void updateStockById(@RequestParam("id") Integer id, @RequestParam("stock") int stock){
iProductService.updateStockById(id, stock);
}
}
里面有两个对外提供的接口,是用于微服务下不同系统之间进行通信的接口。具体实现细节看:微服务(九)—— Feign的使用.
@Value("${image.localhost}")
private String mageHost;
这个语句是获取yml里面的配置信息,是产品图片的存储位置
下面看一下Service层的实现ProductServiceImpl
package com.aiun.product.service.impl;
/**
* @author lenovo
*/
@Service("iProductService")
public class ProductServiceImpl implements IProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private CategoryMapper categoryMapper;
@Value("mageHost")
private String mageHost;
@Autowired
private ICategoryService iCategoryService;
@Override
public ServerResponse saveOrUpdateProduct(Product product) {
if(product != null) {
//更新产品
if (product.getId() != null) {
int resultCount = productMapper.updateByPrimaryKey(product);
if (resultCount > 0) {
return ServerResponse.createBySuccessMessage("更新产品成功!");
}
return ServerResponse.createByErrorMessage("更新产品失败!");
}else{
int count = productMapper.insert(product);
if (count > 0) {
return ServerResponse.createBySuccessMessage("保存产品成功");
}
return ServerResponse.createByErrorMessage("更新产品失败!");
}
}
return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), "保持或更新产品参数不正确");
}
@Override
public ServerResponse<PageInfo> getProductList(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Product> productList = productMapper.selectList();
return getPageInfoServerResponse(productList);
}
@Override
public ServerResponse<PageInfo> searchProduct(String productName, Integer productId, int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
if (StringUtils.isNotBlank(productName)) {
productName = new StringBuilder().append("%").append(productName).append("%").toString();
}
//根据产品名进行模糊查询
List<Product> productList = productMapper.selectByNameAndProductId(productName, productId);
return getPageInfoServerResponse(productList);
}
@Override
public ServerResponse<ProductDetailVO> getProductDetail(Integer productId) {
if (productId == null) {
return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), "参数不正确");
}
Product product = productMapper.selectByPrimaryKey(productId);
if ((product == null) || (product.getStatus() != ProductStatusEnum.ON_SALE.getCode())) {
return ServerResponse.createByErrorMessage("产品已下架或者删除");
}
//VO对象 value object
ProductDetailVO productDetailVo = assembleProductDetailVo(product);
return ServerResponse.createBySuccess(productDetailVo);
}
@Override
public ServerResponse<PageInfo> getProductBykeywordCategory(String keyword, Integer categoryId, int pageNum, int pageSize, String orderBy) {
if (StringUtils.isBlank(keyword) && categoryId == null) {
return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
}
List<Integer> categoryIdList = new ArrayList<>();
if (categoryId != null) {
Category category = categoryMapper.selectByPrimaryKey(categoryId);
if (category == null && StringUtils.isBlank(keyword)) {
//没有该分类,并且还没有关键字,返回一个空的结果集
PageHelper.startPage(pageNum, pageSize);
List<ProductListVO> productListVoList = Lists.newArrayList();
PageInfo pageInfo = new PageInfo(productListVoList);
return ServerResponse.createBySuccess(pageInfo);
}
categoryIdList = iCategoryService.selectCategoryAndChildrenById(categoryId).getData();
}
if (StringUtils.isNotBlank(keyword)) {
keyword = new StringBuilder().append("%").append(keyword).append("%").toString();
}
PageHelper.startPage(pageNum, pageSize);
//排序处理
if (StringUtils.isNotBlank(orderBy)) {
PageHelper.orderBy(orderBy);
}
List<Product> productList = productMapper.selectByNameAndCategoryIds(StringUtils.isBlank(keyword) ? null : keyword, categoryIdList.size() == 0 ? null : categoryIdList);
return getPageInfoServerResponse(productList);
}
@Override
public Product findById(Integer id) {
return productMapper.selectByPrimaryKey(id);
}
@Override
public void updateStockById(Integer id, int stock) {
Product product = productMapper.selectByPrimaryKey(id);
product.setStock(stock);
productMapper.updateByPrimaryKeySelective(product);
}
/**
* 将产品列表进行分页操作
* @param productList
* @return
*/
private ServerResponse<PageInfo> getPageInfoServerResponse(List<Product> productList) {
List<ProductListVO> productListVoList = Lists.newArrayList();
productList.forEach(e->{
ProductListVO productListVo = assembleProductListVo(e);
productListVoList.add(productListVo);
});
PageInfo pageResult = new PageInfo(productList);
pageResult.setList(productListVoList);
return ServerResponse.createBySuccess(pageResult);
}
/**
* 根据产品信息给前端封装产品列表数据
* @param product
* @return
*/
private ProductListVO assembleProductListVo(Product product) {
ProductListVO productListVo = new ProductListVO();
productListVo.setId(product.getId())
.setCategoryId(product.getCategoryId())
.setName(product.getName())
.setMainImage(product.getMainImage())
.setPrice(product.getPrice())
.setSubtitle(product.getSubtitle())
.setImageHost(mageHost)
.setStatus(product.getStatus());
return productListVo;
}
/**
* productDetailVo对象的数据封装
* @param product
* @return
*/
private ProductDetailVO assembleProductDetailVo(Product product) {
ProductDetailVO productDetailVo = new ProductDetailVO();
productDetailVo.setId(product.getId())
.setPrice(product.getPrice())
.setSubtitle(product.getSubtitle())
.setMainImage(product.getMainImage())
.setSubImages(product.getSubImages())
.setCategoryId(product.getCategoryId())
.setDetail(product.getDetail())
.setName(product.getName())
.setStatus(product.getStatus())
.setStock(product.getStock());
productDetailVo.setImageHost(mageHost);
Category category = categoryMapper.selectByPrimaryKey(product.getCategoryId());
if (category == null) {
//默认是根节点
productDetailVo.setParentCategoryId(0);
} else {
productDetailVo.setParentCategoryId(category.getParentId());
}
productDetailVo.setCreateTime(DateUtils.dateToString(product.getCreateTime()));
productDetailVo.setUpdateTime(DateUtils.dateToString(product.getUpdateTime()));
return productDetailVo;
}
}
这里面涉及一个工具类DateUtils用于实现时间类型和字符串类型的相互转换,这个类定义在backend-common模块里,下面看一下DateUtils类
package com.aiun.common.util;
/**
* 日期和字符串转换的工具类
* @author lenovo
*/
public class DateUtils {
/**
* 设置为常量,提高可扩展性
*/
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static Date stringToDate(String dateTimeStr, String formatStr) {
org.joda.time.format.DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToString(Date date, String formatStr) {
if (date == null) {
return "";
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(formatStr);
}
public static Date stringToDate(String dateTimeStr) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToString(Date date) {
if (date == null) {
return "";
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT);
}
}
下面看一下DAO层的mapper接口
package com.aiun.product.mapper;
/**
* @author lenovo
*/
@Mapper
@Component
public interface ProductMapper {
/**
* 通过主键删除产品
* @param id 主键
* @return 影响行数
*/
int deleteByPrimaryKey(Integer id);
/**
* 插入产品数据
* @param record 产品类
* @return 影响行数
*/
int insert(@Param("record") Product record);
/**
* 有选择的插入产品数据
* @param record 产品类
* @return 影响行数
*/
int insertSelective(Product record);
/**
* 通过主键有选择的插入产品数据
* @param id 主键
* @return 产品类
*/
Product selectByPrimaryKey(Integer id);
/**
* 通过主键有选择的更新产品数据
* @param product 产品类
* @return 影响行数
*/
int updateByPrimaryKeySelective(@Param("record") Product product);
/**
* 通过主键更新产品信息
* @param product 产品类
* @return 影响行数
*/
int updateByPrimaryKey(@Param("record") Product product);
/**
* 获取产品列表
* @return 产品列表
*/
List<Product> selectList();
/**
* 通过产品名或id获取产品
* @param productName 产品名
* @param productId 产品 id
* @return 产品列表
*/
List<Product> selectByNameAndProductId(@Param("productName") String productName, @Param("productId") Integer productId);
/**
* 通过产品名或品种id获取产品
* @return 产品列表
*/
List<Product> selectByNameAndCategoryIds(@Param("productName") String productName, @Param("categoryIdList") List<Integer> categoryIdList);
}
与此对应的是ProductMapper.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="com.aiun.product.mapper.ProductMapper" >
<resultMap id="BaseResultMap" type="com.aiun.product.pojo.Product" >
<constructor >
<idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="category_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="name" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="subtitle" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="main_image" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="sub_images" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="detail" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="price" jdbcType="DECIMAL" javaType="java.math.BigDecimal" />
<arg column="stock" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="status" 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" >
id, category_id, name, subtitle, main_image, sub_images, detail, price, stock, status,
create_time, update_time
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from trade_product
where id = #{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from trade_product
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.aiun.product.pojo.Product" >
insert into trade_product (id, category_id, name,
subtitle, main_image, sub_images,
detail, price, stock,
status, create_time, update_time
)
values (#{record.id,jdbcType=INTEGER}, #{record.categoryId,jdbcType=INTEGER}, #{record.name,jdbcType=VARCHAR},
#{record.subtitle,jdbcType=VARCHAR}, #{record.mainImage,jdbcType=VARCHAR}, #{record.subImages,jdbcType=VARCHAR},
#{record.detail,jdbcType=VARCHAR}, #{record.price,jdbcType=DECIMAL}, #{record.stock,jdbcType=INTEGER},
#{record.status,jdbcType=INTEGER}, now(), now()
)
</insert>
<insert id="insertSelective" parameterType="com.aiun.product.pojo.Product" >
insert into trade_product
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="record.id != null" >
id,
</if>
<if test="record.categoryId != null" >
category_id,
</if>
<if test="record.name != null" >
name,
</if>
<if test="record.subtitle != null" >
subtitle,
</if>
<if test="record.mainImage != null" >
main_image,
</if>
<if test="record.subImages != null" >
sub_images,
</if>
<if test="record.detail != null" >
detail,
</if>
<if test="record.price != null" >
price,
</if>
<if test="record.stock != null" >
stock,
</if>
<if test="record.status != null" >
status,
</if>
<if test="record.createTime != null" >
create_time,
</if>
<if test="record.updateTime != null" >
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="record.id != null" >
#{record.id,jdbcType=INTEGER},
</if>
<if test="record.categoryId != null" >
#{record.categoryId,jdbcType=INTEGER},
</if>
<if test="record.name != null" >
#{record.name,jdbcType=VARCHAR},
</if>
<if test="record.subtitle != null" >
#{record.subtitle,jdbcType=VARCHAR},
</if>
<if test="record.mainImage != null" >
#{record.mainImage,jdbcType=VARCHAR},
</if>
<if test="record.subImages != null" >
#{record.subImages,jdbcType=VARCHAR},
</if>
<if test="record.detail != null" >
#{record.detail,jdbcType=VARCHAR},
</if>
<if test="record.price != null" >
#{record.price,jdbcType=DECIMAL},
</if>
<if test="record.stock != null" >
#{record.stock,jdbcType=INTEGER},
</if>
<if test="record.status != null" >
#{record.status,jdbcType=INTEGER},
</if>
<if test="record.createTime != null" >
#{record.createTime,jdbcType=TIMESTAMP},
</if>
<if test="record.updateTime != null" >
now(),
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.aiun.product.pojo.Product" >
update trade_product
<set >
<if test="record.categoryId != null" >
category_id = #{record.categoryId,jdbcType=INTEGER},
</if>
<if test="record.name != null" >
name = #{record.name,jdbcType=VARCHAR},
</if>
<if test="record.subtitle != null" >
subtitle = #{record.subtitle,jdbcType=VARCHAR},
</if>
<if test="record.mainImage != null" >
main_image = #{record.mainImage,jdbcType=VARCHAR},
</if>
<if test="record.subImages != null" >
sub_images = #{record.subImages,jdbcType=VARCHAR},
</if>
<if test="record.detail != null" >
detail = #{record.detail,jdbcType=VARCHAR},
</if>
<if test="record.price != null" >
price = #{record.price,jdbcType=DECIMAL},
</if>
<if test="record.stock != null" >
stock = #{record.stock,jdbcType=INTEGER},
</if>
<if test="record.status != null" >
status = #{record.status,jdbcType=INTEGER},
</if>
<if test="record.createTime != null" >
create_time = #{record.createTime,jdbcType=TIMESTAMP},
</if>
<if test="record.updateTime != null" >
update_time = now(),
</if>
</set>
where id = #{record.id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.aiun.product.pojo.Product" >
update trade_product
set category_id = #{record.categoryId,jdbcType=INTEGER},
name = #{record.name,jdbcType=VARCHAR},
subtitle = #{record.subtitle,jdbcType=VARCHAR},
main_image = #{record.mainImage,jdbcType=VARCHAR},
sub_images = #{record.subImages,jdbcType=VARCHAR},
detail = #{record.detail,jdbcType=VARCHAR},
price = #{record.price,jdbcType=DECIMAL},
stock = #{record.stock,jdbcType=INTEGER},
status = #{record.status,jdbcType=INTEGER},
create_time = #{record.createTime,jdbcType=TIMESTAMP},
update_time = now()
where id = #{record.id,jdbcType=INTEGER}
</update>
<select id="selectList" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from trade_product
order by id asc
</select>
<select id="selectByNameAndProductId" resultMap="BaseResultMap" parameterType="map">
select
<include refid="Base_Column_List"/>
from trade_product
<where>
<if test="productName != null">
and name like #{productName}
</if>
<if test="productId != null">
and id = #{productId}
</if>
</where>
</select>
<select id="selectByNameAndCategoryIds" resultMap="BaseResultMap" parameterType="map">
select
<include refid="Base_Column_List"/>
from trade_product
where status = 1
<if test="productName != null">
and name like #{productName}
</if>
<if test="categoryIdList != null">
and category_id in
<foreach item="item" index="index" open="(" separator="," close=")" collection="categoryIdList">
#{item}
</foreach>
</if>
</select>
</mapper>
产品的数据库建表
CREATE TABLE amall_product (
id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘商品id’,
category_id int(11) NOT NULL COMMENT ‘分类id,对应mmall_category表的主键’,
name varchar(100) NOT NULL COMMENT ‘商品名称’,
subtitle varchar(200) DEFAULT NULL COMMENT ‘商品副标题’,
main_image varchar(500) DEFAULT NULL COMMENT ‘产品主图,url相对地址’,
sub_images text COMMENT ‘图片地址,json格式,扩展用’,
detail text COMMENT ‘商品详情’,
price decimal(20,2) NOT NULL COMMENT ‘价格,单位-元保留两位小数’,
stock int(11) NOT NULL COMMENT ‘库存数量’,
status int(6) DEFAULT ‘1’ COMMENT ‘商品状态.1-在售 2-下架 3-删除’,
create_time datetime DEFAULT NULL COMMENT ‘创建时间’,
update_time datetime DEFAULT NULL COMMENT ‘更新时间’,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8;
到这里产品的相关类就介绍完毕了,文件上传见SpringBoot实现文件上传.
2.pom.xml
pom文件的依赖和微服务(三)—— 用户模块(backend-user).类似,初次之外还有下面一些依赖
<!-- pagehelper 分页依赖 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<!-- thymeleaf 页面模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- user api 依赖 -->
<dependency>
<groupId>com.aiun</groupId>
<artifactId>backend-service-user-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
pagehelper 分页依赖,是MyBatis的分页工具,用于产品的分页
thymeleaf 页面模板引擎,是前端HTML页面渲染工具
user api 依赖因为产品模块会用到user实体类,所以引入user依赖
3.配置文件application.yml
# Spring
spring:
# 服务应用名称
application:
name: backend-product
# 数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/trade?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
# 指定druid 连接池以及druid 连接池配置
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 1 # 初始连接数
max-active: 20 # 最大连接数
max-idle: 20 # 最大空闲
min-idle: 1 # 最小空闲
max-wait: 60000 # 最长等待时间
server:
port: 8083 # 项目访问端口
servlet:
context-path: /backend-product # 项目访问路径
# 产品图片存放地址
image:
localhost: F:/Work/imgHost/
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
instance:
hostname: localhost
# MyBatis
mybatis:
# 配置 MyBatis 数据返回类型别名(默认别名是类名)
type-aliases-package: com.aiun.product.pojo
# 配置 MyBatis Mapper 映射文件
mapper-locations: classpath:/mappers/*.xml
# MyBatis SQL 打印(方法接口所在的包,不是 Mapper.xml 所在的包)
#logging:
#level:
# com.aiun.order.mappers: debug
## pagehelper
pagehelper:
helperDialect: sqlite #postgresql
reasonable: true
supportMethodsArguments: true
params: countSql
count: countSql
returnPageInfo: check
# 记录日志
logging:
config: classpath:logback-spring.xml
和产品模块的配置类似
4.启动项目
打开SpringBoot的启动类ProductApplication
package com.aiun.product;
/**
* 产品启动类
* @author lenovo
*/
@EnableSwagger2
@EnableEurekaClient
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
通过Google插件Talend API Test进行后端接口测试
保存成功!!!
到这里产品模块就完成了。
注:项目已上传GitHub:https://github.com/AiunCode/BackendManageSystem.