谷粒商城项目(学习笔记五)

谷粒商城项目(学习笔记一)

谷粒商城项目(学习笔记二)

谷粒商城项目(学习笔记三)

谷粒商城项目(学习笔记四)

谷粒商城项目(学习笔记五)

第四章:商品服务——品牌管理

一、逆向工程添加

二、优化页面

1.关掉代码检查

 2.快速显示开关

3.文件OSS上传功能

4.表单的管理

一、逆向工程添加

将renren-fast逆向工程生成的代码放入renren-fast-vue的视图中

brand.vue和brand-add-or-update.vue

CURL开关默认是关的,注销掉按钮显示的判断,默认开启

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

二、优化页面

1.关掉代码检查

在build的webpack.base.conf.js中,注销createLintingRule,重启

谷粒商城项目(学习笔记五)_第1张图片

 2.快速显示开关

1.引入自定义的table表格模板,为brand-add-or-update显示状态添加一个开关

注意:新的mybatis-puls的逻辑删除,Show-Status会出bug,所以改brand的状态是display_status

   
        
      

2.添加改变绑定状态方法

 //改变显示状态
    updateBrandStatus(data) {
      console.log("最新信息", data);
      let { brandId, displayStatus } = data;
      //发送更新信息给后端
      this.$http({
        url: this.$http.adornUrl("/product/brand/update"),
        method: "post",
        data: this.$http.adornData({ brandId, displayStatus }, false)
      }).then(({ data }) => {
        this.$message({
          message: "操作成功",
          type: "success"
        });
      });
    },

 3.修改brand显示状态也为一个开关并且同步显示

      
        
        
      

3.文件OSS上传功能

官方指南安装 - 对象存储 OSS - 阿里云

1.在阿里云OSS新建一个仓库

2.为common中引入spring-cloud-alibaba的oss的sdk

注意:spring-boot和spring-cloud和spring-cloud-alibaba的版本问题

        
        
            com.alibaba.cloud
            spring-cloud-starter-alicloud-oss
            2.2.0.RELEASE
        

3.引入自己角色的key

alibaba:
  cloud:
    access-key: LTAI****************
    secret-key: 5hm*******************
    oss:
      endpoint: oss-cn-beijing.aliyuncs.com

4.在项目中进行测试

@SpringBootTest
public class ProductApplicationTests {

    @Resource
    OSSClient ossClient;

    @Test
    public void testUpload() throws FileNotFoundException {

// 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
        InputStream inputStream = new FileInputStream("D:\\YY\\Pictures\\Saved Pictures\\b.png");
// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。
        ossClient.putObject("gulimall-20211118", "b1.png", inputStream);

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

5.新建一个第三方模组,封装oss服务

1)新建模组 gulimall-third-party

将第三方spring-cloud-starter-alicloud-oss移动到gulimall-third-party中

同时引入gulimall-common时,需要排除mybatis和mysql组件

       
        
            com.alibaba.cloud
            spring-cloud-starter-alicloud-oss
            2.2.0.RELEASE
        

        
            com.yangyan.gulimall
            gulimall-common
            1.0-SNAPSHOT
            
                
                    com.baomidou
                    mybatis-plus-boot-starter
                
                
                    mysql
                    mysql-connector-java
                
            
        

2)配置yaml,并将oss配置到nacos中

server:
  port: 5000
spring:
  application:
    name: gulimall-third-party
  cloud:
    nacos:
      server-addr: localhost:8848
    alicloud:
      access-key: LTAI5t******************
      secret-key: 5hmWDy*************************
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
        bucket: gulima***********

3)新建OssController控制器

注意:ossClient要配置为OSS

@RestController
public class OssController {

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

    @Resource
    OSS ossClient;

    @RequestMapping("/oss/policy")
    public Map 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 + "/"; // 用户上传文件时指定的前缀。
        Map respMap = null;

        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 = new LinkedHashMap();
            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 respMap;
    }
}

4)将访问地址提供给网关

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

5)测试访问

http://localhost:88/api/thirdparty/oss/policyhttp://localhost:88/api/thirdparty/oss/policy6.与前端整合

1)将upload文件夹放入src/components目录下

2)修改action为自己仓库的地址并检查policy.js的addUrl与后端的地址是否一致

action="http://gulimall-20211118.oss-cn-beijing.aliyuncs.com"

3)在brand-add-or-update.vue中导入脚本

注意这里斜杠为"/"而不是"\"

注意添加components

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

4)修改brand-add-or-update品牌logo地址为一个提交文件的按钮

        

5)测试提交

谷粒商城项目(学习笔记五)_第2张图片

4.表单的管理

1.前端logo的回显

自定义显示一个图片

注意如果无法引入图片可能是模板没有,需要手动添加模板

Element - The world's most popular Vue UI framework可以看看官网的快速入门

在src>element-ui>index.js中

        

2.表单前端的校验

1)校验首字母

修改dataRule的校验规则:首字母必须填写,首字母必须是A-Z或者是a-z之间

        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"
          }
        ],

2)校验排序

        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"
          }
        ]

要接收一个数字必须先将接收的内容规定为数字

即改为v-model.number

      
        
      

3.表单后端的校验(jsr303

1)标记校验注解javax.validation.constraints

注意看各个注解可以标注的属性

注意Pattern的正则表达式需要删除左右的两个“/”

@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地址
	 */
	@NotBlank(message = "品牌logo地址不能为空")
	@URL(message = "logo必须是一个合法的url地址")
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	private Integer displayStatus;
	/**
	 * 检索首字母
	 */
	@NotEmpty(message = "首字母不能为空")
	@Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须是一个字母")
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull(message = "排序字段不能为空")
	@Min(value = 0,message = "排序必须大于等于0")
	private Integer sort;

}

2)为controller开启校验

@Valid为开启验证,BindingResult为获得错误信息

    /**
     * 保存
     */
    @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
        if (result.hasErrors()){
            Map map = new HashMap<>();
            //1.获取校验的错误结果
            result.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();
        }
    }

3)统一异常处理

在common中新建com.yangyan.gulimall.product.exception.ExceptionControllerAdvice

将之前的错误判断放过来,并且添加一个错误信息枚举

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

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e) {
        log.error("错误信息", e.getMessage(), e.getClass());

        BindingResult bindingResult = e.getBindingResult();

        Map errorMap =new HashMap<>();

        bindingResult.getFieldErrors().forEach((fieldError -> {
            errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
        }));
        return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
    }

    @ExceptionHandler(value = Exception.class)
    public R handleException(Exception e) {

        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }

}
package com.yangyan.common.exception;

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

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

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

测试成功

谷粒商城项目(学习笔记五)_第3张图片

 4.分组校验

1)在common中抽取分组接口,作为注解,给属性分组

谷粒商城项目(学习笔记五)_第4张图片

	/**
	 * 品牌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;

2)在控制器添加分组

将save的开启注解改为@Validated({AddGroup.class})

    public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){}

加了AddGroup.class的都会进行判断,而不加的则不会判断

3)全部修改后

	/**
	 * 品牌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(message = "品牌logo地址不能为空",groups = {AddGroup.class})
	@URL(message = "logo必须是一个合法的url地址",groups = {AddGroup.class,UpdateGroup.class})
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	private Integer displayStatus;
	/**
	 * 检索首字母
	 */
	@NotEmpty(message = "首字母不能为空",groups = {AddGroup.class})
	@Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须是一个字母",groups = {AddGroup.class,UpdateGroup.class})
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull(message = "排序字段不能为空",groups = {AddGroup.class})
	@Min(value = 0,message = "排序必须大于等于0",groups = {AddGroup.class,UpdateGroup.class})
	private Integer sort;

5.自定义校验

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

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

    Class[] groups() default {};

    Class[] payload() default {};
    int[] vals() ;

}

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

public class ListValueConstraintValidator implements ConstraintValidator {

    private Set 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);
    }
}

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

在校验注解中添加校验器的实现类

@Constraint(validatedBy = {ListValueConstraintValidator.class})

4)添加上注解进行前后端的测试

	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	@ListValue( message = "必须提交指定的值",vals={0,1},groups = {AddGroup.class})
	private Integer displayStatus;

 BilBil视频地址:尚硅谷电商教程《谷粒商城》对标阿里P6/P7,40-60万年薪_哔哩哔哩_bilibili

你可能感兴趣的:(谷粒商城项目,java,spring,intellij-idea)