Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)六(商品分类功能实现)

Posted 蓝盒子bluebox

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)六(商品分类功能实现)相关的知识,希望对你有一定的参考价值。

一、业务实现

1、实现商品分类查询

商城的核心自然是商品,而商品多了以后,肯定要进行分类,并且不同的商品会有不同的品牌信息,其关系如图所示∶

  • 一个商品分类下有很多商品
  • 一个商品分类下有很多品牌
  • 而一个品牌,可能属于不同的分类
  • 一个品牌下也会有很多商品

(1)导入mysql

引入下面的SQL语句





运行成功

(2)页面实现

a、页面分析下

首先我们看下面要实现的效果

在leyou-manage-web当中的src当中的pages下item下Category.vue当中

前端的实现逻辑

b、分析请求
  • 请求方式:Get

  • 请求路径:/api/item/category/list。其中/api是网关前缀,litem是网关的路由映射,真实的路径应该是/category/list

  • 请求参数是pid=0,根据tree组件的说明,应该是父节点的id,第一次查询为0,那就是查询一级类目

  • 返回结果:

    根据前面tree组件的用法我们知道,返回的应该是json数组:

[
    {
        "id" : 74,
        "name" : "手机",
        "parentId" : 0,
        "isParent" : true,
        "sort" : 2
    },
    {
        "id" : 75,
        "name" : "家用电器",
        "parentId" : 0,
        "isParent"  :true,
        "sort" : 3
    }
]
  • 事件:
事件名称说明回调参数
handleAdd新增节点时触发,isEdit为true时有效新增节点node对象,包含属性: name,parentld和sort
handleEdit当某个节点被编辑后触发,isEdit为true的时候有效被编辑节点的id和name
handleDelete当删除节点时触发,isEdit为true时有效被删除节点的id
handleClick点击某节点时触发被点击节点的node对象,包含全部消息

(3)代码实现

a、引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ly-item</artifactId>
        <groupId>com.leyou.service</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ly-item-interface</artifactId>

    <dependencies>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-core</artifactId>
            <version>1.0.4</version>
        </dependency>

    </dependencies>

</project>
b、创建实体类(并添加注解)


package com.leyou.item.pojo;
import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name="tb_category")
@Data
public class Category {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private String name;
    private Long parentId;
    private Boolean isParent;
    private Integer sort;
}
//useGeneratedKeys:
// 对于支持自动生成记录主键的数据库,
// 如:MySQL,SQL Server,
// 此时设置useGeneratedKeys参数值为true,
// 在执行添加记录之后可以获取到数据库自动生成的主键ID。
c、Mapper

package com.leyou.item.mapper;

import com.leyou.item.pojo.Category;
import tk.mybatis.mapper.common.Mapper;

public interface CategoryMapper extends Mapper<Category> {

}
d、service


package com.leyou.item.service;

import com.leyou.item.mapper.CategoryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class CategoryService {
    @Autowired
    private CategoryMapper categoryMapper;

}

e、controller



package com.leyou.item.web;

import com.leyou.item.pojo.Category;
import com.leyou.item.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController         //将当前注册控制层
@RequestMapping("category")     //设置当前的请求的请求路径的名称
public class CategoryController {
    @Autowired
    private CategoryService categoryService;        //注入业务层
    /*
    根据父结点id查询商品分类
     */
    @GetMapping("list")     //设置请求路径对应方法名称的别名(映射)
    public ResponseEntity<List<Category>> queryCategoryListByPid(@RequestParam("pid") Long pid){//设置请求参数,对应是返回值
        return ResponseEntity.ok(categoryService.queryCategoryListByPid(pid));;
    }
}

f、完善service层

package com.leyou.item.service;

import com.leyou.item.mapper.CategoryMapper;
import com.leyou.item.pojo.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;

@Service
public class CategoryService {
    @Autowired
    private CategoryMapper categoryMapper;

    public List<Category> queryCategoryListByPid(Long pid) {
        //查询条件,mapper会把对象当中的非空属性作为查询的条件
        Category t = new Category();
        t.setParentId(pid);
        //selectByPrimaryKey(pid);根据主键查询
        List<Category> list = categoryMapper.select(t);//select方法将传入对象的非空字段作为查询的条件
       /* if(list == null || list.isEmpty()){
        }*/
       //上述判断集合是否为空可简化为以下情况
        if(CollectionUtils.isEmpty(list)){
            //查询失败返回自定义的通用异常
            return null;
        }
        return list;
    }
}

g、编写返回空的时候的404枚举

package com.leyou.common.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter               //get方法
@NoArgsConstructor   //无参构造
@AllArgsConstructor  //有参构造
public enum  ExceptionEnum {//枚举是只具有固定实例个数的类

    PRICE_CANNOT_BE_NULL(400,"价格不能为空!"),
    CATEGORY_NOT_FOND(404,"商品分类没有查到"),
    ;
    private int code;
    private String msg;
}

h、继续完善CategoryService

package com.leyou.item.service;

import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.item.mapper.CategoryMapper;
import com.leyou.item.pojo.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
@Service
public class CategoryService {
    @Autowired
    private CategoryMapper categoryMapper;
    public List<Category> queryCategoryListByPid(Long pid) {
        //查询条件,mapper会把对象当中的非空属性作为查询的条件
        Category t = new Category();
        t.setParentId(pid);
        //selectByPrimaryKey(pid);根据主键查询
        List<Category> list = categoryMapper.select(t);//select方法将传入对象的非空字段作为查询的条件
       /* if(list == null || list.isEmpty()){
        }*/
       //上述判断集合是否为空可简化为以下情况
        if(CollectionUtils.isEmpty(list)){
            //查询失败抛出自定义的通用异常
            throw new LyException(ExceptionEnum.CATEGORY_NOT_FOND);
        }
        return list;
    }
}

I、完善启动类LyItemServiceApplication

添加Mapper的扫描

package com.leyou;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.leyou.item.mapper")
public class LyItemServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(LyItemServiceApplication.class);
    }
}

J、修改数据库相关配置文件,修改数据库名称

server:
  port: 8083
spring:
  application:
    name: item-service
  datasource:
    url: jdbc:mysql://localhost:3306/yun6
    username: root
    password: root
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true
    ip-address: 127.0.0.1

(4)启动运行并测试


http://localhost:8083/category/list?pid=0

然后刷新页面看看

报错

2、跨域问题

(1)什么是跨域问题

跨域问题是指跨域的访问,一下情况属于跨域问题

跨域原因说明示例
域名不同www.jd.com与www.taobao.com
域名相同,端口不同www.jd.com:8080与www.jd.com:8081
二级域名不同item.jd.com与miaosha.jd.com

如果域名和端口都相同,但是请求路径不同,不属于跨域,

如;

www.jd.com/item
www.jd.com/goods

而我们刚才是从manage.leyou.com去访问api.leyou.com,这属于二级域名不同,跨域了。

(2)为什么有跨域问题?

跨域不一定会有跨域问题。

因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是于当前页同域名的路径,这能有效的阻止跨站攻击。

因此:跨域问题是针对ajax的一种限制。

但是这却给我们的开发带来了不变,而且在实际生成环境中,肯定会有很多台服务器之间交互,地址和端口都可能不同,怎么办?

(3)解决跨域问题的方案

目前比较常用的跨域解决方案有3种

  • jsonp
    最早的解决方案,利用script标签可以跨域的原理实现。
    限制:

    • 需要服务的支持
    • 只能发起GET请求
  • nginx反向代理
    思路是:利用nginx反向代理把跨域为不跨域,支持各种请求方式缺点:需要在nginx进行额外配置,语义不清晰

  • CORS
    规范化的跨域请求解决方案,安全可靠。

    优势:

    • 在服务端进行控制是否允许跨域,跨域自定义规则
    • 支持各种请求方式

​ 缺点

​ 会产生额外的请求方式

我们这里会采用cors的跨域方案。

(4)cors解决跨域

浏览器会将Ajax请求分为两类,其处理方案稍有差异:简单的请求,特殊请求

a、简单请求

只需要同时满足以下两大条件,就属于简单请求

(1)请求方法是以下三种方法之一

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不可以超过以下几种字段

  • Accept
  • Accept-Language
  • Content-Lanuage
  • Last-Even-ID
  • Content-Type:只限与三个值:application/x-www-form-urlencodedmultipart/ form-datatext/plain

当浏览器发现发现的ajax请求是简单请求时,会在请求头中携带一个字段:Origin


Origin中会指出当前请求属于哪个域(协议+域名+端口)。服务会根据这个值决定是否允许其跨域。

如果服务器允许跨域,需要在返回的响应头中携带下面信息∶

Access-Control-Allow-Origin: http://manage .leyou.com
Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Orgin:可接受的域,是一个具体的域的名或者*,代表任意
  • Access-Control-Allow-Credentials:是否允许携带cookie,默认情况下,cors不会携带cookie,除非这个值是true

注意:
如果跨域请求要想操作cookie,需要满足3个条件:

  • 服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。
  • 浏览器发起ajax需要指定withCredentials 为true
  • 响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名
b、特殊请求

不符合简单请求的条件,会被浏览器判定为特殊请求,,例如请求方式为PUT。

预检请求

特殊请求会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight) .

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。

只有得到肯定答复,浏览器才会发出正式的xMLHttpRequest 请求,否则就报错。

—个“预检"请求的样板;

与简单请求相比,除了Origin以外,多了两个头;

  • Access-Control-Request-Method:接下来会用到的请求方式,比如PUT
  • Access-Control-Request-Headers:会额外用到的头信息

预检请求的响应

服务的收到预检请求,如果许可跨域,会发出响应:


除了Access-Control-Allow-OriginAccess-Control-Allow-Credentials 以外,这里又额外多出了3个头。

Access-Control-Allow-Methods:允许访问的方式
Access-Control-Allow-Headers:允许携带的头
Access-Control-Max-Age:本次许可的有效时长,单位是秒,过期之前的ajax请求就无需再次进行预检了

如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。

(5)实现非常简单

虽然原理比较复杂,但是前面说过:

  • 浏览器端都有浏览器自动完成,我们无需操心
  • 服务端可以通过拦截器统一实现,不必每次都去进行跨域判定的编写。

事实上,SpringMNC已经帮我们写好了CORS的跨域过滤器︰
CorsFilter ,内部已经实现了刚才所讲的判定逻辑,我们直接用就好了。

ly-api-gateway中编写一个配置类,并且注册CorsFilter:

a、创建对应的包结构

b、创建GlobalCorsConfig


package com.leyou.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * 使用CORS,用于解决ajax跨域访问问题
 *
 * SpringMVC 提供的 CorsFilter 跨域过滤器
 */
@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1.添加CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //1) 允许的域,不要写*,否则cookie就无法使用了(将来如果有多个域需要夸则添加多个)
        config.addAllowedOrigin("http://manage.leyou.com");
        config.addAllowedOrigin("http://api.leyou.com");
        //2) 是否发送Cookie信息
        config.setAllowCredentials(true);
        //3) 允许的请求方式
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");以上是关于Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)六(商品分类功能实现)的主要内容,如果未能解决你的问题,请参考以下文章

IDEA SpringBoot 项目打包成jar包

JavaEE 之 SpringBoot

面试-科大讯飞日常实习面试

(超详解)SpringBoot高级部分-自动配置+监听机制+监控+项目部署

Java项目:超市进销存系统设计和实现(java+Springboot+ssm+mysql+jsp+maven)

SpringBoot核心注解应用