【谷粒商城基础篇】商品服务开发:品牌管理

在这里插入图片描述

谷粒商城笔记合集

分布式基础篇分布式高级篇高可用集群篇
===简介&环境搭建===
项目简介与分布式概念(第一、二章)
基础环境搭建(第三章)
===整合SpringCloud===
整合SpringCloud、SpringCloud alibaba(第四、五章)
===前端知识===
前端开发基础知识(第六章)
===商品服务开发===
商品服务开发:基础概念、三级分类(第七、八章)
商品服务开发:品牌管理(第九章)
商品服务开发:属性分组、平台属性(第十、十一章)
商品服务:商品维护(第十二、十三章)
===仓储服务开发===
仓储服务:仓库维护(第十四章)
基础篇总结(第十五章)

九、商品服务&品牌管理⚠️

9.1 前端开发:引入组件

注意:组件中使用到的所有API都是逆向工程生成好的

  1. 管理系统前端项目 系统管理-菜单管理 中商品系统目录下新增:品牌管理菜单

    在这里插入图片描述

  2. 逆向工程renren-generator 中生成的前端组件拷贝到 管理系统前端项目renren-fast-vue 中

    在这里插入图片描述

  3. 保存修改,查看组件效果

    在这里插入图片描述

9.2 前后端联调:显示状态按钮优化

在这里插入图片描述

API

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

    return R.ok();
}

前端

  1. src/utils/index.js:注释权限验证

    /**
     * 是否有权限
     * @param {*} key
     */
    export function isAuth (key) {
      // return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false
      return true
    }
    
  2. views/modules/product/brand.vue:优化显示状态按钮

    <template>
      <div class="mod-config">
        <el-table
          :data="dataList"
          border
          v-loading="dataListLoading"
          @selection-change="selectionChangeHandle"
          style="width: 100%;">
          <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" :active-value="1" :inactive-value="0" @change="updateBrandStatus(scope.row)"></el-switch>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </template>
    
  3. views/modules/product/brand-add-or-update.vue:新增对话框优化显示状态按钮

    <template>
      <el-dialog
        :title="!dataForm.brandId ? '新增' : '修改'"
        :close-on-click-modal="false"
        :visible.sync="visible">
        <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="140px">
        <el-form-item label="显示状态" prop="showStatus">
          <!-- :active-value="1" :inactive-value="0" @change="updateBrandStatus(scope.row) -->
          <el-switch v-model="dataForm.showStatus" active-color="#13ce66" inactive-color="#ff4949" :active-value="1" :inactive-value="0"></el-switch>
        </el-form-item>
        </el-form>
      </el-dialog>
    </template>
    
  4. 修改请求URL:/product/brand/update/status

9.3 文件上传分析💡

9.3.1 文件存储方式

在这里插入图片描述

9.3.2 云存储上传方式

1)普通上方式

在这里插入图片描述

2)服务端签名后上传

在这里插入图片描述

9.4 后端开发:第三方服务-OSS⚠️

9.4.1 创建阿里云OSS相关服务

  1. 创建OSS存储桶

    在这里插入图片描述

  2. 创建RAM用户

    在这里插入图片描述

  3. 给创建的RAM用户添加OSS管理权限

    在这里插入图片描述

9.4.2 创建第三方服务模块💡

  1. 创建 第三方服务模块:bilimall-third-party

    在这里插入图片描述

    在这里插入图片描述

  2. 第三方服务 中修改 pom.xml

    <?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>
        <!-- 1、修改 SpringBoot 版本 -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.8.RELEASE</version>
            <relativePath/>
        </parent>
        <!-- 2、修改描述信息 -->
        <description>第三方服务</description>
        <properties>
            <java.version>17</java.version>
            <!-- 3、修改 SpringCloud 版本 -->
            <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
        </properties>
        <dependencies>
            <!-- 4、引入 Spring Cloud Alibaba Oss -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
            </dependency>
            <!-- 5、引入 公共依赖 -->
            <dependency>
                <groupId>cn.lzwei.bilimall</groupId>
                <artifactId>bilimall-common</artifactId>
                <version>0.0.1-SNAPSHOT</version>
                <exclusions>
                    <!-- 6、剔除 MybatisPlus 依赖 -->
                    <exclusion>
                        <groupId>com.baomidou</groupId>
                        <artifactId>mybatis-plus-boot-starter</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
        <dependencyManagement>
            <dependencies>
                <!-- 7、引入 Spring cloud alibaba -->
                <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>
    </project>
    
  3. 第三方服务 中创建配置文件配置nacos注册中心、oss、服务端口:application.yaml

    spring:
      cloud:
        nacos:
          discovery:
            server-addr: 114.132.162.129:8848
        alicloud:
          access-key: LTAI5t9sqxLYEuj8ShZ4km9b
          secret-key: UN7Bl1u9wj4mOK4qzni7YeKc3xgvOr
          oss:
            endpoint: oss-cn-guangzhou.aliyuncs.com
      application:
        name: bilimall-third-party
    server:
      port: 30000
    
  4. nacos服务 中为 第三方服务 创建命名空间:third-party

  5. nacos服务third-party命名空间 中添加 OSS配置文件:oss.yaml

    spring:
      cloud:
        alicloud:
          access-key: LTAI5t9sqxLYEuj8ShZ4km9b
          secret-key: UN7Bl1u9wj4mOK4qzni7YeKc3xgvOr
          oss:
            endpoint: oss-cn-guangzhou.aliyuncs.com
    
  6. 第三方服务 创建配置文件配置nacos配置中心:bootstrap.properties

    spring.application.name=bilimall-third-party
    spring.cloud.nacos.config.server-addr=114.132.162.129:8848
    spring.cloud.nacos.config.namespace=1185ba57-96ae-4b78-ba83-cc4e5f1ae05a
    #命名空间中,默认配置文件的分组:服务名.properties
    spring.cloud.nacos.config.group=dev
    #命名空间中,其他配置集[0]
    spring.cloud.nacos.config.ext-config[0].data-id=oss.yaml
    spring.cloud.nacos.config.ext-config[0].group=dev
    spring.cloud.nacos.config.ext-config[0].refresh=true
    
  7. 第三方服务 的主启动类上开启 服务发现与注册 功能

    @EnableDiscoveryClient
    

9.4.3 API:OSS服务端签名💡

  1. 第三方服务 中创建 cn.lzwei.bilimall.thirdparty.controller.OssController

    package cn.lzwei.bilimall.thirdparty.controller;
    
    @RestController
    public class OssController {
        @Resource
        OSS ossClient;
    
        @Value("${spring.cloud.alicloud.access-key}")
        private String accessId;
        @Value("${spring.cloud.alicloud.oss.endpoint}")
        private String endpoint;
        @Value("${spring.cloud.alicloud.oss.bucket}")
        private String bucket;
    
        @RequestMapping("/oss/policy")
        public R policy(){
            // 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
            String dir = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.now())+"/";
            String host= "https://" + bucket + "." + endpoint;
    
            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("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));
                // respMap.put("expire", formatISO8601Date(expiration));
    
    
            } catch (Exception e) {
                // Assert.fail(e.getMessage());
                System.out.println(e.getMessage());
            }
            return R.ok().put("data",respMap);
        }
    }
    
  2. 第三方服务 中添加配置信息:

    spring:
      cloud:
        alicloud:
          oss:
            bucket: bilimall-20221226
    
  3. 网关服务bilimall-gateway 中添加路由规则:注意规则顺序,越具体的匹配路径优先级越高

    spring:
      cloud:
        gateway:
          routes:
            - id: thir_party_route
              uri: lb://bilimall-third-party
              predicates:
                - Path=/api/thirdparty/**
              filters:
                - RewritePath=/api/thirdparty/?(?<segment>.*),/$\{segment}
    
  4. 启动 第三方服务 ,查看访问效果

    在这里插入图片描述

9.5 前后端联调:文件上传💡

9.5.1 前端开发:文件上传控件

  1. 将文件上传组件的文件夹拷贝到 src/components 目录下

    在这里插入图片描述

  2. 修改 components/upload/singleUpload.vue:修改上传的请求地址为自己 存储桶域名

    <template> 
      <div>
        <el-upload
          action="https://bilimall-20221226.oss-cn-guangzhou.aliyuncs.com"
        </el-upload>
      </div>
    </template>
    
  3. 修改 components/upload/policy.js:删除请求参数的设置

    import http from '@/utils/httpRequest.js'
    export function policy () {
      return new Promise((resolve, reject) => {
        http({
          url: http.adornUrl('/thirdparty/oss/policy'),
          method: 'get'		//删除请求参数
        }).then(({ data }) => {
          resolve(data)
        })
      })
    }
    
  4. 在品牌新增的弹窗组件中引入文件上传组件:views/modules/product/brand-add-or-update.vue

    <template>
      <el-dialog
        :title="!dataForm.brandId ? '新增' : '修改'"
        :close-on-click-modal="false"
        :visible.sync="visible">
        <el-form-item label="品牌logo地址" prop="logo">
          <!-- <el-input v-model="dataForm.logo" placeholder="品牌logo地址"></el-input> -->
          <singleUpload v-model="dataForm.logo"></singleUpload>
        </el-form-item>
      </el-dialog>
    </template>
    
    <script>
      import singleUpload from '@/components/upload/singleUpload'
      export default {
        components: {
          singleUpload
        }
      }
    </script>
    

9.5.2 出现问题:上传阿里云跨域

在这里插入图片描述

9.5.3 问题解决:配置阿里云跨域

  • 在阿里云控制台中对 存储桶bilimall-20221226 进行跨域设置

    在这里插入图片描述

  • 上传成功

    在这里插入图片描述

9.6 API&前端开发:新增、表单图片显示💡

在这里插入图片描述

API

BrandController

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

    return R.ok();
}

前端

  1. 将 views/modules/product/brand.vue 表单品牌logo地址显示更改为图片控件:引入elementui所有组件,使用<el-image>,效果不佳

  2. 将 views/modules/product/brand.vue 表单品牌logo地址显示使用原生 <image>

    <template>
      <div class="mod-config">
        <el-table
          :data="dataList"
          border
          v-loading="dataListLoading"
          @selection-change="selectionChangeHandle"
          style="width: 100%;">
          <el-table-column
            prop="logo"
            header-align="center"
            align="center"
            label="品牌logo地址">
            <template slot-scope="scope">
              <!-- <el-image
                  style="width: 100px; height: 80px"
                  :src="scope.row.logo"
              fit="contain"></el-image> -->
              <img :src="scope.row.logo" style="width: 100px; height: 80px" />
            </template>
          </el-table-column>
        </el-table>
      </div>
    </template>
    
  3. 更改 views/modules/product/brand-add-or-update.vue 的表单校验规则:首字母、排序

    <template>
      <el-dialog
        :title="!dataForm.brandId ? '新增' : '修改'"
        :close-on-click-modal="false"
        :visible.sync="visible">
        <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="140px">
        <el-form-item label="排序" prop="sort">
          <el-input v-model.number="dataForm.sort" placeholder="排序"></el-input>
        </el-form-item>
        </el-form>
      </el-dialog>
    </template>
    
    <script>
      export default {
        data () {
          return {
            dataRule: {
              firstLetter: [
                {
                  validator: (rule, value, callback) => {
                    if (value === '') {
                      callback(new Error('首字母必须填写'))
                    } else if (!/^[a-zA-Z]$/.test(value)) {
                      callback(new Error('首字母必须a-z或者A-Z之间'))
                    } else {
                      callback()
                    }
                  },
                  trigger: 'blur'
                }
              ],
              sort: [
                {
                  validator: (rule, value, callback) => {
                    if (value === '') {
                      callback(new Error('排序字段必须填写'))
                    } else if (!Number.isInteger(value) || value < 0) {
                      callback(new Error('排序必须是一个大于等于0的整数'))
                    } else {
                      callback()
                    }
                  },
                  trigger: 'blur'
                }
              ]
            }
          }
        }
      }
    </script>
    

9.7 后端开发:数据校验&统一异常处理⚠️

9.7.1 API:添加JSR303校验💡

在这里插入图片描述

添加校验,开启校验功能并封装返回信息

  1. 给 BrandEntity 添加校验规则:添加javax.validation.constraints包下的注解 @NotBlank、@URL、@Pattern、@Min@NotEmpty、@NotNull

    @Data
    @TableName("pms_brand")
    public class BrandEntity implements Serializable {
    	private static final long serialVersionUID = 1L;
    	/**
    	 * 品牌id
    	 */
    	@TableId
    	private Long brandId;
    	/**
    	 * 品牌名
    	 */
    	@NotBlank(message = "品牌名称至少包含一个非空字符")
    	private String name;
    	/**
    	 * 品牌logo地址
    	 */
    	@NotEmpty(message = "品牌logo地址")
    	@URL(message = "品牌logo地址必须是标准的url地址")
    	private String logo;
    	/**
    	 * 介绍
    	 */
    	private String descript;
    	/**
    	 * 显示状态[0-不显示;1-显示]
    	 */
    	@NotNull(message = "显示状态必须非空")
    	private Integer showStatus;
    	/**
    	 * 检索首字母
    	 */
    	@NotEmpty(message = "首字母非空")
    	@Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须是a-z或A-Z")
    	private String firstLetter;
    	/**
    	 * 排序
    	 */
    	@NotNull(message = "排序非空")
    	@Min(message = "排序字段必须非空且大于等于0",value = 0)
    	private Integer sort;
    }
    
  2. 在 BrandController 相应API中开启校验功能:添加 @Valid 注解

    效验的bean后紧跟一个 BindingResult 获取到效验的结果

    /**
     * 保存
     */
    @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->{
                String field = item.getField();
                String defaultMessage = item.getDefaultMessage();
                map.put(field,defaultMessage);
            });
            //2.返回收集结果
            return R.error(400,"数据校验失败").put("data",map);
        }else {
            brandService.save(brand);
            return R.ok();
        }
    }
    

9.7.2 统一异常处理:获取校验异常类型

2022-12-27 17:59:36.053 ERROR 21812 --- [io-11000-exec-1] .b.p.e.BilimallExceptionControllerAdvice : 数据校验异常信息...异常类型class org.springframework.web.bind.MethodArgumentNotValidException

这里使用到了 SpringMVC 的注解 @ControllerAdvice

  • 编写异常处理类使用SpringMvc的@ControllerAdvice

  • 使用@ExceptionHandler标记方法可以处理异常

发送请求获取校验异常类型:MethodArgumentNotValidException

  1. 关闭 BrandController 中的校验异常拦截

    /**
     * 保存
     */
    @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->{
    //            String field = item.getField();
    //            String defaultMessage = item.getDefaultMessage();
    //            map.put(field,defaultMessage);
    //        });
    //        //2.返回收集结果
    //        return R.error(400,"数据校验失败").put("data",map);
    //    }else {
    //        brandService.save(brand);
    //        return R.ok();
    //    }
        brandService.save(brand);
        return R.ok();
    }
    
  2. 在 商品服务 中添加统一异常处理类:BilimallExceptionControllerAdvice

    /**
     * 集中处理所有异常
     */
    @Slf4j
    @ResponseBody
    @ControllerAdvice(basePackages = "cn.lzwei.bilimall.product.controller")
    public class BilimallExceptionControllerAdvice {
    
        /**
         * 数据校验失败
         * @param e
         * @return
         */
        @ExceptionHandler(value = Exception.class)
        public R handleVaildException(Exception e){
            log.debug("数据校验异常信息{},异常类型{}",e.getMessage(),e.getClass());//获取校验异常类型
            return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMessage()).put("data",e.getMessage());
        }
    }
    

9.7.3 统一异常处理:优化⚠️

在这里插入图片描述

区分数据校验异常、其他异常

统一异常码、异常信息:后端将定义的错误码写入到开发手册,前端出现对于的错误,就可以通过手册查询到对应的异常

  1. 公共服务 中增加异常状态枚举类:cn.lzwei.common.exception.BizCodeEnume

    /***
     * 错误码和错误信息定义类
     * 1. 错误码定义规则为5为数字
     * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
     * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
     * 错误码列表:
     *  10: 通用
     *      001:参数格式校验
     *  11: 商品
     *  12: 订单
     *  13: 购物车
     *  14: 物流
     */
    public enum BizCodeEnume {
        UNKNOW_EXCEPTION(10000,"系统未知异常"),
        VAILD_EXCEPTION(10001,"参数格式校验失败");
        private Integer code;
        private String message;
        BizCodeEnume(Integer code,String message){
            this.code=code;
            this.message=message;
        }
        public Integer getCode() {
            return code;
        }
        public String getMessage() {
            return message;
        }
    }
    
  2. 商品服务 统一异常处理类 BilimallExceptionControllerAdvice 中进一步优化:区分数据校验异常、其他异常;统一异常码、异常信息

    /**
     * 集中处理所有异常
     */
    @Slf4j
    @RestControllerAdvice(basePackages = "cn.lzwei.bilimall.product.controller")
    public class BilimallExceptionControllerAdvice {
    
        /**
         * 数据校验失败
         * @param e
         * @return
         */
        @ExceptionHandler(value = MethodArgumentNotValidException.class)
        public R handleVaildException(MethodArgumentNotValidException e){
            log.error("数据校验异常信息{},异常类型{}",e.getMessage(),e.getClass());
            Map<String,String> map=new HashMap();
            //1.获取所有校验异常的字段,并进行遍历组装
            BindingResult bindingResult = e.getBindingResult();
            bindingResult.getFieldErrors().forEach(item->{
                String field = item.getField();
                String defaultMessage = item.getDefaultMessage();
                map.put(field,defaultMessage);
            });
            //2.返回收集结果
            return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMessage()).put("data",map);
        }
    
        /**
         * 其他异常
         */
        @ExceptionHandler(value = Throwable.class)
        public R handleException(Throwable t){
            return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMessage());
        }
    }
    

9.7.4 数据校验:分组校验⚠️

注意💡:

API指定分组@Validated(value = {xxx.class}) :@NotNull(groups = {xxx.class}) 生效,其他不生效

API未指定分组 @Valid:@NotNull(groups = {xxx.class})不生效,其他生效

在这里插入图片描述

  1. 公共服务 中新增分组接口:cn.lzwei.common.valid.SaveGroup、UpdateStatusGroup、UpdateGroup

    public interface SaveGroup {
    }
    public interface UpdateStatusGroup {
    }
    public interface UpdateGroup {
    }
    
  2. 商品服务 BrandEntity中添加校验分组

    @Data
    @TableName("pms_brand")
    public class BrandEntity implements Serializable {
    	private static final long serialVersionUID = 1L;
    
    	/**
    	 * 品牌id
    	 */
    	@NotNull(message = "修改时品牌id不能为空",groups = {UpdateGroup.class, UpdateStatusGroup.class})
    	@Null(message = "新增品牌时id必须为空",groups = {SaveGroup.class})
    	@TableId
    	private Long brandId;
    	/**
    	 * 品牌名
    	 */
    	@NotBlank(message = "品牌名称至少包含一个非空字符",groups = {UpdateGroup.class,SaveGroup.class})
    	@Null(message = "修改状态时品牌名必须为空",groups = {UpdateStatusGroup.class})
    	private String name;
    	/**
    	 * 品牌logo地址
    	 */
    	@NotEmpty(message = "品牌logo地址",groups = {UpdateGroup.class,SaveGroup.class})
    	@URL(message = "品牌logo地址必须是标准的url地址",groups = {UpdateGroup.class,SaveGroup.class})
    	@Null(message = "修改状态时品牌logo地址必须为空",groups = {UpdateStatusGroup.class})
    	private String logo;
    	/**
    	 * 介绍
    	 */
    	@Null(message = "修改状态时介绍必须为空",groups = {UpdateStatusGroup.class})
    	private String descript;
    	/**
    	 * 显示状态[0-不显示;1-显示]
    	 */
    	@NotNull(message = "显示状态必须非空",groups = {UpdateGroup.class,SaveGroup.class, UpdateStatusGroup.class})
    	private Integer showStatus;
    	/**
    	 * 检索首字母
    	 */
    	@NotEmpty(message = "首字母非空",groups = {UpdateGroup.class,SaveGroup.class})
    	@Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须是a-z或A-Z",groups = {UpdateGroup.class,SaveGroup.class})
    	@Null(message = "修改状态时首字母必须为空",groups = {UpdateStatusGroup.class})
    	private String firstLetter;
    	/**
    	 * 排序
    	 */
    	@NotNull(message = "排序非空",groups = {UpdateGroup.class,SaveGroup.class})
    	@Min(message = "排序字段必须非空且大于等于0",value = 0,groups = {UpdateGroup.class,SaveGroup.class})
    	@Null(message = "修改状态时排序必须为空",groups = {UpdateStatusGroup.class})
    	private Integer sort;
    }
    
  3. 商品服务 BrandController中添加注解开启分组校验:@Validated

    /**
     * 保存
     */
    @RequestMapping("/save")
    public R save(@Validated(value = {SaveGroup.class}) @RequestBody BrandEntity brand/**,BindingResult result*/){
        brandService.save(brand);
        return R.ok();
    }
    /**
     * 修改
     */
    @RequestMapping("/update")
    public R update(@Validated(value = {UpdateGroup.class}) @RequestBody BrandEntity brand){
    	brandService.updateById(brand);
        return R.ok();
    }
    /**
     * 修改状态
     */
    @RequestMapping("/update/status")
    public R updateStatus(@Validated(value = {UpdateStatusGroup.class}) @RequestBody BrandEntity brand){
        brandService.updateById(brand);
        return R.ok();
    }
    

9.7.5 数据校验:自定义校验⚠️

在这里插入图片描述

  1. 公共服务 中创建自定义校验注解:cn.lzwei.common.valid.@ListValue

    @Constraint(validatedBy = {StatusConstraintValidatorForInteger.class }) //校验器
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    public @interface ListValue {
        String message() default "{cn.lzwei.common.valid.ListValue.message}"; //默认在配置文件中获取消息
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { };
    
        int[] value() default { }; //可接受的值
    }
    
  2. 公共服务 中创建配置文件并配置 自定义注解 的默认消息:ValidationMessages.properties

    cn.lzwei.common.valid.ListValue.message     = 必须使用指定的值
    
  3. 公共服务 中为自定义校验注解创建校验器:cn.lzwei.common.valid.StatusConstraintValidatorForInteger

    /**
     * 自定义校验注解@ListValue 的Integer校验器
     */
    public class StatusConstraintValidatorForInteger implements ConstraintValidator<ListValue,Integer> {
    
        Set<Integer> set=new HashSet<>();
        //初始化:获取可接受的数据集合
        @Override
        public void initialize(ListValue constraintAnnotation) {
            int[] value = constraintAnnotation.value();
            if (value!=null || value.length>0){
                for (int i : value) {
                    set.add(i);
                }
            }
        }
        //判断:值是否可接受
        @Override
        public boolean isValid(Integer value, ConstraintValidatorContext context) {
            return set.contains(value);
        }
    }
    
  4. 商品服务 的 BrandEntity 的显示状态上使用自定义注解

    public class BrandEntity implements Serializable {
    	private static final long serialVersionUID = 1L;
        ...
            
    	/**
    	 * 品牌id
    	 */
    	@NotNull(message = "修改时品牌id不能为空",groups = {UpdateGroup.class, UpdateStatusGroup.class})
    	@Null(message = "新增品牌时id必须为空",groups = {SaveGroup.class})
    	@TableId
    	private Long brandId;
    	/**
    	 * 显示状态[0-不显示;1-显示]
    	 */
    	@ListValue(value={1,0},groups = {UpdateStatusGroup.class})
    	@NotNull(message = "显示状态必须非空",groups = {UpdateGroup.class,SaveGroup.class, UpdateStatusGroup.class})
    	private Integer showStatus;
    }
    

9.8 后端开发:优化 分页&搜索💡

在这里插入图片描述

  1. 分页优化:cn.lzwei.bilimall.product.config.MybatisConfig

    /**
     * 分页查询
     */
    @MapperScan("cn.lzwei.bilimall.product.dao")
    @Configuration
    @EnableTransactionManagement
    public class MybatisConfig {
    
        @Bean
        public PaginationInterceptor paginationInterceptor(){
            PaginationInterceptor paginationInterceptor=new PaginationInterceptor();
            //设置请求的页面大于最大页后操作,true回到首页,false继续请求。默认false
            paginationInterceptor.setOverflow(true);
            //设置最大单页限制数量,默认500条,-1不受限制
            paginationInterceptor.setLimit(1000);
            return paginationInterceptor;
        }
    }
    
  2. 搜索优化:cn.lzwei.bilimall.product.service.impl.BrandServiceImpl

    @Service("brandService")
    public class BrandServiceImpl extends ServiceImpl<BrandDao, BrandEntity> implements BrandService {
    
        @Override
        public PageUtils queryPage(Map<String, Object> params) {
            String key = (String) params.get("key");
            QueryWrapper<BrandEntity> queryWrapper=new QueryWrapper();
            //添加搜索条件
            if(!StringUtils.isNullOrEmpty(key)){
                queryWrapper.eq("brand_id",key).or().like("name",key);
            }
            IPage<BrandEntity> page=  this.page(
                    new Query<BrandEntity>().getPage(params),
                    queryWrapper
            );
            return new PageUtils(page);
        }
    
    }
    

9.9 API:关联分类 查询&新增💡

在这里插入图片描述

在这里插入图片描述

API开发

  1. CategoryBrandRelationController

    @RestController
    @RequestMapping("product/categorybrandrelation")
    public class CategoryBrandRelationController {
        @Autowired
        private CategoryBrandRelationService categoryBrandRelationService;
    
        /**
         * 获取品牌关联的所有分类列表
         */
        @GetMapping("/catelog/list")
        public R cateloglist(@RequestParam(name = "brandId") Long brandId){
            List<CategoryBrandRelationEntity> data=categoryBrandRelationService.list(
                    new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId)
            );
    
            return R.ok().put("data", data);
        }
        /**
         * 保存品牌的分类信息:添加冗余字段 分类名+品牌名
         */
        @RequestMapping("/save")
        public R save(@RequestBody CategoryBrandRelationEntity categoryBrandRelation){
    		categoryBrandRelationService.saveDetail(categoryBrandRelation);
    
            return R.ok();
        }
    }
    
  2. CategoryBrandRelationService

    public interface CategoryBrandRelationService extends IService<CategoryBrandRelationEntity> {
    
        PageUtils queryPage(Map<String, Object> params);
    
        /**
         * 保存品牌的分类信息:添加冗余字段 分类名+品牌名
         */
        void saveDetail(CategoryBrandRelationEntity categoryBrandRelation);
    }
    
  3. CategoryBrandRelationServiceImpl

    @Service("categoryBrandRelationService")
    public class CategoryBrandRelationServiceImpl extends ServiceImpl<CategoryBrandRelationDao, CategoryBrandRelationEntity> implements CategoryBrandRelationService {
    
        @Resource
        BrandService brandService;
        @Resource
        CategoryService categoryService;
    
        /**
         * 保存品牌的分类信息:添加冗余字段 分类名+品牌名
         */
        @Override
        public void saveDetail(CategoryBrandRelationEntity categoryBrandRelation) {
            //1.获取品牌、分类信息
            Long brandId = categoryBrandRelation.getBrandId();
            Long catelogId = categoryBrandRelation.getCatelogId();
            BrandEntity brand = brandService.getById(brandId);
            CategoryEntity category = categoryService.getById(catelogId);
            //2.添加冗余字段 分类名+品牌名
            categoryBrandRelation.setBrandName(brand.getName());
            categoryBrandRelation.setCatelogName(category.getName());
            //3.保存关联信息
            this.save(categoryBrandRelation);
        }
    }
    

9.10 API:修改优化&品牌分类关系表💡

在这里插入图片描述

  1. BrandController:修改品牌,还需同步修改品牌分类关系表

    @RestController
    @RequestMapping("product/brand")
    public class BrandController {
        @Autowired
        private BrandService brandService;
        /**
         * 品牌修改:并更新在其他表中的冗余字段
         */
        @RequestMapping("/update")
        public R update(@Validated(value = {UpdateGroup.class}) @RequestBody BrandEntity brand){
    		brandService.updateDetail(brand);
    
            return R.ok();
        }
    }
    
  2. BrandService:修改品牌,还需同步修改品牌分类关系表

    public interface BrandService extends IService<BrandEntity> {
        /**
         * 品牌修改:并更新在其他表中的冗余字段
         */
        void updateDetail(BrandEntity brand);
    }
    
  3. BrandServiceImpl:修改品牌,还需同步修改品牌分类关系表

    @Service("brandService")
    public class BrandServiceImpl extends ServiceImpl<BrandDao, BrandEntity> implements BrandService {
        @Resource
        CategoryBrandRelationService categoryBrandRelationService;
        /**
         * 品牌修改:并更新在其他表中的冗余字段
         */
        @Transactional
        @Override
        public void updateDetail(BrandEntity brand) {
            this.updateById(brand);
            //1.更新 品牌分类关联表
            Long brandId = brand.getBrandId();
            String name = brand.getName();
            categoryBrandRelationService.updateBrand(brandId,name);
            //TODO 品牌修改:更新在其他表中的冗余字段
        }
    }
    
  4. CategoryBrandRelationService:修改品牌,还需同步修改品牌分类关系表

    public interface CategoryBrandRelationService extends IService<CategoryBrandRelationEntity> {
        /**
         * 品牌修改:并更新在其他表中的冗余字段
         */
        void updateBrand(Long brandId, String name);
    }
    
  5. CategoryBrandRelationServiceImpl:修改品牌,还需同步修改品牌分类关系表

    @Service("categoryBrandRelationService")
    public class CategoryBrandRelationServiceImpl extends ServiceImpl<CategoryBrandRelationDao, CategoryBrandRelationEntity> implements CategoryBrandRelationService {
        /**
         * 品牌修改:并更新在其他表中的冗余字段
         */
        @Override
        public void updateBrand(Long brandId, String name) {
            CategoryBrandRelationEntity categoryBrandRelationEntity = new CategoryBrandRelationEntity();
            categoryBrandRelationEntity.setBrandName(name);
            baseMapper.update(
                    categoryBrandRelationEntity,
                    new UpdateWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId)
            );
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

愿你满腹经纶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值