大型商城:商品服务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 用来存放第三方服务
@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中不写处理异常的方法,有异常就会抛出,这是用一个异常处理类去接受处理!
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;
}
}
@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
@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等修改为空了!! 待解决楼