微服务(五)—— 产品模块(backend-product)

上一篇: 微服务(四)—— 用户模块(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) DEFAULT1COMMENT ‘类别状态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) DEFAULT1COMMENT ‘商品状态.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.

下一篇: 微服务(六)—— 注册中心模块(backend-server).

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值