电商项目——商品服务-API-品牌管理——第十章——上篇

电商项目——初识电商——第一章——上篇
电商项目——分布式基础概念和电商项目微服务架构图,划分图的详解——第二章——上篇
电商项目——电商项目的虚拟机环境搭建_VirtualBox,Vagrant——第三章——上篇
电商项目——Linux虚拟机中安装docker,mysql,redis_VirtualBox——第四章——上篇
电商项目——电商项目的环境搭建_开发工具&环境搭建——第五章——上篇
电商项目——快速开发人人开源搭建后台管理系统&代码生成器逆向工程搭建——第六章——上篇
电商项目——分布式组件(SpringCloud Alibaba,SpringCloud)——第七章——上篇
电商项目——前端基础——第八章——上篇
电商项目——商品服务-API-三级分类——第九章——上篇
电商项目——商品服务-API-品牌管理——第十章——上篇
电商项目——商品服务-API-属性分组——第十一章——上篇
电商项目——商品服务-API-品牌管理——第十二章——上篇
电商项目——商品服务-API-平台属性——第十三章——上篇
电商项目——商品服务-API-新增商品——第十四章——上篇
电商项目——商品服务-API-商品管理——第十五章——上篇
电商项目——商品服务-API-仓库管理——第十六章——上篇

1:使用逆向工程的前后端代码

我们先来看一下mall-product中关于商品表中的信息
在这里插入图片描述
第一步:在人人开源后台管理系统中的菜单管理,点击新增
在这里插入图片描述
第二步:在renren-fast-vue中找到对应的路径product/brand创建brand.vue
我们可以使用代码自动生成器(renren-generator),里的前端代码
在这里插入图片描述
在这里插入图片描述
解决办法:在src/utils/index.js中注释掉如下内容,并返回true

/**
 * 是否有权限
 * @param {*} key
 */
export function isAuth (key) {
  // return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false
  return true;
}

如上完成就可以实现增删改查效果

2:效果优化与快速显示开关

实现:我们要完成在状态字段里显示一个开关按钮,并且状态按钮发生变化的时候就应该给服务器发送请求,修改品牌的状态
在这里插入图片描述
在这里插入图片描述

我们先优化一下逆向生成的品牌管理代码
由于ESLint的语法,太严格,我们删掉如下代码
在这里插入图片描述
第一步:优化开关,如下
在这里插入图片描述
在这里插入图片描述

代码演示

 <el-table-column
        prop="showStatus"
        header-align="center"
        align="center"
        label="显示状态">
<!--        通过 Scoped slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)的数据,-->
        <template slot-scope="scope">
          <el-switch
            v-model="scope.row.showStatus"
            style="display: block"
            :v-model="true"
            active-color="#13ce66"
            inactive-color="#ff4949"
          >
          </el-switch>
        </template>
      </el-table-column>
      
  <el-form-item label="显示状态" prop="showStatus">
        <el-switch
          v-model="dataForm.showStatus"
          style="display: block"
          :v-model="true"
          active-color="#13ce66"
          inactive-color="#ff4949"

        >
        </el-switch>
    </el-form-item>

第二步:监听开关状态,如果状态改变,就应该给服务器发送请求,修改品牌的状态。
我们在Switch开关中找到了change事件,我们使用它来进行监听
switch按钮

 <template slot-scope="scope">
          <el-switch
            v-model="scope.row.showStatus"
            :v-model="true"
            :active-value="1"
            :inactive-value="0"
            @change="updateBrandStatus(scope.row)"
          >
          </el-switch>
        </template>

监听事件的方法

methods: {
      //修改商品的状态
      updateBrandStatus(data){

        console.log("最新状态",data)

        //解构这个对象
        let {brandId,showStatus} =data
        //发送请求进行修改
        this.$http({
          url: this.$http.adornUrl('/product/brand/update'),
          method: 'post',
          params: this.$http.adornParams({
            brandId,
            showStatus
          },false)
        }).then(()=>{
          this.$message({
            type: 'sucess',
            message: '状态更新成功!'
          });
        })

      },
      }

我们可以使用postman测试上面的路径
在这里插入图片描述
在这里插入图片描述

3:云存储开通与使用

接下来,我们来编写文件上传功能,

我们来看下如何将上传的文件进行存储
分为两种情况

  1. 单体应用
  2. 分布式应用
  • 以前开发的是小商城——单体应用,项目就部署在一台服务器上,我们想要做文件上传,如上图,浏览器发送一个请求,叫服务器去保存在项目中的某一个位置,如果是分布式情况,一台服务器不够,我们就得布置多台服务器,还是像以前单体应用一样是不行的,因为我们下一次如果负载均衡到了其他服务器是获取不到文件的,就会有一些问题,我们该怎么做呢?我们可以将上传的文件都存储到统一的文件存储中去,这样任何一个文件都可以在同一一个地方写,同一个地方读,就不会出现自己持有的文件,别人找不到的情况,(文件存储可以自己搭建,也可以用第三方给我们做好的云存储)
  • 推荐使用云存储功能:如今的项目要求快速开发,快速迭代,以及满足弹性成本,所以使用云存储是非常合理的
  • 我们电商系统也最终使用第三方的云存储服务,借此熟悉第三方云存储开通和使用流程
    在这里插入图片描述

3.1 SpringCloud Alibaba-OSS

阿里云对象存储官网

  • 对象存储服务(Oblect Storage Service,O5s)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。

第一次使用阿里云,要进行账号绑定,实名认证等,然后在开通对象存储

  • 存储空间(Bucket)

存储空间是用户用于存储对象(Object)的容器,所有的对象都必须隶属于某个存储空间。存储空间具有各种配置属性,包括地域、访问权限、存储类型等。用户可以根据实际需求,创建不同类型的存储空间来存储不同的数据。
同一个存储空间的内部是扁平的,没有文件系统的目录等概念,所有的对象都直接隶属于其对应的存储空间。
每个用户可以拥有多个存储空间。
存储空间的名称在OSS范围内必须是全局唯一的,一旦创建之后无法修改名称。
存储空间内部的对象数目没有限制。

演示创建一个bucket
在这里插入图片描述
在这里插入图片描述
演示如何上传文件,并访问
在这里插入图片描述

3.2 上传文件到阿里云的方法

我们以后的上传文件,是通过后台界面,点击上传,把图片保存到阿里与的bucket里的,并且拿到这个图片的真正地址,那我们要怎么做呢?

第一种:用户通过浏览器,选择文件上传,把图片先上传给应用服务器(把整个文件上传请求提交给网关,然后在交给商品服务,商品服务拿到图片流了,在使用java代码把图片的流数据,传给对象储存,最终存在bucket里面,并拿到地址)
缺点:我们文件上传还要过到我们的应用服务器里面(完全没必要,服务器会在大量的流量下,带来瓶颈)
优点:我们要上传到阿里云的bucket与我们的账号密码有关,但是这个东西不可以暴露,使用第一种方式的安全自处在于,文件交给我们的服务器,服务器用账号密码上传给阿里云
在这里插入图片描述
第二种:用浏览器直接上传给对象,我们将阿里云的账号密码直接写在js代码里面,让js控制,把表单直接提交给阿里云,风险就是泄露了阿里云的账号密码,所以我们可以使用服务端签名后直传,我们把操作对象的需要的账号密码直接存储在应用服务器里面,浏览器想要给阿里云上传数据,怎么办呢?上传之前先找我们的应用服务器,要到一个policy,服务器利用阿里云的账号密码,生成一个防伪签名(包含访问阿里云的授权令牌,以及我们要上传文件的地址),我们前端拿到这些信息以后,在带着这些信息(没有账号密码,只有防伪签名),以及要上传的文件,交给OSS,OSS验证这些是正确的就成功保存
在这里插入图片描述

4:OSS整合测试

电商项目——如何上传文件到阿里云的OSS中?

5:OSS获取服务端签名

电商项目——如何上传文件到阿里云的OSS中?

6:OSS前后端联调测试上传

第一步:把已经封装好的upload放到指定位置
在这里插入图片描述
在这里插入图片描述
第二步:修改如下的品牌logo地址,为文件上传
在这里插入图片描述
我们要先抽取我们第一步中的组件放到brand-add-or-update.vue中

 //1.导入组件
  import singleUpload from "../../../components/upload/singleUpload";
    export default {
    //2.写入组件中
    components: {singleUpload},
    <!--      //3:引入组件-->
      <single-upload v-model="dataForm.logo"></single-upload>

我们第一次测试上传图片没有成功
在这里插入图片描述
我们寻找问题:看singleUpload.vue
在里面点击上传会触发 :before-upload="beforeUpload"中的beforeUpload事件
beforeUpload方法:
在这里插入图片描述
解决办法:
在这里插入图片描述

//..
    public R policy(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
    //...
                    return R.ok().put("data",respMap);
}

进行上传测试,发现出现跨域问题,我们就要去OSS对象存储中的权限管理进行跨域配置如下
在这里插入图片描述

在这里插入图片描述
再次进行上传测试发现,上传成功
在这里插入图片描述

服务端签名后直传的url分析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7:表单校验与自定义校验器

问题一:
接下来我们调试一下品牌新增功能,我们点击新增可以弹出对话框,新增数据
在这里插入图片描述
解决方法如下:

<el-switch
          :active-value="1"
          :inactive-value="0"
          >
 </el-switch>

问题二:
在这里插入图片描述
解决方法

      <el-table-column
        prop="logo"
        header-align="center"
        align="center"
        label="品牌logo地址">
<!--        通过 Scoped slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)的数据-->
        <template slot-scope="scope">
          <el-image
            style="width: 140px; height: 80px"
            :src="scope.row.logo"
            fit="contain"></el-image>
        </template>
      </el-table-column>

进行测试发现图片没显示出来,检查发现报如下错误
在这里插入图片描述

报错如下
在这里插入图片描述
解决办法:
在这里插入图片描述
问题三:我们再来看一个数据校验的有关问题,如果我们点击新增什么都不填写,就确定就会报如下红线,如下的数据校验都是默认的不可以为空,如果我们乱填该怎么办呢?
在这里插入图片描述
解决办法:我们前端在给后台提交数据时,一定要做一个前端校验(基于表单的数据校验)
页面代码重要的是:rule=dataRule和prop

 data () {

      var firstLetter = (rule, value, callback) => {
        console.log("dddeeeeed")

        if (value === '') {
          callback(new Error('首字母必须填写'));
        } else {
          if (!/^[a-zA-Z]$/.test(value)) {
            callback(new Error('首字母必须在a-z和A-Z中'));
          }else {
            callback();
          }
        }
      };

      // 让排序接受一个数组v-model.number
      var sort = (rule, value, callback) => {
        console.log("dddd")
        if (value === '') {
          callback(new Error('排序字段必须填写'));
        } else if (!number.isInteger(value) || value<0) {
          callback(new Error('排序字段必须是一个大于0的数'));
        } else {
          callback();
        }
      };
      return {
    
        dataRule: {
          name: [
            { required: true, message: '品牌名不能为空', trigger: 'blur' }
          ],
          logo: [
            { required: true, message: '品牌logo地址不能为空', trigger: 'blur' }
          ],
          descript: [
            { required: true, message: '介绍不能为空', trigger: 'blur' }
          ],
          showStatus: [
            { required: true, message: '显示状态[0-不显示;1-显示]不能为空', trigger: 'blur' }
          ],
          firstLetter: [
            { validator: firstLetter, trigger: 'blur' }
          ],
          sort: [
            { validator: sort, trigger: 'blur' }
          ]
        }
      }
    },

有了前端校验我们就可以保证后台管理系统前端页面,传递给服务器的数据是正确的,但是我还要加上服务端校验,因为就算把前端校验好的数据提交给服务器,服务器不校验也会很危险(绕过这个前端项目,知道你会发什么请求,比如postman,这样就会造成很大的危险)
服务端校验是很重要的

8:JSR303数据校验

这一章节我们就完成后端校验,完成新增服务的双版校验
思路:
3: JSR30
1)给bean添加校验注解
2)开启校验功能@Valid
校验错误以后会有默认的响应
3)给校验的bean后紧跟一个BindingResult。就可以获取到检验结果
代码演示
BrandEntity .java

/**
 * 品牌
*/
@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
	@URL(message = "logo必须是一个合法的url地址")
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	private Integer showStatus;
	/**
	 * 检索首字母
	 */
	@NotEmpty

	@Pattern(regexp ="/^[a-zA-Z]$/",message = "检索首字母必须是一个字母")
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull

	@Min(value = 0,message = "排序必须大于等于0")
	private Integer sort;
}

BrandController.java

/**
     * 保存
     */
    @RequestMapping("/save")
   // @RequiresPermissions("product:brand:save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult bindingResult){
        Map<String,String> map=new HashMap<>();

        if (bindingResult.hasErrors()){
            bindingResult.getFieldErrors().forEach((item)->{
                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();

        }

        }

9:统一异常处理

我们以后很多项目中都会用到校验功能,难道我们要在模块在一直写返回的校验结果的处理信息嘛?这样显然太麻烦了,我们可以做统一的异常处理
思路:我们如果不去感知错误的异常处理信息,那么遇到问题就会自动抛出去,抛到哪里呢?
可以抛到我们自定义的exception包下的MallExceptionController类中
MallExceptionController.java

//集中的异常处理器类
@Slf4j
//所有的json数据都要以json的方式返回给前端
//@ResponseBody
//@ControllerAdvice(basePackages = "com.atstudying.mall.product.controller")
@RestControllerAdvice(basePackages = "com.atstudying.mall.product.controller")
public class MallExceptionController {


    //它告诉springmvc,它可以处理什么异常(MethodArgumentNotValidException)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e){

        log.error("数据校验出现问题:{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult result=e.getBindingResult();

        Map<String,String> error=new HashMap<>();
        result.getFieldErrors().forEach((fieldError -> {
            error.put(fieldError.getField(),fieldError.getDefaultMessage());
        }));
        return R.error(400,"数据校验出错").put("data",error);
    }

    //对于异常处理,统一所有的异常用这个感知(如果异常抛出,上面的异常接受不到就会统一用这个异常)
    @ExceptionHandler(value = Throwable.class)
    public R handleValidException(Throwable e){

        log.error("数据校验出现问题:{},异常类型:{}",e.getMessage(),e.getClass());

        return R.error(400,"数据校验出错").put("data",e.getMessage());
    }
}

BrandController.java

/**
     * 保存
     */
    //不去感知错误的异常处理信息,那么遇到问题就会自动抛出去
    @RequestMapping("/save")
   // @RequiresPermissions("product:brand:save")
    public R save(@Valid @RequestBody BrandEntity brand){
        brandService.save(brand);
        return R.ok();
        }

我们看如上的代码,在后来由于业务众多,业务的状态码(400)也众多,所以我们要把业务状态码集中起来,放在mall-common微服务中
系统错误码介绍:

在这里插入图片描述

public enum BigCodeEnume {

    UNKNOW_EXCEPTION(10000,"系统未知异常"),
    VALID_EXCEPTION(10001,"参数校验失败");

    private int code;
    private String msg;

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

    public int getCode(){
        return code;
    }
    public String getMsg(){
        return msg;
    }
}

我们就可以修改上面MallExceptionController类中的返回方法为如下

        return R.error(BigCodeEnume.VALID_EXCEPTION.getCode(),BigCodeEnume.VALID_EXCEPTION.getMsg()).put("data",error);
        return R.error(BigCodeEnume.UNKNOW_EXCEPTION.getCode(),BigCodeEnume.UNKNOW_EXCEPTION.getMsg()).put("data",e.getMessage());

10:JSR303分组校验

思路:
4)分组校验:(多场景分组校验
1)给校验注解标注什么组需要校验(UpdateGroup.class我们要去mall-common中编写一个valid/UpdateGroup.interface)
@NotNull(message = “修改必须指定品牌id”,groups ={UpdateGroup.class} )
2)在控制器中,写上对应校验需要的组 @Validated(value = {UpdateGroup.class})
3)默认没有分组校验的注解,在分组校验的情况下不生效

BrandEntity.java

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
   private static final long serialVersionUID = 1L;

   /**
    * 品牌id
    */
   //    Class<?>[] groups() default {};
   @NotNull(message = "修改必须指定品牌id",groups ={UpdateGroup.class} )
   @Null(message = "新增不可以指定id",groups = {AddGroup.class})
   @TableId
   private Long brandId;
   /**
    * 品牌名
    */
   @NotBlank(message = "品牌名不可以为空",groups = {UpdateGroup.class,AddGroup.class})
   private String name;
   /**
    * 品牌logo地址
    */
   @NotEmpty(groups = {AddGroup.class})
   @URL(message = "logo必须是一个合法的url地址",groups = {AddGroup.class,UpdateGroup.class})
   private String logo;
   /**
    * 介绍
    */
   private String descript;
   /**
    * 显示状态[0-不显示;1-显示]
    */
   private Integer showStatus;
   /**
    * 检索首字母
    */
   @NotEmpty(groups = {UpdateGroup.class,AddGroup.class})

   @Pattern(regexp ="^[a-zA-Z]$",message = "检索首字母必须是一个字母",groups = {UpdateGroup.class,AddGroup.class})
   private String firstLetter;
   /**
    * 排序
    */
   @NotNull(groups = {UpdateGroup.class,AddGroup.class})

   @Min(value = 0,message = "排序必须大于等于0",groups = {UpdateGroup.class,AddGroup.class})
   private Integer sort;

}

控制器
BrandController

    /**
     * 保存
     */
    //不去感知错误的异常处理信息,那么遇到问题就会自动抛出去
    @RequestMapping("/save")
    public R save(@Validated(value = {AddGroup.class}) @RequestBody BrandEntity brand){

        brandService.save(brand);
        return R.ok();
       }

    /**
     * 修改
     *      *      *      * @RequestBody:获取请求体,必须发送post请求(只有post请求才有请求体,get请求没有请求体)
     * springmvc(web)自动将请求体的数据(json),转化为对应的对象
     */
    @RequestMapping("/update")
    public R update(@Validated(value = {UpdateGroup.class}) @RequestBody BrandEntity brand){
		brandService.updateById(brand);

        return R.ok();
    }

在这里插入图片描述

11:JSR303自定义校验注解

JSR303自定义校验注解

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值