2020谷粒商城分布式基础学习文档

谷粒商城分布式基础篇

必看

本文很多的东西都来自这位大佬的文档
我的仓库地址,里面有很多好东西

架构&介绍

2020谷粒商城分布式基础学习文档_第1张图片

2020谷粒商城分布式基础学习文档_第2张图片

环境的搭建

简单的操作

我使用的vagrant是我自己打包好的,可观看该博客自定义box,或者在博客最下面找到云盘地址

  1. 创建模块gulimall-coupon、gulimall-member、gulimall-order、gulimall-product、gulimall-ware

  2. 创建虚拟机(建议使用vagrant),在里面安装docker。并使用docker 运行mysql、redis、nacos。

    • 可以进入gulimall/data/1.基础篇/vagrant 目录,然后在终端执行 vagrant up 就会搭建好环境。
  3. clone 人人开源,完成前后端的搭建,以及逆向生成代码。

    • 克隆这三个人人开源项目
    git clone [email protected]:renrenio/renren-generator.git
    git clone [email protected]:renrenio/renren-fast-vue.git
    git clone [email protected]:renrenio/renren-fast.git
    
    • 把clone 的这三个项目里面的.git 目录删除。
  4. 初始化数据库。

    • gulimall_pms、gulimall_oms、gulimall_ums、gulimall_wms、gulimall_sms
    • 进入gulimall/data/1.基础篇/vagrant 目录,然后在终端执行 bash db_init.sh 就会初始化好数据库环境。
  5. 逆向工程生成 coupon、member、order、product、ware模块的代码。

    • 修改renren-generator 模块里面的application.yml、generator.properties就能根据数据库逆向生成代码。
    • 修改 template/Controller.java.vm,将里面有关shiro 的注解都注释掉,因为,我们不用shiro做权限控制。
    • 逆向生成的代码,需要一些通用的工具类,所以我们创建一个通用模块gulimall-common。
  6. 给coupon、member、order、product、ware模块 配置数据库连接信息、nacos discovery 、 nacos config。

  7. 创建gateway 模块。

    • 规定:统一调用前缀。 api/product…
    • 根据官方文档配置
  8. idea 的配置工具,能一次启动我们配置好的项目。

    2020谷粒商城分布式基础学习文档_第3张图片

    2020谷粒商城分布式基础学习文档_第4张图片

  9. 运行renren-fast

    • 在配置文件中加上nacos 的地址,已经配置好项目名,然后启动即可。

启动renren-fast-vue

  1. 使用vscode 运行renren-fast-vue 项目。

    1. 需要先安装node.js 。这个软件会替我们安装npm 这个包管理工具。

    2. 安装运行步骤

      # 首先把项目文件夹下的package.json里面的node-sass4.9.0改成4.9.2
      $ pwd
      /Users/haitao/Desktop/gulimall/renren-fast-vue
      $ npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
      $ npm install -f
      $ npm run dev
      
    3. 关掉权限检查。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2XvM18wF-1592837435384)(./http://haitaoss-markdown.oss-cn-shenzhen.aliyuncs.com/2020/06/22/image20200622171248088.png)]

      2020谷粒商城分布式基础学习文档_第5张图片

    4. js同源策略

      2020谷粒商城分布式基础学习文档_第6张图片

      解决:在网关添加一个filter

      @Configuration
      public class GulimallCorsConfiguration {
               
      
          @Bean
          public CorsWebFilter corsWebFilter(){
               
              UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
      
              CorsConfiguration corsConfiguration = new CorsConfiguration();
      
              //1、配置跨域
              corsConfiguration.addAllowedHeader("*");
              corsConfiguration.addAllowedMethod("*");
              corsConfiguration.addAllowedOrigin("*");
              corsConfiguration.setAllowCredentials(true);
      
              source.registerCorsConfiguration("/**",corsConfiguration);
              return new CorsWebFilter(source);
          }
      }
      
    5. renren-fast 也配置了同源策略,我们将它删除

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iYlVrTK7-1592837435389)(./http://haitaoss-markdown.oss-cn-shenzhen.aliyuncs.com/2020/06/22/image20200622171933143.png)]

    6. 终于顺利登陆了

      2020谷粒商城分布式基础学习文档_第7张图片

测试使用feign进行远程服务调用

@RestController
public class TestFeignController {
     
    @Autowired
    CouponFeignService couponFeignService;

    @GetMapping("/test")
    public R test() {
     
        Map<String, Object> params = new HashMap<>();
        R list = couponFeignService.list(params);
        return R.ok().put("data", list);

    }
}
//@FeignClient(value = "gulimall-coupon")
// 添加到IOC 容器中,如果当前包与主启动类同包或者子包下。就能被扫描然后加入IOC 容器中
// 为了以防万一,我们可以在主启动类上特定指定一下feign接口的包路径
@FeignClient(value = "gulimall-gateway")
public interface CouponFeignService {
     
    /**
     * 我们给方法传递的参数会转换为json 封装到请求体中。
     * 目标服务想获取我们传递的数据得从请求体中获取,所以得使用 @RequestBody 注解
     */
    @RequestMapping("/api/coupon/coupon/list")
    public R list(@RequestBody Map<String, Object> params);
}

分布式组件的选择

概述

  • 注册中心:nacos
  • 配置中心:nacos
  • 服务调用:openFeign
  • 服务网关:gateway

Springcloud 版本选择

https://spring.io/projects/spring-cloud-alibaba

Spring Cloud Version Spring Cloud Alibaba Version Spring Boot Version
-------- -------- --------
Spring Cloud Greenwich 2.1.x.RELEASE 2.1.x.RELEASE
Spring Cloud Finchley 2.0.x.RELEASE 2.0.x.RELEASE
Spring Cloud Edgware 1.5.x.RELEASE 1.5.x.RELEASE

https://spring.io/projects/spring-cloud

Release Train Boot Version
Hoxton 2.2.x
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x

使用 docker 启动 nacos

$ docker run --name nacos01 -d \
-p 8848:8848 \
--privileged=true \
--restart=always \
-e JVM_XMS=512m \
-e JVM_XMX=2048m \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
nacos/nacos-server:1.1.4

nacos 做注册中心

引入依赖

 <dependency>
   <groupId>com.alibaba.cloudgroupId>
   <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>

编写配置文件将服务注册到nacos中

spring:
  # nacos
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.10:8848
  # 不指定名字无法注册进nacos
  application:
    name: gulimall-coupon

在启动类上加上这个注解开启服务发现的功能

@SpringBootApplication
@EnableDiscoveryClient
public class GulimallCouponApplication {
     

    public static void main(String[] args) {
     
        SpringApplication.run(GulimallCouponApplication.class, args);
    }

}

nacos 做配置中心

引入依赖

 <dependency>
   <groupId>com.alibaba.cloudgroupId>
   <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>

官方使用步骤

  1. 首先,修改 pom.xml 文件,引入 Nacos Config Starter

     
         com.alibaba.cloud
         spring-cloud-starter-alibaba-nacos-config
     
    
  2. 在应用的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config 元数据

     spring.application.name=nacos-config-example
     spring.cloud.nacos.config.server-addr=127.0.0.1:8848
    
  3. 完成上述两步后,应用会从 Nacos Config 中获取相应的配置,并添加在 Spring Environment 的 PropertySources 中。这里我们使用 @Value 注解来将对应的配置注入到 SampleController 的 userName 和 age 字段,并添加 @RefreshScope 打开动态刷新功能。配置的加载:如果配置中心和当前应用的配置文件中都配置了相同的项,优先使用配置中心的配置

     @RefreshScope
     class SampleController {
           
    
     	@Value("${user.name}")
     	String userName;
    
     	@Value("${user.age}")
     	int age;
     }
    

默认读取配置中心的文件

# 这么配置默认找的是 public->DEFAULT->appName.properties 文件(命名空间、组、文件名)
spring.application.name=appName
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

同时加载多个配置集

spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=192.168.1.10:8848
spring.cloud.nacos.config.namespace=b4e817d4-8bdd-4e13-a917-14c8abef6296
# 这个是默认配置文件的加载,默认的DEFAULT ,加载的是组里面的 ${spring.application.name}.properties 文件
spring.cloud.nacos.config.group=prod

# 扩展配置

# 读取 b4e817d4-8bdd-4e13-a917-14c8abef6296->prood->datasource.yml
spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml
spring.cloud.nacos.config.ext-config[0].group=prod
spring.cloud.nacos.config.ext-config[0].refresh=true

# 读取 b4e817d4-8bdd-4e13-a917-14c8abef6296->prood->mybatis.yml
spring.cloud.nacos.config.ext-config[1].data-id=mybatis.yml
spring.cloud.nacos.config.ext-config[1].group=prod
spring.cloud.nacos.config.ext-config[1].refresh=true

# 读取 b4e817d4-8bdd-4e13-a917-14c8abef6296->prood->other.yml
spring.cloud.nacos.config.ext-config[2].data-id=other.yml
spring.cloud.nacos.config.ext-config[2].group=prod
spring.cloud.nacos.config.ext-config[2].refresh=true

老师总结的知识点

2、细节
*  1)、命名空间:配置隔离;
*      默认:public(保留空间);默认新增的所有配置都在public空间。
*      1、开发,测试,生产:利用命名空间来做环境隔离。
*         注意:在bootstrap.properties;配置上,需要使用哪个命名空间下的配置,
*         spring.cloud.nacos.config.namespace=9de62e44-cd2a-4a82-bf5c-95878bd5e871
*      2、每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
*
*  2)、配置集:所有的配置的集合,说白了就是我们在nacos里面写个每一个配置文件。
*
*  3)、配置集ID:类似文件名。
*      Data ID:类似文件名
*
*  4)、配置分组:
*      默认所有的配置集都属于:DEFAULT_GROUP;
*      1111,618,1212
*
* 项目中的使用:每个微服务创建自己的命名空间,使用配置分组区分环境,dev,test,prod
*
* 3、同时加载多个配置集
* 1)、微服务任何配置信息,任何配置文件都可以放在配置中心中
* 2)、只需要在bootstrap.properties说明加载配置中心中哪些配置文件即可
* 3)、@Value,@ConfigurationProperties。。。
* 以前SpringBoot任何方法从配置文件中获取值,都能使用。
* 配置中心有的优先使用配置中心中的,

openFeign 调用服务

概述

  • openFeign 是基于面向接口的实现。我们只需要编写好接口,openFeign就能帮我们创建好接口调用的代理对象,我们直接使用这个代理对象即可。
  • openFeign 集成了RestTemplate,真正帮我们发送rest请求的是RestTemplate。
  • openFeign 还集成了ribbon,也就是具有负载均衡的效果。
  • 注意:将微服务注册到注册中心中(服务的提供方和调用方都要注册)

引入依赖

<dependency>
  <groupId>org.springframework.cloudgroupId>
  <artifactId>spring-cloud-starter-openfeignartifactId>
dependency>

配置文件

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.10:8848
  application:
    name: gulimall-member

编写接口

@FeignClient(name = "gulimall-coupon" )
public interface CouponFeignService {
     
    @GetMapping("/coupon/coupon/member/list" )
    public R memberCoupons();
}

开启feign的功能

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.atguigu.gulimall.member.feign" )
public class GulimallMemberApplication {
     

    public static void main(String[] args) {
     
        SpringApplication.run(GulimallMemberApplication.class, args);
    }

}

远程接口调用的细节

  1. 定义一个接口,这个接口必须添加到IOC容器中。

    • 使用@FeignClient 注解申明这是一个feign 接口

    • 如果当前接口所在的包与主启动类同包或者子包下。就能被扫描然后加入IOC 容器中

    • 但是为了省事,我们最好是在主启动类上特定指定一下feign接口的包路径@EnableFeignClients(basePackages = {"com.atguigu.gulimall.product.feign"})

    • feign 接口远程调用服务过程中,是将数据放到请求体中,所以服务的提供放获取参数的使用必须使用@RequestBody 注解,表示从请求体中获取数据。

gateway

概述

网关的作用:鉴权、限流、日志输出、隐藏微服务

https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.3.RELEASE/reference/html/

  • Route: The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates, and a collection of filters. A route is matched if the aggregate predicate is true.
  • Predicate: This is a Java 8 Function Predicate. The input type is a Spring Framework ServerWebExchange. This lets you match on anything from the HTTP request, such as headers or parameters.
  • Filter: These are instances of Spring Framework GatewayFilter that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request.

断言的编写

filter的编写

gateway:route、predicate、filter

流程:请求到达网关,网关通过断言来判断我们的请求是否符合某个路由规则。如果符合了就按照路由规则路由到指定的地方。到指定地方的过程中我们可以进行过滤,请求的响应结果也会经过过滤。在过滤环节我们可以添加、修改请求体或者响应体的信息。

2020谷粒商城分布式基础学习文档_第8张图片

导入依赖

 <dependency>
   <groupId>org.springframework.cloudgroupId>
   <artifactId>spring-cloud-starter-gatewayartifactId>
dependency>

配置文件

spring:
  cloud:
    # 网关的配置
    gateway:
      routes:
        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?>/?.*), /renren-fast/$\{
     segment}

是什么是同源策略

概述

  • 同源策略是每一个浏览器都必须实现的协议。目的是防止用户被钓鱼。
  • 比如有人做了个钓鱼网站,当你输入账号密码点击登录。他其实不是登录而是通过js代码发送请求用你的钱买东西。
  • 所以随着技术的发展就定义出同源策略,这是浏览器针对js代码的策略。
  • 说白了就是当浏览器知道你这个js发起的http设置到数据安全问题时(post、delete等请求),会先发送一个OPTIONS 请求,根据OPTIONS 请求的响应体内容判断目标服务器是否同意当前网站发送的跨域请求,如果同意,浏览器才会真正的发送js代码发起的请求。

跨域的流程

2020谷粒商城分布式基础学习文档_第9张图片

跨越的解决

2020谷粒商城分布式基础学习文档_第10张图片

2020谷粒商城分布式基础学习文档_第11张图片

选择

  • SpringBoot 已经为我们提供了跨域请求的filter,我们拿来用即可。

    @Configuration
    public class GulimallCorsConfiguration {
           
    
        @Bean
        public CorsWebFilter corsWebFilter(){
           
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    
            CorsConfiguration corsConfiguration = new CorsConfiguration();
    
            //1、配置跨域
            corsConfiguration.addAllowedHeader("*");
            corsConfiguration.addAllowedMethod("*");
            corsConfiguration.addAllowedOrigin("*");
            corsConfiguration.setAllowCredentials(true);
    
            source.registerCorsConfiguration("/**",corsConfiguration);
            return new CorsWebFilter(source);
        }
    }
    

Element UI

tree

文档地址

2020谷粒商城分布式基础学习文档_第12张图片

:data -> v-bind:data // array类型,就是tree数据
:props="defaultProps" // array 里面的每一个元素,主要是确定元素的children 和 节点显示的名字
node-key="catId" // 唯一表示,设置主键字段即可
:expand-on-click-node="false" // 点击的时候不展开,只有点击展开按钮才展开
show-checkbox // 显示复选框
:draggable="draggable" // 是否可以拖拽,这里绑定一个属性
:allow-drop="allowDrop" // 拖拽时判定目标节点能否被放置
@node-drop="handleDrop" // 拖拽成功触发的函数
ref="menuTree"  // 获取tree 里面选中的节点的时候,需要这个tree 的引用
 <el-tree
      :data="menu"
      :props="defaultProps"
      node-key="catId"
      :expand-on-click-node="false"
      :default-expanded-keys="expandedKey"
      show-checkbox
      :draggable="draggable"
      :allow-drop="allowDrop"
      @node-drop="handleDrop"
      ref="menuTree"
    >
  
    <span class="custom-tree-node" slot-scope="{ node, data }">
      <span>{
    { node.label }}span>
      <span>
        <el-button v-if="node.level <=2 " type="text" size="mini" @click="() => append(data)">Appendel-button>
        <el-button
          v-if="data.children.length==0"
          type="text"
          size="mini"
          @click="() => remove(node, data)"
        >Deleteel-button>
      span>
    span>
  el-tree>

Attributes

参数 说明 类型 可选值 默认值
data 展示数据 array
node-key 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 String
props 配置选项,具体看下表 object
show-checkbox 节点是否可被选择 boolean false
default-checked-keys 默认勾选的节点的 key 的数组 array
draggable 是否开启拖拽节点功能 boolean false
allow-drop 拖拽时判定目标节点能否被放置。type 参数有三种情况:‘prev’、‘inner’ 和 ‘next’,分别表示放置在目标节点前、插入至目标节点和放置在目标节点后 Function(draggingNode, dropNode, type)

props

参数 说明 类型 可选值 默认值
label 指定节点标签为节点对象的某个属性值 string, function(data, node)
children 指定子树为节点对象的某个属性值 string

Method

Tree 内部使用了 Node 类型的对象来包装用户传入的数据,用来保存目前节点的状态。 Tree 拥有如下方法:

方法名 说明 参数
getCheckedKeys 若节点可被选择(即 show-checkboxtrue),则返回目前被选中的节点的 key 所组成的数组 (leafOnly) 接收一个 boolean 类型的参数,若为 true 则仅返回被选中的叶子节点的 keys,默认值为 false

event

node-drop 拖拽成功完成时触发的事件 共四个参数,依次为:被拖拽节点对应的 Node、结束拖拽时最后进入的节点、被拖拽节点的放置位置(before、after、inner)、event

确认消息弹框

Element ui 确认消息弹框https://element.eleme.cn/#/zh-CN/component/message-box

2020谷粒商城分布式基础学习文档_第13张图片

消息提示

消息提示https://element.eleme.cn/#/zh-CN/component/message

2020谷粒商城分布式基础学习文档_第14张图片

对话框

对话框(添加的时候,里面可以放表单)https://element.eleme.cn/#/zh-CN/component/dialog

2020谷粒商城分布式基础学习文档_第15张图片

开关

开关https://element.eleme.cn/#/zh-CN/component/switch

2020谷粒商城分布式基础学习文档_第16张图片

el-switch>

开关触发Events

事件名称 说明 回调参数
change switch 状态发生变化时的回调函数 新状态的值
node-click 节点被点击时的回调 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身。

按钮

按钮https://element.eleme.cn/#/zh-CN/component/button

2020谷粒商城分布式基础学习文档_第17张图片

表单

2020谷粒商城分布式基础学习文档_第18张图片

表格自定义列模板https://element.eleme.cn/#/zh-CN/component/table

<el-table-column prop="showStatus" header-align="center" align="center" label="显示状态">
        <template slot-scope="scope"> // 就是定义变量名字
          <el-switch v-model="scope.row.showStatus" active-color="#13ce66" inactive-color="#ff4949">el-switch>											// 通过变量取值
        template>
      el-table-column>

form 表单自定义校验规则

2020谷粒商城分布式基础学习文档_第19张图片

form 表单级联选择器

文件上传

2020谷粒商城分布式基础学习文档_第20张图片

文件上传https://element.eleme.cn/#/zh-CN/component/upload

导入组件

导入elementui

2020谷粒商城分布式基础学习文档_第21张图片

页面布局

页面布局

2020谷粒商城分布式基础学习文档_第22张图片

2020谷粒商城分布式基础学习文档_第23张图片

文件存储服务

概述

2020谷粒商城分布式基础学习文档_第24张图片

2020谷粒商城分布式基础学习文档_第25张图片

2020谷粒商城分布式基础学习文档_第26张图片

实操

阿里云 oss object storage service (OSS) 对象存储服务

java sdk 参考

简单上传demo

alibaba-oss

OSS 的配置以及有用信息

endpoint的取值:

image-20200622220731678

accessKeyId和accessKeySecret需要创建一个RAM账号:

2020谷粒商城分布式基础学习文档_第27张图片

创建用户完毕后,会得到一个“AccessKey ID”和“AccessKeySecret”,然后复制这两个值到代码的“AccessKey ID”和“AccessKeySecret”。

另外还需要添加访问控制权限:

2020谷粒商城分布式基础学习文档_第28张图片

oss 跨域请求

2020谷粒商城分布式基础学习文档_第29张图片

sdk对接oss

添加依赖包

在Maven项目中加入依赖项(推荐方式)

在 Maven 工程中使用 OSS Java SDK,只需在 pom.xml 中加入相应依赖即可。以 3.8.0 版本为例,在 内加入如下内容:

<dependency>
    <groupId>com.aliyun.ossgroupId>
    <artifactId>aliyun-sdk-ossartifactId>
    <version>3.8.0version>
dependency>

上传文件流

以下代码用于上传文件流:

// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
// 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
String accessKeyId = "";
String accessKeySecret = "";

// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

// 上传文件流。
InputStream inputStream = new FileInputStream("");
ossClient.putObject("", "", inputStream);

// 关闭OSSClient。
ossClient.shutdown();

使用alibaba oss start 对接oss

更为简单的使用方式,是使用SpringCloud Alibaba

2020谷粒商城分布式基础学习文档_第30张图片

详细使用方法,见: https://help.aliyun.com/knowledge_detail/108650.html

(1)添加依赖

        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alicloud-ossartifactId>
            <version>2.2.0.RELEASEversion>
        dependency>

(2)创建“AccessKey ID”和“AccessKeySecret”

(3)配置key,secret和endpoint相关信息

      access-key: LTAI4G4W1RA4JXz2QhoDwHhi
      secret-key: R99lmDOJumF2x43ZBKT259Qpe70Oxw
      oss:
        endpoint: oss-cn-shanghai.aliyuncs.com

(4)注入OSSClient并进行文件上传下载等操作

2020谷粒商城分布式基础学习文档_第31张图片

但是这样来做还是比较麻烦,如果以后的上传任务都交给gulimall-product来完成,显然耦合度高。最好单独新建一个Module来完成文件上传任务。

微服务方式对接oss

1)新建gulimall-third-party

2)添加依赖,将原来gulimall-common中的“spring-cloud-starter-alicloud-oss”依赖移动到该项目中

        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alicloud-ossartifactId>
            <version>2.2.0.RELEASEversion>
        dependency>

        <dependency>
            <groupId>com.bigdata.gulimallgroupId>
            <artifactId>gulimall-commonartifactId>
            <version>1.0-SNAPSHOTversion>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidougroupId>
                    <artifactId>mybatis-plus-boot-starterartifactId>
                exclusion>
            exclusions>
        dependency>

另外也需要在“pom.xml”文件中,添加如下的依赖管理

<dependencyManagement>

        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>${spring-cloud.version}version>
                <type>pomtype>
                <scope>importscope>
            dependency>
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-alibaba-dependenciesartifactId>
                <version>2.2.1.RELEASEversion>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

3)在主启动类中开启服务的注册和发现

@EnableDiscoveryClient

OSS 签名后直传

背景

采用JavaScript客户端直接签名(参见JavaScript客户端签名直传)时,AccessKeyID和AcessKeySecret会暴露在前端页面,因此存在严重的安全隐患。因此,OSS提供了服务端签名后直传的方案。

原理

2020谷粒商城分布式基础学习文档_第32张图片

服务端签名后直传的原理如下:

  1. 用户发送上传Policy请求到应用服务器。
  2. 应用服务器返回上传Policy和签名给用户。
  3. 用户直接上传数据到OSS。

阿里云对象存储一服务端签名后直传

JSR303校验

步骤1:使用校验注解

在Java中提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints”包中,提供了如@Email,@NotNull等注解。

在非空处理方式上提供了@NotNull,@Blank和@

(1)@NotNull

The annotated element must not be null. Accepts any type.
注解元素禁止为null,能够接收任何类型

(2)@NotEmpty

the annotated element must not be null nor empty.

该注解修饰的字段不能为null或""

Supported types are:

支持以下几种类型

CharSequence (length of character sequence is evaluated)

字符序列(字符序列长度的计算)

Collection (collection size is evaluated)
集合长度的计算

Map (map size is evaluated)
map长度的计算

Array (array length is evaluated)
数组长度的计算

(3)@NotBlank

The annotated element must not be null and must contain at least one non-whitespace character. Accepts CharSequence.
该注解不能为null,并且至少包含一个非空白字符。接收字符序列。

步骤2:在请求方法种,使用校验注解@Valid,开启校验,

    @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand){
     
		brandService.save(brand);

        return R.ok();
    }

想要自定义错误消息,可以覆盖默认的错误提示信息,如@NotBlank的默认message是

public @interface NotBlank {
     

	String message() default "{javax.validation.constraints.NotBlank.message}";

可以在添加注解的时候,修改message:

	@NotBlank(message = "品牌名必须非空")
	private String name;

步骤3:给校验的Bean后,紧跟一个BindResult,就可以获取到校验的结果。拿到校验的结果,就可以自定义的封装。

 @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
     
        if( result.hasErrors()){
     
            Map<String,String> map=new HashMap<>();
            //1.获取错误的校验结果
            result.getFieldErrors().forEach((item)->{
     
                //获取发生错误时的message
                String message = item.getDefaultMessage();
                //获取发生错误的字段
                String field = item.getField();
                map.put(field,message);
            });
            return R.error(400,"提交的数据不合法").put("data",map);
        }else {
     

        }
		brandService.save(brand);

        return R.ok();
    }

这种是针对于该请求设置了一个内容校验,如果针对于每个请求都单独进行配置,显然不是太合适,实际上可以统一的对于异常进行处理。

步骤4:统一异常处理

可以使用SpringMvc所提供的@ControllerAdvice,通过“basePackages”能够说明处理哪些路径下的异常。

(1)抽取一个异常处理类

package com.bigdata.gulimall.product.exception;

import com.bigdata.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * 集中处理所有异常
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.bigdata.gulimall.product.controller")
public class GulimallExceptionAdvice {
     


    @ExceptionHandler(value = Exception.class)
    public R handleValidException(MethodArgumentNotValidException exception){
     
        Map<String,String> map=new HashMap<>();
        BindingResult bindingResult = exception.getBindingResult();
        bindingResult.getFieldErrors().forEach(fieldError -> {
     
            String message = fieldError.getDefaultMessage();
            String field = fieldError.getField();
            map.put(field,message);
        });

        log.error("数据校验出现问题{},异常类型{}",exception.getMessage(),exception.getClass());
        return R.error(400,"数据校验出现问题").put("data",map);
    }

}

(2)默认异常处理

   @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){
     
        log.error("未知异常{},异常类型{}",throwable.getMessage(),throwable.getClass());
        return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),BizCodeEnum.UNKNOW_EXEPTION.getMsg());
    }

分组校验功能(完成多场景的复杂校验)

1、给校验注解,标注上groups,指定什么情况下才需要进行校验

如:指定在更新和添加的时候,都需要进行校验

	@NotEmpty
	@NotBlank(message = "品牌名必须非空",groups = {
     UpdateGroup.class,AddGroup.class})
	private String name;

在这种情况下,没有指定分组的校验注解,默认是不起作用的。想要起作用就必须要加groups。

2、业务方法参数上使用@Validated注解

@Validated的value方法:

Specify one or more validation groups to apply to the validation step kicked off by this annotation.
指定一个或多个验证组以应用于此注释启动的验证步骤。

JSR-303 defines validation groups as custom annotations which an application declares for the sole purpose of using
them as type-safe group arguments, as implemented in SpringValidatorAdapter.

JSR-303 将验证组定义为自定义注释,应用程序声明的唯一目的是将它们用作类型安全组参数,如 SpringValidatorAdapter 中实现的那样。

Other SmartValidator implementations may support class arguments in other ways as well.

其他SmartValidator 实现也可以以其他方式支持类参数。

3、默认情况下,在分组校验情况下,没有指定指定分组的校验注解,将不会生效,它只会在不分组的情况下生效。

自定义校验功能

1、编写一个自定义的校验注解

@Documented
@Constraint(validatedBy = {
      ListValueConstraintValidator.class})
@Target({
      METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
     
    String message() default "{com.bigdata.common.valid.ListValue.message}";

    Class<?>[] groups() default {
      };

    Class<? extends Payload>[] payload() default {
      };

    int[] value() default {
     };
}

2、编写一个自定义的校验器

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
     
    private Set<Integer> set=new HashSet<>();
    @Override
    public void initialize(ListValue constraintAnnotation) {
     
        int[] value = constraintAnnotation.value();
        for (int i : value) {
     
            set.add(i);
        }

    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
     


        return  set.contains(value);
    }
}

3、关联自定义的校验器和自定义的校验注解

@Constraint(validatedBy = {
      ListValueConstraintValidator.class})

4、使用实例

	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	@ListValue(value = {
     0,1},groups ={
     AddGroup.class})
	private Integer showStatus;

开发规范-模型包名的定义

  • **VO(View Object):**视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
  • TO (Transfer Object):数据传输对象,不同应用程序之间传输的对象。
  • **DTO(Data Transfer Object):**数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
  • **DO(Domain Object):**领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
  • **PO(Persistent Object):**持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。

电商项目的核心概念

SPU:Standard Product Unit (标准化产品单元)

  • 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
  • 聚合了商品所有的特色信息。

SKU: Stock Keeping Unit (库存量单位)

  • 即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。SKU这是对于大型连锁超市DC (配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号

例子

  • iphoneX是SPU、MI8是SPU

  • iphoneX 64G黑曜石是 SKU

  • MI8 8+64G+黑色是 SKU

基本属性【规格参数】与销售属性

每个分类下的商品共享规格参数,与销售属性。只是有些商品不一定要用这个分类下全部的
属性;SPU 决定规格参数,SKU 决定销售属性

能完全决定库存与售价的叫sku(销售属性)。描述商品的是spu(规格参数).

同一个分类下的商品,spu的名字是相同的,只不过对应的值不同,有些商品可以没有值。

sku 包含 spu

spu 不包含 sku,而是所属与 sku

  • 属性是以三级分类组织起来的(商品分类,属性组,属性)
  • 规格参数中有些是可以提供检索的
  • 规格参数也是基本属性,他们具有自己的分组
  • 属性的分组也是以三级分类组织起来的(商品分类,属性分组。)
  • 属性名是确定的,但是值是每一个商品不同来决定的

2020谷粒商城分布式基础学习文档_第33张图片

2020谷粒商城分布式基础学习文档_第34张图片

商品管理系统数据库的理解

# 商品的分类与品牌(多对多)
pms_category
pms_brand
pms_category_brand_relation

# 属性:基本属性,销售属性,既是基本属性又是销售属性
# 属性组 (属性和属性组是多对多关系)
# 属性值
pms_attr (根据attr_type 区分是什么属性,里面定义了一个属性可能的所有值)
pms_attr_group
pms_attr_attrgroup_relation
pms_product_attr_value(因为每个产品的属性值不一样,所以单独抽出一个商品属性值表。)

# 商品的规格参数
pms_spu_info
pms_spu_info_desc
pms_spu_images
pms_spu_comment


# 真正到手的商品,sku 是包含spu的。
pms_sku_info
pms_sku_images
pms_sku_sale_attr_value
pms_comment_replay

jackson 注解

@JsonInclude(JsonInclude.Include.NON_EMPTY) // 不为 "" 或者 null 才会转换为json
private List<CategoryEntity> children;

错误

P83 报错

2020谷粒商城分布式基础学习文档_第35张图片

关于pubsub、publish报错,无法发送查询品牌信息的请求:
1、npm install --save pubsub-js
2、在src下的main.js中引用:
① import PubSub from ‘pubsub-js’
② Vue.prototype.PubSub = PubSub

p100报错

P100点击规格找不到页面,解决:在数据库gulimall_admin执行以下sql再刷新页面即可:【INSERT INTO sys_menu (menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (76, 37, ‘规格维护’, ‘product/attrupdate’, ‘’, 2, ‘log’, 0);】

分布式基础篇总结

1、分布式基础概念

  • 微服务、注册中心、配置中心、远程调用、Feign、网关

2、基础开发

  • SpringBoot2.0、SpringCloud、Mybatis-Plus、Vue组件化、阿里云对象存储

3、环境

  • Vagrant、Linux、Docker、MySQL、Redis、逆向工程&人人开源

4、开发规范

  • 数据校验JSR303、全局异常处理、全局统一返回、全局跨域处理

  • 数据为null 就不封装成json,发给客户端。

    @JsonInclude(JsonInclude.Include.NON_EMPTY) // 不为 "" 或者 null 才会转换为json
    private List<CategoryEntity> children;
    
  • 枚举状态、业务状态码、VO与TO与PO划分、逻辑删除

  • Lombok:@Data、@Slf4j

你可能感兴趣的:(JAVAEE学习)