谷粒商城项目笔记之分布式基础(二)

本文详细记录了谷粒商城项目中商品服务的三级分类实现,包括数据库设计、代码编写、路径重写和跨域问题解决。在分类管理中,涉及了树形结构展示、删除、新增和拖拽排序等功能。此外,还介绍了品牌管理部分,如文件上传、前端表单校验和JSR303数字校验的运用。在开发过程中,遇到了端口占用、路径映射错误和跨域等问题,通过调整配置和代码实现了解决方案。
摘要由CSDN通过智能技术生成

6 商品服务

6.1 三级分类

image-20221029090944745

商城的商品页面展示是一个三级分类的。有一级分类、二级分类、三级分类。这就是我们接下来要进行的操作。

表名解释

image-20221116142746821

数据库中表名 对应表
pms_attr 属性表
pms_attr_attrgroup_relation 属性&属性分组关联表
pms_attr_group 属性分组表
pms_brand 商品品牌表
pms_category 商品三级分类表

6.1.1 数据库

  • 首先我们在gulimall_pms这个数据库中的pms_category这个表下插入数据(此处展示不了,自己去找资源创建)
    以上就将商品分类的表和数据都给创建好了,接下来我们就需要进行代码编写了。

6.1.2 查出所有分类及其子分类

1、CategoryController

gulimall-product中的controller包下的CategoryController

  • 在类中对原来逆向生成的代码进行修改,
@RestController
@RequestMapping("product/category")
public class CategoryController {
   
    @Autowired
    private CategoryService categoryService;
    /**
     * 查出所有分类以及子分类,以树形结构组装起来
     */
    @RequestMapping("/list/tree")
    public R list(){
   
        List<CategoryEntity> entities =  categoryService.listWithTree();

        return R.ok().put("data", entities);
    }
 }
2、CategoryService

接着我们使用idea自带的工具帮助我们生成相应的方法。

/**
 * 商品三级分类
 */
public interface CategoryService extends IService<CategoryEntity> {
   

    void removeMenuByIds(List<Long> asList);    //本次修改的方法
}
3、CategoryServiceImpl
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
   

//    @Autowired
//    CategoryDao categoryDao;   //其实这里ServiceImpl的泛型就是categoryDao,所以我们可以直接使用ServiceImpl里面的baseMapper

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
   
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> listWithTree() {
   
        //1.查出所有分类
        //没有查询条件,就是查询所有
        List<CategoryEntity> entities = baseMapper.selectList(null);

        //2.组装成父子的树形结构
        //2.1 找到所有的一级分类  categoryEntity -> {} lambda表达式
        List<CategoryEntity> level1Menus = entities.stream().filter(categoryEntity ->
            categoryEntity.getParentCid() == 0   //一级分类的父分类id是0   过滤
        ).map((menu) -> {
       //在菜单收集成list之前先通过递归找到菜单的所有子分类,放在map中,然后排序,即将当前菜单改了之后重新返回,然后再收集菜单。
            menu.setChildren(getChildrens(menu,entities));
            return menu;
        }).sorted((menu1,menu2) -> {
   
            return (menu1.getSort() == null ?0:menu1.getSort()) - (menu2.getSort() == null ?0:menu2.getSort());   //子菜单肯定有前一个和后一个之分,
        }).collect(Collectors.toList());


        return level1Menus;
    }

    @Override
    public void removeMenuByIds(List<Long> asList) {
   
        baseMapper.deleteBatchIds(asList);
    }

    //递归查找所有的子菜单
    //root  当前菜单   all  所有菜单
    private List<CategoryEntity> getChildrens(CategoryEntity root,List<CategoryEntity> all){
   

        List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
   
            return categoryEntity.getParentCid() == root.getCatId(); // 二级菜单的父分类id == 一级分类的catid

        }).map(categoryEntity -> {
   
            categoryEntity.setChildren(getChildrens(categoryEntity, all));   // 二级菜单下还有三级菜单,继续查找
            return categoryEntity;

        }).sorted((menu1, menu2) -> {
       //sorted()定制排序
            return (menu1.getSort() == null ?0:menu1.getSort()) - (menu2.getSort() == null ?0:menu2.getSort());
        }).collect(Collectors.toList());

        return children;
    }

}

这里使用的是流式编程,对于这方面我们可去参考java8新特性的StreamAPI来进行相应的学习。

4、启动测试(端口占用问题解决)

我们启动gulimall-product微服务进行测试查询。在启动的时候发现有报错

Description:

The Tomcat connector configured to listen on port 10000 failed to start. The port may already be in use or the connector may be misconfigured.

Action:

Verify the connector's configuration, identify and stop any process that's listening on port 10000, or configure this application to listen on another port.

上面的意思是我们的10000端口被占用。接下来我们看看到底是什么服务占用了10000端口。

在这里插入图片描述
在这里插入图片描述

image-20221119171718001

netstat -aon |findstr "12000"     #找到12000端口
taskkill /F /PID  6164   #杀死12000端口对应的线程

记:深信服vpn进程在日常工作中需要,而且不知道为什么杀不死?

  • 解决办法:暂时修改gulimall_product端口为11000,gulimall_ware端口为12000.

    我们接着进行测试,浏览器发送http://localhost:11000/product/category/list/tree,测试结果如下图,显示正确。这里我们推荐浏览器装一个Json格式的处理的插件可以很好的帮助我们查看Json数据。

  • 查询所有


6.1.3 配置网关路由与路径重写

启动renren-fast微服务,这个是后台。将renren-fast-vue在vscode中启动,接着我们来到后台系统进行菜单模块的添加。

1、 后台添加目录和菜单
  1. 在菜单管理中添加一个商品系统的目录。如下图。image-20221027213836441

  2. 在商品系统中新增一个分类维护的菜单。菜单的路由其实就是我们商品微服务中的访问路径。

image-20221027214924676

我们在后台系统中修改的,在数据库的gulimall-admin中也会同步进行修改。

image-20221027215105624

  • 我们可以看到如果我们点击角色管理的话,地址栏是/sys-role,但是我们实际发送的请求应该是/sys/role,所以由此可以知道后台会将/自动转换为-,同理我们去访问/product/category也会自动被转换为/product-category

  • 我们在renren-fast-vue中可以看到有一个文件,对应的其实就是/sys-role,即sys文件夹下的role.vue对应的就是角色管理这个页面的展示。所以对于商品分类/product/category,我们接下来要做的就是在renren-fast-vue下创建一个product文件夹,文件夹中创建一个category.vue来进行页面展示。

image-20221029122852894

image-20221027215558125

2、编写树形结构
  1. 对于这一段前端开发的代码,我们可以借鉴element.eleme.cn中的快速开发指南进行编写。
<template>
    <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
</template>

<script>
export default {
    name: 'category',
    components: {},
    directives: {},
     data() {
      return {
        data: [],
        defaultProps: {
          children: 'children',
          label: 'label'
        }
      };
    },
    mounted() {
        
    },
    methods: {
        handleNodeClick(data) {
        console.log(data);
      },
      getMenus(){
        this.$http({
          url: this.$http.adornUrl('/product/category/list/tree'),
          method: 'get'
        }).then(data=>{
            console.log(data)
        })
      }
    },
    created(){
        this.getMenus();
    }
};
</script>

<style scoped>

</style>
  1. 进行测试

测试中发现检查网页源代码发现,本来应该是给商品微服务11000端口发送的查询的,但是发送到了renren-fast了。

renren-fast-vue中有一个Index.js是管理api接口请求地址的,如下图。如果我们本次只是简单的将8080改为11000端口,那么当下次如果是12000呢?难道每次都要改吗?所以我们的下一步做法是使用网关进行路由。通过网关映射到具体的请求地址。

image-20221027222102566

注意这里截图中vue的格式是有错误的,如果不进行修正的话,运行会报错。我们前端的代码中只要显示了红色的,就要检查是哪里错误

image-20221028095819963

对于微服务,后面我们统一改为加api前缀才能路由过去。

接下来进行测试访问,我们发现验证码一直加载不出来。检查网页源代码发现是因为我们直接给网关发送验证码请求了。但是真实的应该是给renren-fast发送请求。image-20221027222409536

3、将renren-fast注册进nacos,使用网关进行统一管理并进行路径重写
  • 引入gulimall-common

image-20221027222544585

  • 在renren-fast的application.yml文件中配置nacos注册中心地址image-20221027222916180

  • 在renren-fast的主启动类上加入@EnableDiscoveryClient注解,使得该微服务会被注册中心发现image-20221027222947987

  • 在网关配置文件中加入:

    - id: admin_route
      uri: lb://renren-fast # 路由给renren-fast,lb代表负载均衡
      predicates:  # 什么情况下路由给它
       - Path=/api/** # 默认前端项目都带上api前缀,路径中带了api前缀的
      filters:
           - RewritePath=/api/(?<segment>.*),/renren-fast/$\{
         segment}
    

    注意

    这个地方一定要格式对齐,否则启动后会出现下面这个问题:

    启动报错:Caused by: org.yaml.snakeyaml.scanner.ScannerException: mapping values are not allowed here

    这个地方报错的原因大概率是yml文件语法错误:注意这个坑找了好久,id uri predicates filters都要对齐,同一层级。

4、启动测试(有坑)
  1. 最开始进行启动,在renren-fast的CorsConfig跨域配置中,allowedOriginPatterns报错。出现原因是因为:我们使用的springboot版本是2.1.8.RELEASE。所以将这个.allowedOriginPatterns换成.allowedOrigins即可。

image-20221028084313631

image-20221028084331356

  1. 最开始报错,在b站看了评论和弹幕之后将gulimall-common这个依赖给取消了,因为启动报依赖循环报错。后面我将所有的依赖都换成老师的同样的版本之后就没有了。
  • 记一次错误踩坑 (这个在后来将gulimall下的所有依赖进行按照老师统一版本进行重构之后,还是使用的引入)

    对于renren-fast中不要引入gulimall-common这个依赖,否则会出现依赖循环报错这个情况以及一系列的突发情况,正常应该是我们自己引入nacos的pom。

    舍弃:

    	<dependency>
    		<groupId>com.alibaba.cloud</groupId>
    		<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    		<version>2.1.0.RELEASE</version>
    	</dependency>
    
    	<!--nacos作为注册中心,服务注册与发现-->
    	<dependency>
    		<groupId>com.alibaba.cloud</groupId>
    		<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    		<version>2.1.0.RELEASE</version>
    	</dependency>
    

如此这样之后,启动renren-fast项目后发现仍然报错,报错的原因居然是:no server available nacos

这样的错误可能是application.yml文件中nacos配置错误。回去一看,果然是写成了nacos-config-addr,其实应该是nacos-discovery-addr . (注意,细心啊。)

再次启动,启动成功。

image-20221028094934894

鉴于上面出现很多错误,但是老师视频中没有出现这些错误,大概率是因为依赖的原因,所以对于gulimall中所有的依赖进行统一,按照老师的依赖进行配置。以防止后面出现很多突发的错误。

  • 根据老师的依赖进行重新设置,然后重新运行网关。

修改后运行成功,验证码出现。

5、浏览器跨域问题

上面我们验证码出现了,但是我们登录却报错,原因在于浏览器的跨域问题。

image-20221028141815905

  1. 引入浏览器跨域知识

image-20221029163137756

image-20221029163152496

image-20221029163210926

image-20221029163222814

  1. 解决办法:在gulimall-gateway中设置GulimallCorsConfiguration解决跨域问题

我们在gulimall-gateway中创建一个config来存放GulimallCorsConfiguration。注意这个包一定是要在gateway这个包下,否则启动报错(坑)。


@Configuration
public class GulimallCorsConfiguration {
   

    @Bean   // 添加过滤器
    public CorsWebFilter corsWebFilter(){
   
        // 基于url跨域,选择reactive包下的
        UrlBasedCorsConfigurationSource source = new
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值