谷粒商城-分布式基础篇_03
1. 三级分类; 2. 评判管理; 3.SKU & SPU; 4. 其他业务逻辑; 5. 基础篇总结

前言

做谷粒商城,我并不关注业务,但是为了有一个比较完整的体验,我需要把相关业务也写一遍,笔记可能会只做后端代码部分,需要其他更详细的笔记,不建议看此笔记。

完成谷粒商城中的以下内容:

  1. 三级分类
  2. 品牌管理
  3. SKU & SPU(代码省略)
  4. 基础篇的其他业务逻辑(代码省略)
  5. 分布式基础篇总结

三级分类实现

后端

  1. CategotyController

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    package com.imxushuai.gulimall.product.controller;

    import com.imxushuai.common.utils.PageUtils;
    import com.imxushuai.common.utils.R;
    import com.imxushuai.gulimall.product.entity.CategoryEntity;
    import com.imxushuai.gulimall.product.service.CategoryService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;



    /**
    * 商品三级分类
    *
    * @author imxushuai
    * @email imxushuai@gmail.com
    * @date 2021-12-14 11:27:24
    */
    @RestController
    @RequestMapping("product/category")
    public class CategoryController {
    @Autowired
    private CategoryService categoryService;

    @GetMapping("/list/tree")
    public R listWithTree() {
    List<CategoryEntity> tree = categoryService.listWithTree();
    return R.ok().put("data", tree);
    }

    /**
    * 列表
    */
    @RequestMapping("/list")
    public R list(@RequestParam Map<String, Object> params){
    PageUtils page = categoryService.queryPage(params);

    return R.ok().put("page", page);
    }


    /**
    * 信息
    */
    @RequestMapping("/info/{catId}")
    public R info(@PathVariable("catId") Long catId){
    CategoryEntity category = categoryService.getById(catId);

    return R.ok().put("data", category);
    }

    /**
    * 保存
    */
    @RequestMapping("/save")
    public R save(@RequestBody CategoryEntity category){
    categoryService.save(category);

    return R.ok();
    }

    /**
    * 修改
    */
    @RequestMapping("/update")
    public R update(@RequestBody CategoryEntity category){
    categoryService.updateById(category);

    return R.ok();
    }

    /**
    * 修改
    */
    @RequestMapping("/update/sort")
    public R updateSort(@RequestBody List<CategoryEntity> categoryList){
    categoryService.updateBatchById(categoryList);

    return R.ok();
    }

    /**
    * 删除
    */
    @RequestMapping("/delete")
    public R delete(@RequestBody Long[] catIds){
    categoryService.removeBatch(Arrays.asList(catIds));

    return R.ok();
    }

    }
  2. CatewayService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package com.imxushuai.gulimall.product.service;

    import com.baomidou.mybatisplus.extension.service.IService;
    import com.imxushuai.common.utils.PageUtils;
    import com.imxushuai.gulimall.product.entity.CategoryEntity;

    import java.util.List;
    import java.util.Map;

    /**
    * 商品三级分类
    *
    * @author imxushuai
    * @email imxushuai@gmail.com
    * @date 2021-12-14 11:27:24
    */
    public interface CategoryService extends IService<CategoryEntity> {

    PageUtils queryPage(Map<String, Object> params);

    List<CategoryEntity> listWithTree();

    void removeBatch(List<Long> asList);
    }
  3. CatewayServiceImpl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    package com.imxushuai.gulimall.product.service.impl;

    import org.springframework.stereotype.Service;

    import java.util.Comparator;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;

    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.imxushuai.common.utils.PageUtils;
    import com.imxushuai.common.utils.Query;

    import com.imxushuai.gulimall.product.dao.CategoryDao;
    import com.imxushuai.gulimall.product.entity.CategoryEntity;
    import com.imxushuai.gulimall.product.service.CategoryService;


    @Service("categoryService")
    public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @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() {
    // 查询所有的分类
    List<CategoryEntity> categoryList = baseMapper.selectList(null);

    // 递归分类
    List<CategoryEntity> level1List = categoryList.stream()
    .filter(category -> category.getParentCid() == 0)
    .peek(category -> category.setChildren(getChildrenList(category, categoryList)))
    .sorted(Comparator.comparingInt(c -> (c.getSort() == null ? 0 : c.getSort())))
    .collect(Collectors.toList());

    return level1List;
    }

    @Override
    public void removeBatch(List<Long> asList) {
    // TODO 检查菜单, 若被其他分类引用则不允许删除
    baseMapper.deleteBatchIds(asList);
    }

    /**
    * 查询分类的所有子分类
    *
    * @param category 父分类
    * @param categoryList 所有分类列表
    */
    private List<CategoryEntity> getChildrenList(CategoryEntity root, List<CategoryEntity> categoryList) {

    List<CategoryEntity> childrenList = categoryList.stream()
    .filter(category -> category.getParentCid().equals(root.getCatId()))
    .peek(category -> category.setChildren(getChildrenList(category, categoryList)))
    .sorted(Comparator.comparingInt(c -> (c.getSort() == null ? 0 : c.getSort())))
    .collect(Collectors.toList());

    return childrenList;
    }

    }

配置路由网关与路径重写

在网关的routing.yml中新增配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
spring:
cloud:
gateway:
routes:
- id: baidu_route
uri: https://www.baidu.com
predicates:
- Query=url, baidu
- id: qq_route
uri: https://www.qq.com/
predicates:
- Query=url, qq
# gulimall-product的路由
- id: product_route
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
# renren-fast路由配置
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
# 转发/api/** 到 /renren-fast/**
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}

网关跨域配置

gulimall-gateway中添加配置类用于允许跨域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.imxushuai.gulimall.gulimallgateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class GulimallCorsConfiguration {

@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);

source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(source);
}

}

关闭renren-fast中自带的跨域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/

package io.renren.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {
// 注释掉跨域配置
/* @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}*/
}

品牌管理

品牌管理的业务代码基本全部使用人人逆向工程生成的代码,主要记录文件上传部分

第三方服务

集成 Spring Cloud OSS完成图片上传

  1. 创建Module子工程,名称:gulimall-third-party

  2. 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.8.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.imxushuai.gulimall</groupId>
    <artifactId>gulimall-third-party</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gulimall-third-party</name>
    <description>谷粒商城-第三方服务</description>
    <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Greenwich.SR5</spring-cloud.version>
    </properties>
    <dependencies>
    <dependency>
    <groupId>com.imxushuai.gulimall</groupId>
    <artifactId>gulimall-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <exclusions>
    <exclusion>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    </exclusion>
    </exclusions>
    </dependency>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>${spring-cloud.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.1.0.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>

    </project>
  3. 启动类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.imxushuai.gulimall.thirdparty;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

    @EnableDiscoveryClient
    @SpringBootApplication
    public class GulimallThirdPartyApplication {

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

    }
  4. 配置文件

    • application.yml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      server:
      port: 30000
      spring:
      application:
      name: gulimall-third-party
      cloud:
      nacos:
      discovery:
      server-addr: 127.0.0.1:8848
      alicloud:
      access-key: your-access-key
      secret-key: your-secret-key
      oss:
      endpoint: oss-cn-chengdu.aliyuncs.com
      bucket: imxushuai-gulimall
    • bootstrap.yml

      1
      2
      3
      4
      5
      6
      spring:
      cloud:
      nacos:
      config:
      server-addr: 127.0.0.1:8848
      namespace: f15863d6-6739-4543-9aa6-51e9ad55d5af
  5. 修改gulimall-gatewayrouting.yml,配置gulimall-third-party的路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    spring:
    cloud:
    gateway:
    routes:
    - id: third_party_route
    uri: lb://gulimall-third-party
    predicates:
    - Path=/api/thirdparty/**
    filters:
    - RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment}
  6. gulimall-third-party新增API

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    package com.imxushuai.gulimall.thirdparty.controller;

    import com.aliyun.oss.OSS;
    import com.aliyun.oss.common.utils.BinaryUtil;
    import com.aliyun.oss.model.MatchMode;
    import com.aliyun.oss.model.PolicyConditions;
    import com.imxushuai.common.utils.R;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.nio.charset.StandardCharsets;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.LinkedHashMap;
    import java.util.Map;

    @RestController
    public class OssController {

    @Autowired
    private OSS ossClient;

    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;

    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;

    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;
    @Value("${spring.cloud.alicloud.secret-key}")
    private String accessKey;

    @RequestMapping("/oss/policy")
    public R policy() {

    String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint

    String dir = new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + "/"; // 用户上传文件时指定的前缀。

    Map<String, String> respMap = null;
    try {
    long expireTime = 30;
    long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
    Date expiration = new Date(expireEndTime);
    PolicyConditions policyConds = new PolicyConditions();
    policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
    policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

    String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
    byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
    String encodedPolicy = BinaryUtil.toBase64String(binaryData);
    String postSignature = ossClient.calculatePostSignature(postPolicy);

    respMap = new LinkedHashMap<String, String>();
    respMap.put("accessid", accessId);
    respMap.put("policy", encodedPolicy);
    respMap.put("signature", postSignature);
    respMap.put("dir", dir);
    respMap.put("host", host);
    respMap.put("expire", String.valueOf(expireEndTime / 1000));

    } catch (Exception e) {
    // Assert.fail(e.getMessage());
    System.out.println(e.getMessage());
    } finally {
    ossClient.shutdown();
    }
    return R.ok().put("data", respMap);
    }

    }
  7. 测试文件上传

JRS-303校验

JSR-303 是 Java EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是hibernate Validator。

之前写另一篇的文章的时候,也有用到Spring MVC的相关注解进行参数校验,但没这么全面,我重新单开了一篇文章把JRS-303重新记录一下。

文章链接:

  1. 实体类的字段添加注解,表示其字段的校验规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    package com.imxushuai.gulimall.product.entity;

    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;

    import java.io.Serializable;

    import com.imxushuai.common.valid.AddGroup;
    import com.imxushuai.common.valid.ListValue;
    import com.imxushuai.common.valid.UpdateGroup;
    import com.imxushuai.common.valid.UpdateStatusGroup;
    import lombok.Data;
    import org.hibernate.validator.constraints.URL;

    import javax.validation.constraints.*;

    /**
    * 品牌
    *
    * @author leifengyang
    * @email leifengyang@gmail.com
    * @date 2019-10-01 21:08:49
    */
    @Data
    @TableName("pms_brand")
    public class BrandEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
    * 品牌id
    */
    @NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class})
    @Null(message = "新增不能指定id", groups = {AddGroup.class})
    @TableId
    private Long brandId;
    /**
    * 品牌名
    */
    @NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
    private String name;
    /**
    * 品牌logo地址
    */
    @NotBlank(groups = {AddGroup.class})
    @URL(message = "logo必须是一个合法的url地址", groups = {AddGroup.class, UpdateGroup.class})
    private String logo;
    /**
    * 介绍
    */
    private String descript;
    /**
    * 显示状态[0-不显示;1-显示]
    */
    @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
    @ListValue(values = {0, 1}, groups = {AddGroup.class, UpdateStatusGroup.class})
    private Integer showStatus;
    /**
    * 检索首字母
    */
    @NotEmpty(groups = {AddGroup.class})
    @Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是一个字母", groups = {AddGroup.class, UpdateGroup.class})
    private String firstLetter;
    /**
    * 排序
    */
    @NotNull(groups = {AddGroup.class})
    @Min(value = 0, message = "排序必须大于等于0", groups = {AddGroup.class, UpdateGroup.class})
    private Integer sort;

    }
  2. gulimall-common中创建校验分组:AddGroupUpdateGroupUpdateStatusGroup

    • AddGroup

      1
      2
      3
      4
      package com.imxushuai.common.valid;

      public interface AddGroup {
      }
    • UpdateGroup

      1
      2
      3
      4
      package com.imxushuai.common.valid;

      public interface UpdateGroup {
      }
    • UpdateStatusGroup

      1
      2
      3
      4
      package com.imxushuai.common.valid;

      public interface UpdateStatusGroup {
      }
  3. gulimall-common中创建自定义校验注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.imxushuai.common.valid;

    import javax.validation.Constraint;
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;

    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;

    @Documented
    @Constraint(validatedBy = {ListValueConstraintValidator.class})
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    public @interface ListValue {

    String message() default "必须提交指定的值";

    Class[] groups() default {};

    Class[] payload() default {};

    int[] values() default {};

    }
  4. gulimall-common中创建自定义校验器,用于对自定义注解标注的字段进行校验

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package com.imxushuai.common.valid;

    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import java.util.HashSet;
    import java.util.Set;

    public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

    private Set<Integer> set = new HashSet<>();

    @Override
    public void initialize(ListValue constraintAnnotation) {
    int[] values = constraintAnnotation.values();

    for (int value : values) {
    set.add(value);
    }

    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
    // 校验值
    return set.contains(value);
    }
    }
  5. gulimall-productBrandController中的API上添加@Validated注解标注进行参数校验以及所使用的校验分组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /**
    * 保存
    */
    @RequestMapping("/save")
    public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand){
    brandService.save(brand);

    return R.ok();
    }
    /**
    * 修改
    */
    @RequestMapping("/update")
    public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand){
    brandService.updateById(brand);

    return R.ok();
    }
    /**
    * 修改状态
    */
    @RequestMapping("/update/status")
    public R updateStatus(@Validated(UpdateStatusGroup.class) @RequestBody BrandEntity brand){
    brandService.updateById(brand);

    return R.ok();
    }
  6. gulimall-product中新增统一异常处理类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package com.imxushuai.gulimall.product.exception;

    import com.imxushuai.common.exception.BizCodeEnum;
    import com.imxushuai.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.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;

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

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

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e) {
    log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass());
    BindingResult bindingResult = e.getBindingResult();

    Map<String, String> errorMap = new HashMap<>();
    bindingResult.getFieldErrors().forEach((fieldError) -> {
    errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
    });
    return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(), BizCodeEnum.VALID_EXCEPTION.getMsg()).put("data", errorMap);
    }

    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable) {

    log.error("错误:", throwable);
    return R.error(BizCodeEnum.UNKNOW_EXCEPTION.getCode(), BizCodeEnum.UNKNOW_EXCEPTION.getMsg());
    }


    }
  7. gulimall-common中定义统一异常Code与消息的枚举类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package com.imxushuai.common.exception;

    /***
    * 错误码和错误信息定义类
    * 1. 错误码定义规则为5为数字
    * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
    * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
    * 错误码列表:
    * 10: 通用
    * 001:参数格式校验
    * 11: 商品
    * 12: 订单
    * 13: 购物车
    * 14: 物流
    */
    public enum BizCodeEnum {
    UNKNOW_EXCEPTION(10000,"系统未知异常"),
    VALID_EXCEPTION(10001,"参数格式校验失败");

    private int code;
    private String msg;
    BizCodeEnum(int code,String msg){
    this.code = code;
    this.msg = msg;
    }

    public int getCode() {
    return code;
    }

    public String getMsg() {
    return msg;
    }
    }
  8. 重启服务,测试校验是否正常生效

SPU & SKU

此部分的代码太多了,所以就不写笔记了。

详情请参照视频。

分布式基础篇总结

  1. 分布式基础概念

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

  2. 基础开发

    Spring Boot、Spring Cloud、Mybatis-Plus、Vue组件、阿里云存储

  3. 环境

    虚拟机、Linux、Docker、MySQL、Redis、人人开源相关项目

  4. 开发规范

    • 数据校验JSR303、全局异常处理、全局统一返回、全局跨域处理
    • 枚举状态、业务状态码、VO/TP/PO划分、逻辑删除(假删除)
    • Lombok
文章作者: imxushuai
文章链接: https://www.imxushuai.com/2021/12/14/43.谷粒商城-分布式基础-03/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 imxushuai
支付宝打赏
微信打赏