商城-1(开发分类功能)

目录

分类功能实现逻辑

业务分析

实施开发

业务逻辑层实现类

创建控制层

可能遇见问题:

1 Windows系统未配置java环境

2 Mac安装Nacos

 3 Seata的启动

4 Nacos启动


 

分类功能实现逻辑

我们数据库mall_pms的category表使用自关联实现了三级分类

当前酷鲨商城项目使用固定的三级分类

1.从数据库中查询出所有分类信息,一次性全查

2.构建分类信息的父子结构,实现查询返回父子结构的分类信息

3.将查询到的结果保存在Redis中,以备后续用户直接获取

代码中要判断Redis中是否包含全部分类数据,不包含的话做上面操作

包含分类数据的话直接获得之后返回

业务分析

查询全部分类的业务重点在构建三级分类树结构

我们需要将从数据库中查询出的分类对象构成下面的结构

[
    {id:1,name:"手机/运行商/数码",parentId:0,depth:1,children:[
        {id:2,name:"手机通讯",parentId:1,depth:2,children:[
            {id:3,name:"智能手机",parentId:2,depth:3,children:null},
            {id:4,name:"非智能手机",parentId:2,depth:3,children:null}
        ]}
    ]},
    {id:5,name:"电脑/办公",parentId:0,depth:1,children:[....]}
]

上面是我们需要获得的对象的结构

在数据库mall_pms中

有pms_category表,这个表就是保存全部分类信息的表格

  • id:主键
  • name:显示在页面上的分类名称
  • parentId:父分类的id 如果是一级分类父分类id为0
  • depth:分类深度,当前项目就是3级分类,1\2\3 分别代表它的等级
  • keyword:搜索关键字
  • sort:排序依据 正常查询时,根据此列进行排序,数字越小越出现在前面(升序)
  • icon:图标地址
  • enable:是否可用
  • isparent:是否为父分类 0 假 1真
  • isdisplay:是否显示在导航栏 0不显示 1显示

实施开发

业务逻辑层实现类

在csmall-front-webapi项目中开发

创建service.impl包

包中编写业务逻辑层实现类 实现IFrontCategoryService

@DubboService
@Service
@Slf4j
public class FrontCategoryServiceImpl implements IFrontCategoryService {

    // 装配操作Redis的对象
    @Autowired
    private RedisTemplate redisTemplate;
    // 当前front模块没有连接数据库的操作,所有数据均来自于Dubbo调用product模块
    // 这里是消费product模块查询所有分类数据的功能
    @DubboReference
    private IForFrontCategoryService dubboCategoryService;

    // 开发过程中使用Redis的规范:为了降低Redis使用Key拼写错误的情况,我们会定义常量
    public static final String CATEGORY_TREE_KEY="category_tree";

    @Override
    public FrontCategoryTreeVO categoryTree() {
        // 我们先检查Redis中是否已经保存了包含所有分类的三级分类树对象
        if(redisTemplate.hasKey(CATEGORY_TREE_KEY)){
            // redis中已经包含了三级分类树对象,获取后直接返回即可
            FrontCategoryTreeVO<FrontCategoryEntity> treeVO=
                    (FrontCategoryTreeVO<FrontCategoryEntity>)
                    redisTemplate.boundValueOps(CATEGORY_TREE_KEY).get();
            // 将从redis中获得的treeVO返回
            return treeVO;
        }
        // Redis中没有三级分类树信息,表示本次请求可能是首次访问
        // dubbo调用查询所有分类对象的方法
        List<CategoryStandardVO> categoryStandardVOs=
                        dubboCategoryService.getCategoryList();
        // 我们需要将没有关联子分类能力的CategoryStandardVO类型
        // 转换为具备关联子分类能力的FrontCategoryEntity类型
        // 并将正确的父子分类关系保存构建起来,最好编写一个单独的方法
        FrontCategoryTreeVO<FrontCategoryEntity> treeVO=
                            initTree(categoryStandardVOs);
        // 上面已经完成了三级分类树的构建,下面要将返回值treeVO
        // 保存在redis中,方便后面请求的访问
        redisTemplate.boundValueOps(CATEGORY_TREE_KEY)
                .set(treeVO,1, TimeUnit.MINUTES);
        // 上面时间是针对学习测试而定的,实际开发中会设置比较长的时间,比如24小时
        // 最后别忘了返回!!!!
        return treeVO;
    }

    // 将从数据库中查询到的分类对象转换为三级分类树的方法
    private FrontCategoryTreeVO<FrontCategoryEntity> initTree(
                        List<CategoryStandardVO> categoryStandardVOs) {
        // 第一步:
        // 确实所有分类对象的父分类id
        // 以父分类id为key,将相同父分类的子分类对象保存到同一个Map的key中
        // 一个父分类可能包含多个子分类,所有这个Map的value是一个List
        Map<Long,List<FrontCategoryEntity>> map=new HashMap<>();
        log.info("当前分类对象总数:{}",categoryStandardVOs.size());
        // 遍历所有分类对象的集合
        for(CategoryStandardVO categoryStandardVO: categoryStandardVOs){
            // CategoryStandardVO是没有children熟悉的不能保存分类父子关系
            // 所有我们要先将其中的对象转换为能够保存父子分类关系的FrontCategoryEntity
            FrontCategoryEntity frontCategoryEntity=new FrontCategoryEntity();
            // 将同名属性赋值到frontCategoryEntity对象中
            BeanUtils.copyProperties(categoryStandardVO,frontCategoryEntity);
            // 获取当前元素对应的父分类id(父分类id为0表示当前分类对象为一级分类)
            // 后面反复使用这个父分类id,所以最好取出
            Long parentId=frontCategoryEntity.getParentId();
            // 这个父分类id要作为key保存到map中,所有要先判断map中是否已经包含这个key
            if(map.containsKey(parentId)){
                // 运行到这表示当前map已经包含了这个父分类的key
                // 将当前分类追加到map元素的value中
                map.get(parentId).add(frontCategoryEntity);
            }else {
                // 运行到这表示当前map还没有这个父分类id的key对应的元素
                // 先实例化一个list对象当做map的value,并添加当前分类对象
                List<FrontCategoryEntity> value=new ArrayList<>();
                value.add(frontCategoryEntity);
                // 在map中创建一个以父分类id为key的元素(使用put方法)
                map.put(parentId,value);
            }
        }
        // 第二步:
        // 将子分类对象关联到父分类对象的children属性中
        // 下面操作应该从一级分类开始,我们先通过0作为父分类id获得所有一级分类
        List<FrontCategoryEntity> firstLevels=map.get(0L);
        // 判断一级分类集合是否为空,防止后续出现空指针
        if(firstLevels==null || firstLevels.isEmpty()){
            throw new CoolSharkServiceException(
                    ResponseCode.INTERNAL_SERVER_ERROR,"没有一级分类,运行结束");
        }
        // 遍历所有一级分类对象
        for(FrontCategoryEntity oneLevel : firstLevels){
            // 获得当前一级分类的id
            Long secondLevelParentId=oneLevel.getId();
            // 获取当前一级分类对象包含的二级分类集合
            List<FrontCategoryEntity> secondLevels=map.get(secondLevelParentId);
            if(secondLevels==null || secondLevels.isEmpty()){
                log.warn("当前分类没有二级分类内容:{}",secondLevelParentId);
                // 如果当前一级分类没有二级分类,跳过本次循环,继续下次循环
                continue;
            }
            // 遍历二级分类对象集合
            for(FrontCategoryEntity twoLevel : secondLevels){
                // 获得二级分类的id(三级分类的父id)
                //                               ↓↓↓↓↓↓
                Long thirdLevelParentId=twoLevel.getId();
                // 根据二级分类id获得对应当前二级分类对象的所有三级分类元素集合
                List<FrontCategoryEntity> thirdLevels=map.get(thirdLevelParentId);
                // 判断当前三级分类是否为空
                if(thirdLevels==null || thirdLevels.isEmpty()){
                    log.warn("当前二级分类对象没有三级分类内容:{}",thirdLevelParentId);
                    continue;
                }
                // 将三级分类对象集合,保存到二级分类对象的children属性中
                twoLevel.setChildrens(thirdLevels);
            }
            // 将二级分类对象集合,保存到一级分类对象的children属性中
            oneLevel.setChildrens(secondLevels);
        }
        // 到此为止,所有的分类对象都保存到了正确的父分类对象的children属性中
        // 最终包含它们的是firstLevels集合,但是我们的返回值是FrontCategoryTreeVO<FrontCategoryEntity>
        // 所以我们要实例化这个类型对象,并将firstLevels赋值到其中,然后返回
        FrontCategoryTreeVO<FrontCategoryEntity> treeVO=
                new FrontCategoryTreeVO<>();
        treeVO.setCategories(firstLevels);
        // 千万记得返回treeVO对象!!!!
        return treeVO;
    }
}

创建控制层

controller包

CategoryController类

代码如下

@RestController
@RequestMapping("/front/category")
@Api(tags = "前台分类查询")
public class CategoryController {
    @Autowired
    private IFrontCategoryService frontCategoryService;

    @GetMapping("/all")
    @ApiOperation("查询获得三级分类树对象")
    public JsonResult<FrontCategoryTreeVO<FrontCategoryEntity>> getTreeVO(){
        FrontCategoryTreeVO<FrontCategoryEntity> treeVO=
                frontCategoryService.categoryTree();
        return JsonResult.ok(treeVO);
    }
    
}

启动nacos\seata\redis

先启动生产者product\后启动消费者front

访问

localhost:10004/doc.html

可能遇见问题:

1 Windows系统未配置java环境

参考:

JDK 安装与环境变量配置(Win10详细版)_初学者-Study的博客-优快云博客_jdk的环境https://blog.youkuaiyun.com/qq_38436214/article/details/105071088

2 Mac安装Nacos

mac系统如何安装nacos_ANSGSY的博客-优快云博客_mac nacos安装icon-default.png?t=M85Bhttps://blog.youkuaiyun.com/weixin_49866355/article/details/126959558?spm=1001.2014.3001.5501

 3 Seata的启动

Seata概述(如何使远程调用也支持事务功能)_ANSGSY的博客-优快云博客_seata 远程调用icon-default.png?t=M85Bhttps://blog.youkuaiyun.com/weixin_49866355/article/details/127021366?spm=1001.2014.3001.5501

4 Nacos启动

注册中心Nacos_ANSGSY的博客-优快云博客icon-default.png?t=M85Bhttps://blog.youkuaiyun.com/weixin_49866355/article/details/126998723?spm=1001.2014.3001.5501

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nia134

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值