Do not go gentle into that|

stepForward-

园龄:3年9个月粉丝:5关注:3

2023-06-06 11:45阅读: 84评论: 0推荐: 0

线上商城项目实战简单总结

SpringBoot 大型线上商城项目实战总结

知识点和可以借鉴到自己项目的点:

分页逻辑的处理操作

​ 这里没有使用封装好的分页处理的相关工具类,而是自己去写分页封装的逻辑代码,帮助我们去了解分页操作的底层逻辑。

​ 一个是PageQueryUtil工具类,这个工具类是作为分页查询操作的一个"参数接受器",为什么这么说,因为它继承了map,它的构造器中的参数也是map类型,这样我们除了传分页查询需要的page(页码)limit(每页条数)两个重要的参数之外,还可以传其他查询条件的参数,例如商城端用户查询我的订单时的controller处理操作:

image-20221215111317548

PageQueryUtil工具类的构造器:

image-20221215110000941

​ 注意红色框住注释的地方,是整个分页处理操作的核心。

​ 除了上面的PageQueryUtil工具类,另一个就是分页查询返回结果类PageResult,它不仅仅返回分页查询出来的结果集,既列表数据,它还封装各种分页的参数给前端使用。

image-20221215110438424

商品分类的三级联动

​ 就是一级商品分类下会显示所有二级商品分类,二级分类下会显示所有三级分类,这是在商城端的商品分类。

image-20221215150018784

​ 在商品分类表中,除了分类id决定唯一一个商品分类之外,分类级别和分类名称两个属性也能决定唯一一个商品分类。比如在添加商品分类时,如果存在分类等级和分类名称相同的商品分类则添加失败。

​ 在商品分类表中,父分类id字段和分类级别是实现商品分级联动的关键。

image-20221215150530740

​ 例如我要查询一级分类下的所有二级分类,那么sql语句就是:

select * from tb_newbee_mall_goods_category where parent_id=0 and category_level=1; 

​ 查询一级分类下所有的二级分类,那么不就是上面查询结果获取主键分类id作为查询条件去查询所有二级分类。

SELECT
	* 
FROM
	tb_newbee_mall_goods_category 
WHERE
	parent_id IN ( SELECT category_id FROM tb_newbee_mall_goods_category WHERE parent_id = 0 AND category_level = 1 ) 
	AND category_level = 2;

​ 同理获取二级分类下的所有三级分类也是一样。

SELECT
	* 
FROM
	tb_newbee_mall_goods_category 
WHERE
	parent_id IN ( SELECT category_id FROM tb_newbee_mall_goods_category WHERE parent_id IN ( SELECT category_id FROM tb_newbee_mall_goods_category WHERE parent_id = 0 AND category_level = 1 ) AND category_level = 2 ) 
	AND category_level = 3;

​ 其实就是一个子查询操作,此项目中没有很难的sql查询语句,我们将复杂的sql操作都是通过mybatis框架提供的方法写在了业务层进行处理,mapper层只是简单的封装一些增删改查的操作,后续优化我觉得可以使用mybatisplus框架减去mapper层的一些简单重复的增删改查方法,并且还有条件构造器和分页构造器可以大大简化代码量。

public List<NewBeeMallIndexCategoryVO> getCategoriesForIndex() {
    List<NewBeeMallIndexCategoryVO> newBeeMallIndexCategoryVOS = new ArrayList<>();
    //获取一级分类的固定数量的数据
    //sql语句:select * from tb_newbee_mall_goods_category where parent_id=0 and category_level=1;
    List<GoodsCategory> firstLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(Collections.singletonList(0L), NewBeeMallCategoryLevelEnum.LEVEL_ONE.getLevel(), Constants.INDEX_CATEGORY_NUMBER);
    if (!CollectionUtils.isEmpty(firstLevelCategories)) {
        //获取所有一级分类的主键id
        List<Long> firstLevelCategoryIds = firstLevelCategories.stream().map(GoodsCategory::getCategoryId).collect(Collectors.toList());
        //获取二级分类的数据
        //sql语句:
        // select * from tb_newbee_mall_goods_category
        //where parent_id in (select category_id from tb_newbee_mall_goods_category where parent_id=0 and category_level=1)
        //and category_level=2;
        List<GoodsCategory> secondLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(firstLevelCategoryIds, NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel(), 0);
        if (!CollectionUtils.isEmpty(secondLevelCategories)) {
            List<Long> secondLevelCategoryIds = secondLevelCategories.stream().map(GoodsCategory::getCategoryId).collect(Collectors.toList());
            //获取三级分类的数据
            //sql语句:select * from tb_newbee_mall_goods_category where
            // parent_id in (select category_id from tb_newbee_mall_goods_category
            // where parent_id in (select category_id from tb_newbee_mall_goods_category where parent_id=0 and category_level=1) and category_level=2)
            // and category_level=3;
            List<GoodsCategory> thirdLevelCategories = goodsCategoryMapper.selectByLevelAndParentIdsAndNumber(secondLevelCategoryIds, NewBeeMallCategoryLevelEnum.LEVEL_THREE.getLevel(), 0);
            if (!CollectionUtils.isEmpty(thirdLevelCategories)) {
                //根据 parentId--->存放的是二级分类的主键id 将 thirdLevelCategories 分组   
                //groupingBy()对集合中一个或多个属性进行分组
                Map<Long, List<GoodsCategory>> thirdLevelCategoryMap = thirdLevelCategories.stream().collect(groupingBy(GoodsCategory::getParentId));
                List<SecondLevelCategoryVO> secondLevelCategoryVOS = new ArrayList<>();
                //处理二级分类
                for (GoodsCategory secondLevelCategory : secondLevelCategories) {
                    SecondLevelCategoryVO secondLevelCategoryVO = new SecondLevelCategoryVO();
                    BeanUtil.copyProperties(secondLevelCategory, secondLevelCategoryVO);
                    //如果该二级分类下有数据则放入 secondLevelCategoryVOS 对象中
                    if (thirdLevelCategoryMap.containsKey(secondLevelCategory.getCategoryId())) {
                        //根据二级分类的id取出thirdLevelCategoryMap分组中的三级分类list
                        List<GoodsCategory> tempGoodsCategories = thirdLevelCategoryMap.get(secondLevelCategory.getCategoryId());
                        //ThirdLevelCategoryVO和secondLevelCategoryVO类不同,请注意,secondLevelCategoryVO有list方法存取ThirdLevelCategoryVO对象,就下面这个方法
                        secondLevelCategoryVO.setThirdLevelCategoryVOS((BeanUtil.copyList(tempGoodsCategories, ThirdLevelCategoryVO.class)));
                        secondLevelCategoryVOS.add(secondLevelCategoryVO);
                    }
                }
                //到这里已经处理完了二级分类VO对象,所以可以直接操作二级分类VO对象即可。
                //处理一级分类
                if (!CollectionUtils.isEmpty(secondLevelCategoryVOS)) {
                    //根据 parentId--->存放的是一级分类的主键id 将 secondLevelCategories 分组
                    Map<Long, List<SecondLevelCategoryVO>> secondLevelCategoryVOMap = secondLevelCategoryVOS.stream().collect(groupingBy(SecondLevelCategoryVO::getParentId));
                    for (GoodsCategory firstCategory : firstLevelCategories) {
                        NewBeeMallIndexCategoryVO newBeeMallIndexCategoryVO = new NewBeeMallIndexCategoryVO();
                        BeanUtil.copyProperties(firstCategory, newBeeMallIndexCategoryVO);
                        //如果该一级分类下有数据则放入 newBeeMallIndexCategoryVOS 对象中
                        if (secondLevelCategoryVOMap.containsKey(firstCategory.getCategoryId())) {
                            //根据一级分类的id取出secondLevelCategoryVOMap分组中的二级级分类list
                            List<SecondLevelCategoryVO> tempGoodsCategories = secondLevelCategoryVOMap.get(firstCategory.getCategoryId());
                            newBeeMallIndexCategoryVO.setSecondLevelCategoryVOS(tempGoodsCategories);
                            newBeeMallIndexCategoryVOS.add(newBeeMallIndexCategoryVO);
                        }
                    }
                }
            }
        }
        return newBeeMallIndexCategoryVOS;
    } else {
        return null;
    }
}

​ 上面是所有商品分类回显到商城主页的业务逻辑代码,个人觉得还是有点复杂的。这里还使用stream流操作简化代码量了,还学到了stream流collect(groupingBy())对集合中一个或多个属性进行分组,返回结果是一个map类型,这样处理到三级分类时,根据三级分类中的父类id字段进行分组,这样三级分类就归好类,同理二级分类归类到自己所属的一级分类也是如此。(这里可能表达不清楚,建议这里可以进行代码调试就好理解一点,可以学习和借鉴如何去处理这些同一张表下的有关联的数据,是不是可以使用自查询操作?)

VO对象

​ 一开始我不知道什么是Vo对象,然后百度了一下,各有各的说辞,但我觉得Vo对象就是一个为了不暴露过多信息,前端需要什么信息(字段),就将这些字段封装到vo对象响应给前端即可。这个项目分为管理端和用户端,管理端中是不需要用到vo对象的,这里的vo对象都是在用户端中使用的,因为管理端都是管理人员在使用,而用户端则需要。

图片上传和回显处理流程

​ 疑问:在图片上传后图片的访问地址是如何保存到数据库,并且如何进行图片的回显操作?

image-20221210134043026

​ img标签中的src属性为什么为:"http://localhost:28080/upload/20221210_11093518.jpg"

​ 然后我就查看了前后端的代码弄明白了它的图片上传的流程和src中url路径的处理

  1. 首先前端利用js根据id绑定点击事件,当我们点击触发上传按钮时,会发送ajax请求到后端。

    image-20221210135205805

    1. 后端接受ajax请求,响应成功后将响应体中的内容进行赋值操作

      image-202212101357527103. 然后表单提交发送ajax请求到后端

      image-20221210143008934

      后端的controller层代码:数据检验和调用service层。

      /**
           * 添加
           */
          @RequestMapping(value = "/goods/save", method = RequestMethod.POST)
          @ResponseBody
          public Result save(@RequestBody NewBeeMallGoods newBeeMallGoods) {
              if (StringUtils.isEmpty(newBeeMallGoods.getGoodsName())
                      || StringUtils.isEmpty(newBeeMallGoods.getGoodsIntro())
                      || StringUtils.isEmpty(newBeeMallGoods.getTag())
                      || Objects.isNull(newBeeMallGoods.getOriginalPrice())
                      || Objects.isNull(newBeeMallGoods.getGoodsCategoryId())
                      || Objects.isNull(newBeeMallGoods.getSellingPrice())
                      || Objects.isNull(newBeeMallGoods.getStockNum())
                      || Objects.isNull(newBeeMallGoods.getGoodsSellStatus())
                      || StringUtils.isEmpty(newBeeMallGoods.getGoodsCoverImg())
                      || StringUtils.isEmpty(newBeeMallGoods.getGoodsDetailContent())) {
                  return ResultGenerator.genFailResult("参数异常!");
              }
      
              String result = newBeeMallGoodsService.saveNewBeeMallGoods(newBeeMallGoods);
      
              if (ServiceResultEnum.SUCCESS.getResult().equals(result)) {
                  return ResultGenerator.genSuccessResult();
              } else {
                  return ResultGenerator.genFailResult(result);
              }
          }
      

      这样就将数据保存到了数据库。

      image-20221210143428522

      1. 但是我们发现tb_newbee_mall_goods_info该表的goods_cover_img字段中存储照片的路径并不是真实的路径,那我为什么前端图片能正常显示呢?

        是因为我们在NeeBeeMallWebMvcConfigurer类中添加了本地资源映射方法,他的作用是对设定的请求路径进行一个本地资源文件的映射,它会映射到文件存储真实的路径中去。就上面那个新添加到数据库中的例子,http://localhost:28080/upload/20221209_11093518.jpg符合/upload/**,会被映射到真实的路径:D:\mall-images\upload\ (在我电脑下)

        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            //资源映射绑定
            registry.addResourceHandler("/upload/**").addResourceLocations("file:" + Constants.FILE_UPLOAD_DIC);
        }
        

本文作者:sunshineTv

本文链接:https://www.cnblogs.com/sunshineTv/p/17460139.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   stepForward-  阅读(84)  评论(0编辑  收藏  举报
@format
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.