大型商城:商品服务API-品牌管理

前端页面

新建菜单  product/brand

品牌管理可以使用之前逆向工程生成的前端代码:brand.vue  brand-add-or-update.vue

 

 

阿里云云存储OSS的使用

java SDK文档:https://help.aliyun.com/document_detail/195870.html

一般使用:springcloud整合了OSS的使用 文档

1.在你的 Spring Boot 项目的 pom.xml 文件中添加依赖 aliyun-oss-spring-boot-starter。

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>aliyun-oss-spring-boot-starter</artifactId>
</dependency>
//我导入这个		
<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
			<version>2.1.0.RELEASE</version>
		</dependency>

2.在 application.properties 中配置 accessKeyId、secretAccessKey 和 region。

// application.properties
alibaba.cloud.access-key=your-ak
alibaba.cloud.secret-key=your-sk
alibaba.cloud.oss.endpoint=***

3.注入 OSSClient 并使用它来上传文件到 OSS 服务器和从 OSS 服务器下载文件

@Service
 public class YourService {
 	@Autowired
 	private OSSClient ossClient;

 	public void saveFile() {
 		// download file to local
 		ossClient.getObject(new GetObjectRequest(bucketName, objectName), new File("pathOfYourLocalFile"));
 	}
 }

实际上传是使用一下方式:

新建一个module     gulimall-third-party  用来存放第三方服务

OssController 用來让客户端获取policy

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

    @RequestMapping("/oss/policy")
    public R polic(){

        // 填写Host地址,格式为https://bucketname.endpoint。
        String host = "https://"+bucket+"."+endpoint;
        // 设置上传回调URL,即回调服务器地址,用于处理应用服务器与OSS之间的通信。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。
        //String callbackUrl = "https://192.168.0.0:8888";
        // 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
        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);
            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);
    }

}

获取后,校验成功,直接通过前端上传到oss

前端代码中:src/components/upload 中有上传组件

文件中直接引入该组件,可实现上传,如singleUpload.vue 是单文件上传

JSR303

1.给bean添加校验注解,并定义信息 :在 javax.validation.constraints 包中有很多注解

比如:@Emiail 必须是一个邮箱   @NotEmpty 不能为空

 

2.controller中添加@Valid 表示需要校验

测试:

3.给校验的bean后紧跟一个BindingResult,就可以获取到校验的结果。然后可以自己进行封装

    /**
     * 保存
     */
    @RequestMapping("/save")

    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
        if (result.hasErrors()){
            Map<String,String> map = new HashMap<>();
            //获取错误的结果
            result.getFieldErrors().forEach((item)->{
                //FieldError 获取错误提示
                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();
    }

 

给品牌logo加上:@NotEmpty  @URL(message = "logo必须是一个合法的URL地址")

检索首字母:@NotEmpty @Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字母必须是一个字母")

排序:@NotNull @Min(value = 0,message = "排序必须大于0")

以后校验会很多,所以统一写一个异常处理类!

controller中不写处理异常的方法,有异常就会抛出,这是用一个异常处理类去接受处理!

在common中定义一个枚举,写明报错代码以及对应的报错信息

package com.tinstu.common.exception;

/**
 * 错误状态码
 *  10 :通用
 *        001:参数格式错误
 *  11:商品
 *  12:订单
 *  13:购物车
 *  14:物流
 */
public enum BizCodeEnume {
    UNKNOW_EXCEPTION(10000,"系统错误"),
    VAILD_EXCEPION(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.ExceptionAdvice

@Slf4j
@RestControllerAdvice(basePackages = "com.tinstu.gulimall.product.controller")
public class ExceptionAdvice {
    //能匹配MethodArgumentNotValidException这个异常,就执行这个方法,匹不上下面还有个Exception.class
    //value =  可以指定多个不同的校验器
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e){
        BindingResult result = e.getBindingResult();
        Map<String,String> errorMap = new HashMap<>();
        //获取错误的结果
        result.getFieldErrors().forEach((item)->{
            //FieldError 获取错误提示
            String message = item.getDefaultMessage();
            //获取错误属性的名字
            String field = item.getField();
            errorMap.put(field,message);
        });
        return R.error(BizCodeEnume.VAILD_EXCEPION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg()).put("data",errorMap);
    }
    
    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }
}

 

4.分组校验

比如品牌id,我们新增不需要带,因为他是自增的,修改的时候必须带!

controller中

5.自定义校验

比如显示状态只能提交0,1,咱就自定义一个校验

commo中创建一个注释@ListValue( vals= { 0, 1 } )

在common中新建一个注解 ListValue

ListValue

@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
    //出现错误,错误信息去哪里取   在validationMessages.properties中
    String message() default "{com.tinstu.common.validator.group.ListValue.message}";
   //支持分组
    Class<?>[] groups() default {};
    //负载信息
    Class<? extends Payload>[] payload() default {};

    int[] vals() default{};
}

 

新建一个ValidationMessages.properties

com.tinstu.common.validator.group.ListValue.message /product/brand/update= you must up value which sugges
折叠标题

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);
        }
    }
    //判断是否校验成功
    // value 需要校验的值
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
        return set.contains(value);
    }
}

 

有一个bug,在后台点击显示状态按钮时,使用/product/brand/update,只提交状态按钮的数据,不可以,可以controller中新增一个方法,用来专门修改数据

TODO:专门写个接口,那么可以通过这个接口,就能吧name等修改为空了!! 待解决楼

阅读剩余
THE END