电商平台搭建--商品管理功能模块开发(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36314960/article/details/79320123

Hi,大家好,我们又见面了。相信通过前面几篇博文的学习,大家已经对如何搭建一款属于自己的电商平台有了初步的了解,也大致懂了SSM框架的主要开发流程,那么在接下来的几篇博文中,我将带领大家完成商品管理功能模块的开发,还在等什么,直接进入正题吧!

一、商品管理功能模块-概要

      先来看商品模块都需要实现哪些功能点


      电商平台的商品管理模块,一般都分为前台和后台,所以在后端要写前台的商品管理,也要写后台的商品管理。按照正常顺序,先来完成前台商品管理的功能。

     相对于后台来讲,前台的商品管理比较简单,主要功能一共就两个,获取商品详情、前台商品搜索。在这两个功能里面,获取商品详情比较简单,我们会在前台商品搜索中把分页逻辑写好,方便前台调用。

二、商品管理功能模块-前台-获取商品详情功能的实现

Service层

//前台-获取商品详细信息
    public ServerResponse<ProductDetailVo> getProductDetail(Integer productId){
        if(productId == null){
            return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGALARGUMENT.getCode(), ResponseCode.ILLEGALARGUMENT.getDesc());
        }
        Product product = productMapper.selectByPrimaryKey(productId);
        if(product == null){
            return ServerResponse.createByErrorMessage("产品已下架或已删除");
        }
        if(product.getStatus() != Const.ProductStatusEnum.ON_SALE.getCode()){
            return ServerResponse.createByErrorMessage("产品已下架或已删除");
        }
        ProductDetailVo productDetailVo = assembleProductDetailVo(product);
        return ServerResponse.createBySuccess(productDetailVo);
    }

    首先来看,在getProductDetail方法中,ServerResponse的泛型被指定为ProductDetailVo类型。这里的Vo是value-object也就是值对象的简称。什么是值对象呢?它的本质也是一个JavaBean,只不过是为了专门解决一种或多种需求独立出来的JavaBean。有了VO以后,对数据的处理就会更加灵活,因为我们可以单独的封装它们,这样即保证了数据的独立性,也不会对项目的整体数据造成影响,当我们的VO处理完毕以后,并入到项目中,也降低了各个功能之间的依赖性。就前台-获取商品详细信息来看,我封装了一个ProductDetailVo这么一个值对象,它里面存放了和商品所有有关的信息字段以及getter和setter方法。

    先回到该方法,传递一个productId来在数据库中查询相关的商品信息。如果productId为空,则提示参数错误,否则就向数据库中查询,将查询结果返回给product,如果其为空表示产品已下架或已删除,还有一种情况是根据商品的状态,根据返回值来判断商品的状态,这个ProductStatusEnum会在下面补充。当商品的校验状态通过以后,就可以直接调用封装好的assembleProductDetailVo方法来处理商品的详细信息,最后直接返回productDetailVo即可,这个字段里面就包括了商品的详细信息。

public class ProductDetailVo {

    private Integer id;
    private Integer categoryId;
    private String name;
    private String subtitle;
    private String mainImage;
    private String subImages;
    private String detail;
    private BigDecimal price;
    private Integer stock;
    private Integer status;
    private String createTime;
    private String updateTime;

    private String imageHost;
    private Integer parentCategoryId;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Integer categoryId) {
        this.categoryId = categoryId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSubtitle() {
        return subtitle;
    }

    public void setSubtitle(String subtitle) {
        this.subtitle = subtitle;
    }

    public String getMainImage() {
        return mainImage;
    }

    public void setMainImage(String mainImage) {
        this.mainImage = mainImage;
    }

    public String getSubImages() {
        return subImages;
    }

    public void setSubImages(String subImages) {
        this.subImages = subImages;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getStock() {
        return stock;
    }

    public void setStock(Integer stock) {
        this.stock = stock;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public String getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }

    public String getImageHost() {
        return imageHost;
    }

    public void setImageHost(String imageHost) {
        this.imageHost = imageHost;
    }

    public Integer getParentCategoryId() {
        return parentCategoryId;
    }

    public void setParentCategoryId(Integer parentCategoryId) {
        this.parentCategoryId = parentCategoryId;
    }
}

    为了使用这个VO,我们不能在其内部去写方法,然后在外部调用,这样是不符合情理的。对于这种情况,可以在对应的Service中封装一个处理VO的方法,这样一来,就可以实现“点对点”的功能服务。在该方法中,我写了一个处理商品详情的VO方法。

    private ProductDetailVo assembleProductDetailVo(Product product){
        ProductDetailVo productDetailVo = new ProductDetailVo();
        productDetailVo.setId(product.getId());
        productDetailVo.setSubtitle(product.getSubtitle());
        productDetailVo.setPrice(product.getPrice());
        productDetailVo.setName(product.getName());
        productDetailVo.setSubImages(product.getSubImages());
        productDetailVo.setMainImage(product.getMainImage());
        productDetailVo.setDetail(product.getDetail());
        productDetailVo.setStatus(product.getStatus());
        productDetailVo.setStock(product.getStock());
        productDetailVo.setCategoryId(product.getCategoryId());

        productDetailVo.setImageHost(PropertiesUtil.getProperty("fet.server.http.prefix", "your-ftp-address"));

        Category category = categoryMapper.selectByPrimaryKey(product.getCategoryId());
        if(category == null){
            productDetailVo.setParentCategoryId(0);
        }else{
            productDetailVo.setParentCategoryId(category.getParentId());
        }

        productDetailVo.setCreateTime(DateTimeUtil.dateToStr(product.getCreateTime()));
        productDetailVo.setUpdateTime(DateTimeUtil.dateToStr(product.getUpdateTime()));
        return productDetailVo;
    }

    像处理JavaBean一样,处理VO的方法类型一定要是当前VO类型的并传递给VO需要的Product数据,否则数据无法处理。首先在该方法中new出一个当前ProductDetailVo的实例,然后把需要处理的数据用setter方法设置好,然后有一个category判断。如果getCategoryId为空,就把它的父节点id也设置为空,否则就把当前的id值更新到父节点id上。最后再设置一下更新商品或者创建商品的创建时间和更新时间,返回productDetailVo,就处理完了ProductDetailVo值对象。

   这里提一下,ImageHost字段为自己的图片服务器地址,因为所有的图片都是保存在图片服务器上面的,所以修改图片要通过图片服务器的方式进行修改,切记。

Service层就写好了,再来看Controller层

    @RequestMapping(value = "detail.do")
    @ResponseBody
    public ServerResponse<ProductDetailVo> detail(Integer productId){
        return iProductService.getProductDetail(productId);
    }

    因为和功能有关的逻辑在Service层中已经处理完善了,所以在Controller里直接返回处理结果即可,别忘了这里的泛型一定是ProductDetailVo类型。

三、商品管理功能模块-前台-商品搜索功能的实现

Service层

//前台商品搜索   
 public ServerResponse<PageInfo> getProductByKeywordCategory(String keyword, Integer categoryId, int pageNum, int pageSize, String orderBy){
        if(StringUtils.isBlank(keyword) && categoryId == null){
            return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGALARGUMENT.getCode(), ResponseCode.ILLEGALARGUMENT.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(category.getId()).getData();
        }
        if(StringUtils.isNotBlank(keyword)){
            keyword = new StringBuilder().append("%").append(keyword).append("%").toString();
        }
//        排序处理
        PageHelper.startPage(pageNum, pageSize);
        if(StringUtils.isNotBlank(orderBy)){
            if(Const.ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)){
                String[] orderByArray = orderBy.split("_");
                PageHelper.orderBy(orderByArray[0]+" "+orderByArray[1]);
            }
        }
        List<Product> productList = productMapper.selectByNameAndCategoryIds(StringUtils.isBlank(keyword)?null:keyword,categoryIdList.size()==0?null:categoryIdList);

        List<ProductListVo> productListVoList = Lists.newArrayList();
        for(Product product : productList){
            ProductListVo productListVo = assembleProductListVo(product);
            productListVoList.add(productListVo);
        }

        PageInfo pageInfo = new PageInfo(productList);
        pageInfo.setList(productListVoList);
        return ServerResponse.createBySuccess(pageInfo);
    }

    在前台商品搜索中,我们需要处理很多事情。首先是搜索方式,可以通过关键词进行搜索,也可以通过categoryId进行搜索(这个categoryId是后端为每一个商品单独添加的id,会直接存放在数据库中对应商品)。其次就是将搜索结果进行一个分页处理,如果没有分页的话,商品列表页会看着非常乱,会严重影响用户体验,这在企业中也是不允许的。

    跟字段验证的方法相似,首先验证keyword和categoryId是否为空,如果为空,则提示参数错误,否则进行下一步操作。像上述方法一样,对于复杂数据的处理,应该封装一个VO,这里的是ProductListVo(下面有介绍)。回到方法中,分页的实现是之前提到的Mybatis三剑客的PageHelper,这是一个开源的GitHub项目(https://github.com/pagehelper/Mybatis-PageHelper)。使用PageHelper进行分页处理,需要三步实现。第一步,调用PageHelper的startPage方法,传入分页数量和每页显示的数量;第二步,填充分页数据;第三步,开始分页。注意,全部用List集合进行处理。对排序逻辑的处理,分为默认排序和价格升降序排序。其实默认排序就是把直接进行搜索的结果显示出来就行,不需要再处理,所以这里的代码是不用写的,只需处理价格的高低即可。因为每排一次序就是重新显示一个界面,所以在处理价格排序时要重新进行分页,逻辑和上述相同不再赘述。因为涉及到排序,所以getProductByKeywordCategory方法的泛型需要为PageInfo,否则无法进行排序。最后将排序处理后的结果返回,就完成了该方法的编写。

PageInfo pageInfo = new PageInfo(productListVoList);//第三步
List<ProductListVo> productListVoList = Lists.newArrayList();//第二步
public class ProductListVo {

    private Integer id;
    private Integer categoryId;

    private String name;
    private String subtitle;
    private String mainImage;
    private BigDecimal price;

    private Integer status;

    private String imageHost;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Integer categoryId) {
        this.categoryId = categoryId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSubtitle() {
        return subtitle;
    }

    public void setSubtitle(String subtitle) {
        this.subtitle = subtitle;
    }

    public String getMainImage() {
        return mainImage;
    }

    public void setMainImage(String mainImage) {
        this.mainImage = mainImage;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getImageHost() {
        return imageHost;
    }

    public void setImageHost(String imageHost) {
        this.imageHost = imageHost;
    }
}

    像之前处理VO方式一样,在对应的Service层中封装一个assemble方法来处里ProductListVo

private ProductListVo assembleProductListVo(Product product){
        ProductListVo productListVo = new ProductListVo();
        productListVo.setId(product.getId());
        productListVo.setName(product.getName());
        productListVo.setCategoryId(product.getCategoryId());
        productListVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","your-ftp-address"));
        productListVo.setMainImage(product.getMainImage());
        productListVo.setPrice(product.getPrice());
        productListVo.setSubtitle(product.getSubtitle());
        productListVo.setStatus(product.getStatus());
        return productListVo;
    }

    因为只是简单的获取到商品的列表信息,所以不用再封装处理数据的方法,只是对字段的一个处理。

Controller层

    @RequestMapping(value = "list.do")
    @ResponseBody
    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);
    }

   因为和功能有关的逻辑在Service层中已经处理完善了,所以在Controller里直接返回处理结果即可。 这里又用到了RequestParam注解来为每个字段设置默认值。keyword和categoryId的默认值为false,表示如果前台不传递这两个字段给后台也是可以进行排序的,即这两个字段不是非必须的。pageNum默认值为1,表示默认只有一页,pageSize默认值为10,表示每页显示10条记录。orderBy默认值为空,表示默认使用默认排序,若使用价格排序,需要前台传入orderBy的值。

四、关于商品模块的一些补充

    (1)、因为是商品模块,安全性较低,所以所有的接口请求都采用默认的GET请求;

    (2)、整个项目的Mybatis层的所有Sql语句,会在后期进行更新;

    (3)、笔者默认认为你已经有一定的JavaBean基础,所以在商品模块没有对JavaBean进行详细的解释;

    (4)、值对象VO是依赖POJO的,所以在处理值对象VO的方法中传入的是对应的POJO类型,例如这里的Product;

  写到这里,前台商品模块所有功能就实现完毕了,在本篇博文中,我们用了较长的篇幅来搭建商品模块的开发基础,希望大家能动手写写,体会一下前台商品模块的开发流程。如果有不懂的地方,欢迎关注,欢迎评论留言。我们下篇再见!!

阅读更多

没有更多推荐了,返回首页