[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]

一、逆向生成前端代码

1.创建品牌管理菜单

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第1张图片

2.前端逆向代码

将逆向生成的前端代码拷贝到category目录下

3.关闭语法检查

You may use special comments to disable some warnings.
Use // eslint-disable-next-line to ignore the next line.
Use /* eslint-disable */ to ignore all warnings in a file.

前端控制台会一直报错,是由于语法检查过于严格,可以关闭语法检查

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第2张图片

二、调整状态显示按钮

1.使用自定义列模板

通过 Scoped slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)的数据

      
        
      

2.修改权限

暂时将权限改成所有可用

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第3张图片

3.updateBrandStatus方法编写

@change=“updateBrandStatus(scope.row)”

    //更新品牌状态
    updateBrandStatus(data) {
      console.log("数据", data);
      let { brandId, showStatus } = data;
      this.$http({
        url: this.$http.adornUrl("/product/brand/update/status"),
        method: "post",
        data: this.$http.adornData({ brandId, showStatus }, false)
      }).then(({ data }) => {
        //消息提示删除成功
        this.$message({
          message: "修改状态成功!",
          type: "success"
        });
      });
    },

三、对象存储OSS文件上传

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

1.开通OSS

https://www.aliyun.com/

2.创建Bucket

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第4张图片

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第5张图片

3.文件上传

1.创建子账户

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第6张图片

2.添加OSS权限

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第7张图片

3.简单上传

导入依赖

<dependency>
    <groupId>com.aliyun.ossgroupId>
    <artifactId>aliyun-sdk-ossartifactId>
    <version>3.10.2version>
dependency>
@Test
public void upload() throws FileNotFoundException {
     
    // Endpoint以北京为例,其它Region请按实际情况填写。在bucket的概览里
    String endpoint = "oss-cn-beijing.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();

    //上传成功
    System.out.println("上传成功");

}

4.使用starter直传

修改Pom

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

Yml配置账号密码

代码:

    @AutoWired
    private OSS ossClient;

@Test
public void upload() throws FileNotFoundException {
     
   
    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

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

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

    //上传成功
    System.out.println("上传成功");

}

5.服务端签名后直传

将oss文件存储服务放入一个第三方maven模块中

配置bootstrap

spring:
  application:
    name: gulimall-third-party
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        namespace: df45e354-ebe2-4109-bd34-0a18ba549d43
        ext-config:
          - dataId: oss.yml
            group: DEFAULT_GROUP
            refresh: true

配置远程config oss.yml

spring:
  config:
    name: gulimall-third-party
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alicloud:
      access-key: xxxxxxxxxxxxxxxxxxxx
      secret-key: xxxxxxxxxxxxxxxxxxxxxx
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
        bucket: gulimall-xiaoxiao
server:
  port: 30000

配置Controller

@RestController
@RequestMapping("thirdparty/oss")
public class OssController {
     

    @Autowired
    private OSS ossClient;
    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;
    @Value("${spring.cloud.alicloud.secret-key}")
    private String accessKey;
    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;
    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;



    @RequestMapping("/policy")
    public R policy() {
     
        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
//        String callbackUrl = "http://88.88.88.88:8888";
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = format+"/"; // 用户上传文件时指定的前缀。

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey);
        Map<String, String> respMap = new LinkedHashMap<String, String>();
        try {
     
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            // PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
            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.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());
        } finally {
     
            ossClient.shutdown();
        }
        return R.ok().put("data",respMap);
    }
}

网关断言

gateway:
  routes:
    - id: thirdparty_route
      uri: lb://gulimall-third-party
      predicates:
        - Path=/api/thirdparty/**
      filters:
        - RewritePath=/api/(?>.*), /$\{
     segment}

6.前端组件配置

获取外网访问oss域名

在这里插入图片描述

/components下新建一个组件upload

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第8张图片

multiUpload.vue







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",
            params: http.adornParams({
     })
        }).then(({
      data }) => {
     
            resolve(data);
        })
    });
}

singleUpload.vue







7.前端引入文件上传

1.导入singleUpload

<script>
import SingleUpload from "@/components/upload/singleUpload";
export default {
  components:{SingleUpload},

2.修改标签体

      <el-form-item label="品牌logo地址" prop="logo">
        
        <single-upload v-model="dataForm.logo">single-upload>
      el-form-item>

8.开启跨域允许

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第9张图片

9.自定义显示为图片

      <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="fill">el-image>
        template>
      el-table-column>

四、表单校验

1.前端自定义校验

        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();
              }
            }
          }
        ],
        sort: [
          {
            validator: (rule, value, callback) => {
              if (value == "") {
                callback(new Error("排序不能为空"));
              } else if (!Number.isInteger(value) || value < 0) {
                callback(new Error("必须输入一个大于等于0的数字"));
              } else {
                callback();
              }
            }
          }
        ]
      }
    };
  }

2.后端JSR303校验

1.给Entity添加注解

给属性添加校验注解,并附上自己的message提示

@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
	private String logo;
	/**
	 * 介绍
	 */
	@NotEmpty
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	@NotNull
	private Integer showStatus;
	/**
	 * 检索首字母
	 */
	@NotEmpty
	@Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须为a-zA-Z的一个字母")
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull
	@Min(value = 0,message = "排序数字必须大于等于0")
	private Integer sort;

}

2.在接收的Requestbody添加@Valid注解

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

3.添加BindingResult

    /**
     * 保存
     */
    @RequestMapping("/save")
    //@RequiresPermissions("product:brand:save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result) {
     
        if (result.hasErrors()) {
     
            Map<String, String> map = new HashMap<>();//保存错误信息
            result.getFieldErrors().forEach((error) -> {
     
                String field = error.getField();
                String eMessage = error.getDefaultMessage();
                map.put(field, eMessage);
            });
            return R.ok().put("error", map);
        } else {
     
            brandService.save(brand);
            return R.ok();
        }
    }

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第10张图片

4.校验异常统一处理

所有后台请求只做正确处理,异常直接抛出

@Slf4j
@RestControllerAdvice(basePackages = "com.xiaoxiao.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
     

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handlerValidException(MethodArgumentNotValidException e) {
     
        log.error("错误异常"+e.getMessage().getClass());
        return R.error(400,"数据校验异常");
    }
    @ExceptionHandler(value = Throwable.class)
    public R handlerException(Throwable t) {
     
        return R.error(400,"系统未知异常");
    }
}

使用枚举列出常用异常

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

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

    public int getCode() {
     
        return code;
    }

    public String getMessage() {
     
        return message;
    }
}

使用枚举异常

@Slf4j
@RestControllerAdvice(basePackages = "com.xiaoxiao.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
     

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handlerValidException(MethodArgumentNotValidException e) {
     
        log.error("错误异常"+e.getClass());
        Map<String,String> map = new HashMap<>();
        e.getBindingResult().getFieldErrors().forEach((error)->{
     
            String field = error.getField();
            String defaultMessage = error.getDefaultMessage();
            map.put(field,defaultMessage);
        });
        return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(),BizCodeEnum.VALID_EXCEPTION.getMessage()).put("errors",map);
    }
    @ExceptionHandler(value = Throwable.class)
    public R handlerException(Throwable t) {
     
        return R.error(BizCodeEnum.UNKNOWN_EXCEPTION.getCode(),BizCodeEnum.UNKNOWN_EXCEPTION.getMessage());
    }
}

5.分组校验

如果标注了分组校验,没有指定分组,则不进行校验

package com.xiaoxiao.gulimall.product.entity;

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

import java.io.Serializable;
import java.util.Date;

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

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import javax.validation.constraints.Pattern;

/**
 * 品牌
 * 
 * @author xiaoxiao
 * @email [email protected]
 * @date 2020-07-07 20:13:06
 */
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
     
   private static final long serialVersionUID = 1L;

   /**
    * 品牌id
    */
   @TableId
	 @NotNull(message = "必须指定品牌id",groups = {
     UpdateGroup.class,UpdateStatusGroup.class})
   @Null(message = "新增不能指定id",groups = {
     AddGroup.class})
   private Long brandId;
   /**
    * 品牌名
    */
   @NotBlank(groups = {
     AddGroup.class, UpdateGroup.class},message = "品牌名不能为空")
   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-显示]
    */
	 @ListValue(vals={
     0,1},groups = {
     AddGroup.class, UpdateStatusGroup.class})//自定义校验器
	 @NotNull(groups = {
     AddGroup.class, UpdateStatusGroup.class})
   private Integer showStatus;
   /**
    * 检索首字母
    */
   @NotEmpty(groups={
     AddGroup.class})
   @Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须为a-zA-Z的一个字母",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;

}
/**
 * 保存
 */
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Validated(value = {
     AddGroup.class}) @RequestBody BrandEntity brand/*, BindingResult result*/) {
     
    brandService.save(brand);
    return R.ok();
}

/**
 * 修改
 */
@RequestMapping("/update")
//@RequiresPermissions("product:brand:update")
public R update(@Validated(value = {
     UpdateGroup.class}) @RequestBody BrandEntity brand) {
     
    brandService.updateById(brand);

    return R.ok();
}

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

    return R.ok();
}

6.自定义校验

1.引入依赖
    <dependency>
        <groupId>javax.validationgroupId>
        <artifactId>validation-apiartifactId>
        <version>2.0.1.Finalversion>
    dependency>
2.新建一个校验注解
package com.xiaoxiao.common.valid;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

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

/**
 * @author xiaoxiao
 * @date 2020/7/17 - 14:23
 */
@Documented
@Constraint(validatedBy = {
      ListValueConstraintValidator.class })
@Target({
      METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
     
    String message() default "{com.xiaoxiao.common.valid.ListValue.message}";

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

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

    int[] vals() default {
      };
}
3.创建ValidationMessages.properties
com.xiaoxiao.common.valid.ListValue.message=显示状态必须为0或者1
4.创建ListValueConstraintValidator
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
     
    private Set<Integer> set = new HashSet<>();
    @Override
    public void initialize(ListValue constraintAnnotation) {
     
        int[] vals = constraintAnnotation.vals();
        for (int val : vals) {
     
            set.add(val);
        }
    }

    /**
     * 校验方法
     * @param value
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
     

        return set.contains(value);
    }
}

[项目练手笔记-谷粒商城(SpringCloud Alibaba+vue前后端分离)]day08-day09品牌管理(阿里云对象存储OSS上传文件,前后端表单数据校验JSR303)_第11张图片

你可能感兴趣的:(练手开源项目,分布式微服务,学习笔记,谷粒商城,前后端分离,JSR303,前后端数据校验,阿里云OSS)