阿里云OSS、数据校验和表的分析

品牌表
在这里插入图片描述
新增菜单
在这里插入图片描述

使用逆向生成的 vue 页面
在这里插入图片描述

复制到 product 文件夹下,重启 renren-fast-vue 项目,但是这里只有查询,没有新增和删除,是因为权限的问题
在这里插入图片描述

Ctrl + Shift + F 全局搜索 isAuth
在这里插入图片描述
是在 index.js 中定义是否有权限的,注释掉直接返回 true,一直都有权限

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

在测试就都好使了,但是细节需要完善
在这里插入图片描述
关闭 Eslint 语法检查
在这里插入图片描述

文件上传功能

在这里插入图片描述
参考文档:https://help.aliyun.com/document_detail/31947.html?spm=5176.8465980.0.dexternal.4e701450OZd0NW

创建 bucket,阿里云对象存储-服务端签名后直传
在这里插入图片描述

创建 gulimall-third-party 模块,引入第三方服务

<?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.atguigu.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.SR3</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.atguigu.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>

注册中心 和 服务中心 的地址都要写上,一个不写注册中心都注册不上,端口号改为 3000
在这里插入图片描述
bootstrap.properties

spring.application.name=gulimall-third-party
spring.cloud.nacos.config.namespace=2c95d4e7-2400-49ae-ba0a-a3bd9e470c11
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

spring.cloud.nacos.config.ext-config[0].data-id=oss.yml
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=true

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alicloud:
      access-key: 填写自己的阿里云
      secret-key: 填写自己的阿里云
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
        bucket: gulishangcheng24
#    alicloud:
#    access-key: LTAI5tNhkwhLZWnEK5m2eukX
#    acce
#    oss:
#      endpoint: oss-cn-beijing.aliyuncs.com
  application:
    name: gulimall-third-party

server:
  port: 30000

配置中心新建 third-party 命名空间,填写阿里云 RAM 的配置(之后在配置也可以)
在这里插入图片描述

OssController

@RestController
public class OssController {

    @Resource
    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;

    @RequestMapping("/oss/policy")
    public Map<String, String> 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<String, String> 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<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());
        } finally {
            ossClient.shutdown();
        }
        return respMap;

    }

}

启动服务浏览器输入 http://localhost:30000/oss/policy/ 测试返回数据
在这里插入图片描述
浏览器就可以请求这个接口,然后服务器给浏览器返回这串信息,浏览器带着它以及要上传的文件提交到阿里云服务器,阿里云进行校验并存储上传的内容

接下来配置一下网关,localhost:88/api/thirdparty/oss/policy
在这里插入图片描述
测试成功
在这里插入图片描述

复制 upload 文件夹到 components
在这里插入图片描述
改成自己的阿里云地址
在这里插入图片描述

brand-add-or-update.vue 引入并声明组件
在这里插入图片描述
这个名字根据 components 定义来的
在这里插入图片描述
更改一下后端 policy 方法返回值,从返回 map 改为返回 R 对象,前端要从 data 里面拿数据
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
阿里云仓库配置一下跨域,要不上传文件 403
在这里插入图片描述
在这里插入图片描述

这个图片回显真搞我心态啊,弄了好久就是不回显,后端前端查,弄了半天把前端 upload 文件夹删了从弄一遍就回显了,应该是我前端哪块不小心弄错了

在这里插入图片描述
细节:显示状态按钮在数据库里面存放的是 0 和 1,激活是 1,不激活是 0
在这里插入图片描述
添加商品成功之后这显示的是文本信息,应该显示的是图片,我们要自定义显示,去 elementui 上面找
在这里插入图片描述
这引入 elementui 图片组件的时候报错,应该是 renren-fast-vue 的工程里没有引入这个组件,我们自己手动引入,到 elementui 官网快速上手里面有
在这里插入图片描述
改过之后还是不回显图片,干脆使用原生的 image 组件

基于表单的数据校验

前端表单校验

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

统一异常处理

返回状态码放到 common 模块中

package com.atguigu.common.exception;

public enum BizCodeEnume {

    UNKNOW_EXEPTION(10000,"系统未知异常"),

    VALID_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;
    }
}

异常处理类 Product 模块中

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

    // 处理特定异常
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleVaildException(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(BizCodeEnume.VALID_EXCEPTION.getCode(), BizCodeEnume.VALID_EXCEPTION.getMsg()).put("data", errorMap);
    }

    // 大的异常
    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable) {
        return R.error(BizCodeEnume.UNKNOW_EXEPTION.getCode(), BizCodeEnume.UNKNOW_EXEPTION.getMsg());
    }
}

新增情况下的校验和修改情况下的校验可能不一样 —— 分组校验

/**
 * 品牌
 * 
 * @author renjianbang
 * @email 1115189918@qq.com
 * @date 2021-04-20 09:51:57
 */
@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-显示]
	 */
	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;

}

controller 不同的方法选择不同的分组,根据 spring 提供的注解 @Validated

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

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

        return R.ok();
    }

groups 参数需要传递接口,接口规定了在什么时候触发规则

public interface AddGroup {
}
public interface UpdateGroup {
}

自定义校验

common 模块引入

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
  • 编写自定义的校验注解
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class}) // 校验器
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) // 哪都可以标注
@Retention(RUNTIME)
public @interface ListValue {
    // 使用该属性去Validation.properties中取
    String message() default "{com.atguigu.common.valid.ListValue.message}";

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

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

    // 数组,需要用户自己指定
    int[] vals() default {};
}
  • 编写自定义校验器
public class ListValueConstraintValidator
        implements ConstraintValidator<ListValue,Integer> { //<注解,校验值类型>

    // 存储所有可能的值
    private Set<Integer> set = new HashSet<>();

    @Override // 初始化,你可以获取注解上的内容并进行处理
    public void initialize(ListValue constraintAnnotation) {
        // 获取后端写好的限制 // 这个value就是ListValue里的value,我们写的注解是@ListValue(value={0,1})
        int[] value = constraintAnnotation.vals();
        for (int i : value) {
            set.add(i);
        }
    }

    @Override // 覆写验证逻辑
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        // 看是否在限制的值里
        return  set.contains(value);
    }
}

实体类添加自定义注解

	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	@ListValue(vals = {0, 1}, groups = {AddGroup.class})
	private Integer showStatus;

SPU 和 SKU

基础概念

三级分类
在这里插入图片描述
SPU 和 SKU

SPU:Standard Product Unit(标准化产品单元)是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
iphoneX 是 SPU、MI 8 是 SPU
iphoneX 64G 黑曜石 是 SKU
MI8 8+64G+黑色 是 SKU

SKU:Stock Keeping Unit(库存量单位)即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。SKU 这是对于大型连锁超市DC(配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种产品均对应有唯一的 SKU 号

SKU 是 SPU 更具体的信息

基本属性【规格参数】与销售属性

 属性是以三级分类组织起来的
 规格参数中有些是可以提供检索的
 规格参数也是基本属性,他们具有自己的分组
 属性的分组也是以三级分类组织起来的
 属性名确定的,但是值是每一个商品不同来决定的

每个分类下的商品共享规格参数,与销售属性。只是有些商品不一定要用这个分类下全部的
属性;

属性表: pms_attr

在这里插入图片描述

属性分组表: pms_attr_group

在这里插入图片描述

属性表和属性分组表关联起来的表: pms_attr_attrgroup_relation

在这里插入图片描述

商品属性值表: pms_product_attr_value,,保存了各个属性所对应的属性值和各种 id 什么的

在这里插入图片描述

spu 描述信息表: pms_spu_info,概括商品信息,具体指在上面的那张表,这张表比较笼统

在这里插入图片描述

sku 信息表: pms_sku_info,spu 包含 sku,一个商品到底有多少个 sku 存放在这里面

在这里插入图片描述

sku 销售属性值表:pms_sku_sale_attr_value

在这里插入图片描述

结合数据库的表好好理解下面这两张图,就能清晰很多

【属性分组-规格参数-销售属性-三级分类】关联关系

在这里插入图片描述

SPU-SKU-属性表

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值