添加品牌管理菜单
然后再添加生成的前端代码
效果优化和快速显示开关在build/webpack.base.conf.js中将下面代码注释
const createLintingRule = () => ({
// test: /.(js|vue)$/,
// loader: 'eslint-loader',
// enforce: 'pre',
// include: [resolve('src'), resolve('test')],
// options: {
// formatter: require('eslint-friendly-formatter'),
// emitWarning: !config.dev.showEslintErrorsInOverlay
// }
})
在显示状态位置添加一个开关按钮
Element - The world's most popular Vue UI framework
在新增弹窗中也添加一个开关按钮
同时修改按钮的开关状态值
添加状态修改方法
//状态修改方法
updateBrandStatus(data){
console.log("最新信息:",data);
let {brandId, showStatus} = data;
this.$http({
url: this.$http.adornUrl('/product/pmsbrand/update'),
method: 'post',
data: this.$http.adornData({brandId, showStatus}, false)
}).then(({ data }) => {
this.$message({
type: "success",
message: "状态更新成功"
})
});
},
品牌管理-云存储开通与使用
登陆阿里云
阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台
创建一个仓库
使用阿里云对象存储-服务端签名后直传的方式
安装 - 对象存储 OSS - 阿里云
先导入阿里云对象存储依赖
com.aliyun.oss aliyun-sdk-oss3.10.2
然后测试
@Test
public void testUpload(){
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "oss-cn-shanghai.aliyuncs.com";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "LTAI5tMDvxJp87Zb8W5Y3n99";
String accessKeySecret = "rpBvcATeRr9fbfCyniDcOmYCskf1u9";
// 填写Bucket名称,例如examplebucket。
String bucketName = "gulimall-leohams";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String objectName = "lufei.jpg";
// 填写本地文件的完整路径,例如D:\localpath\examplefile.txt。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
String filePath= "C:\Users\gf879\Pictures\Saved Pictures\lufei.jpg";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
InputStream inputStream = new FileInputStream(filePath);
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, inputStream);
System.out.println("文件上传成功");
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
其中endpoint是下面的
accessKeyId和accessKeySecret需要创建子账户
运行
查看是否上传成功
接下来介绍如何使用springcloud-alibaba的整合好的方法去实现文件上传
aliyun-spring-boot/aliyun-spring-boot-samples/aliyun-oss-spring-boot-sample at master · alibaba/aliyun-spring-boot · GitHub
首先接入OSS
在common服务中导入以下依赖,一定要注意排除aliyun-java-sdk-core,重新导入正确的版本
com.alibaba.cloud aliyun-oss-spring-boot-startercom.aliyun aliyun-java-sdk-corecom.aliyun aliyun-java-sdk-core4.5.0
接下来只需要在配置文件中配置accessKeyId, secretAccessKey 和 region
alibaba:
cloud:
access-key: LTAI5tMDvxJp87Zb8W5Y3n99
secret-key: rpBvcATeRr9fbfCyniDcOmYCskf1u9
oss:
endpoint: oss-cn-shanghai.aliyuncs.com
测试类中注入OSSClient
package com.example.gulimall.product;
import com.aliyun.oss.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallProductApplicationTests {
@Autowired
OSSClient ossClient;
@Test
public void contextLoads() {
}
@Test
public void testUpload(){
// // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
// String endpoint = "oss-cn-shanghai.aliyuncs.com";
// // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
// String accessKeyId = "LTAI5tMDvxJp87Zb8W5Y3n99";
// String accessKeySecret = "rpBvcATeRr9fbfCyniDcOmYCskf1u9";
// // 填写Bucket名称,例如examplebucket。
// String bucketName = "gulimall-leohams";
// // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
// String objectName = "mmexport1647348431349.jpg";
// // 填写本地文件的完整路径,例如D:\localpath\examplefile.txt。
// // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
// String filePath= "C:\Users\gf879\Pictures\Saved Pictures\mmexport1647348431349.jpg";
//
// // 创建OSSClient实例。
// OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
InputStream inputStream = new FileInputStream("C:\Users\gf879\Pictures\Saved Pictures\cat1.jpg");
// 创建PutObject请求。
ossClient.putObject("gulimall-leohams", "cat1.jpg", inputStream);
System.out.println("文件上传成功");
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
品牌管理-OSS获取服务端签名
首先创建第三方的服务模块gulimall-third-party
加入common依赖以及OSS依赖,同时修改springcloud的版本为
Greenwich.SR3
4.0.0 org.springframework.boot spring-boot-starter-parent2.1.8.RELEASE com.example.gulimall gulimall-third-party0.0.1-SNAPSHOT gulimall-third-party gulimall-third-party 1.8 Greenwich.SR3 com.example.gulimall gulimall-common0.0.1-SNAPSHOT com.baomidou mybatis-plus-boot-starterorg.springframework.boot spring-boot-starter-weborg.springframework.cloud spring-cloud-starter-openfeignorg.springframework.boot spring-boot-starter-testtest com.alibaba.cloud aliyun-oss-spring-boot-startercom.aliyun aliyun-java-sdk-corecom.aliyun aliyun-java-sdk-core4.5.0 com.alibaba.cloud aliyun-spring-boot-dependencies1.0.0 pom import org.springframework.cloud spring-cloud-dependencies${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin
然后创建bootstrap.yml,进行nacos服务注册
spring.cloud.nacos.config.server-addr=192.168.159.135:8848 spring.cloud.nacos.config.namespace=88ca76b3-82d5-49d0-925b-726d5392e48f
然后在nacos中添加一个命名空间third-party并创建一个配置文件oss.yml
alibaba:
cloud:
access-key: LTAI5tMDvxJp87Zb8W5Y3n99
secret-key: rpBvcATeRr9fbfCyniDcOmYCskf1u9
oss:
endpoint: oss-cn-shanghai.aliyuncs.com
然后在 bootstrap.yml中抽取配置
spring.application.name=gulimall-third-party spring.cloud.nacos.config.server-addr=192.168.159.135:8848 spring.cloud.nacos.config.namespace=88ca76b3-82d5-49d0-925b-726d5392e48f 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: 192.168.159.135:8848
application:
name: gulimall-third-party
server:
port: 30000
为了不用再配置mybatis相关配置,需要排除common的mybatis-plus-boot-starter
com.example.gulimall gulimall-common0.0.1-SNAPSHOT com.baomidou mybatis-plus-boot-starter
在启动类中加入开启服务注册发现注解
package com.example.gulimall.thirdparty;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallThirdPartyApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallThirdPartyApplication.class, args);
}
}
测试类代码如下
package com.example.gulimall.thirdparty;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallThirdPartyApplicationTests {
@Autowired
OSSClient ossClient;
@Test
public void contextLoads() {
try {
InputStream inputStream = new FileInputStream("C:\Users\gf879\Pictures\Saved Pictures\cat2.png");
// 创建PutObject请求。
ossClient.putObject("gulimall-leohams", "cat2.png", inputStream);
System.out.println("文件上传成功");
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
服务端签名后直传 - 对象存储 OSS - 阿里云
创建一个controller文件
package com.example.gulimall.thirdparty.controller;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
@RestController
public class OssController {
@Autowired
private OSS ossClient;
@Value("${alibaba.cloud.oss.endpoint}")
private String endpoint;
@Value("${alibaba.cloud.oss.bucket}")
private String bucket;
@Value("${alibaba.cloud.access-key}")
private String accessId;
@RequestMapping("/oss/policy")
public Map policy(){
Map respMap = null;
// 填写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 + "/";
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();
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 respMap;
}
}
需要在application.yml中配置以下内容
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.159.135:8848
application:
name: gulimall-third-party
server:
port: 30000
alibaba:
cloud:
access-key: LTAI5tMDvxJp87Zb8W5Y3n99
secret-key: rpBvcATeRr9fbfCyniDcOmYCskf1u9
oss:
endpoint: oss-cn-shanghai.aliyuncs.com
bucket: gulimall-leohams
运行输入 http://localhost:30000/oss/policy 得到以下加密的签名数据
最后编写路由规则
使http://localhost:88/api/thirdparty/oss/policy 路由到指定服务
规则如下:
- id: third_party_route
uri: lb://gulimall-third-party
predicates:
- Path=/api/thirdparty
@Data
@TableName("pms_brand")
public class PmsBrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId
private Long brandId;
@NotBlank(message = "品牌名必须提交")
private String name;
@NotEmpty
@URL(message = "logo必须是一个合法的url地址")
private String logo;
private String descript;
private Integer showStatus;
@NotEmpty
@Pattern(regexp = "/^[a-zA-Z]$/", message = "检索首字母必须是一个字母")
private String firstLetter;
@NotNull
@Min(value = 0, message = "排序必须大于等于0")
private Integer sort;
}
开启校验功能@Valid,同时返回具体错误校验信息
@RequestMapping("/save")
public R save(@Valid @RequestBody PmsBrandEntity pmsBrand, BindingResult result){
if (result.hasErrors()){
Map map = new HashMap<>();
//1. 获取校验的错误结果
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 {
pmsBrandService.save(pmsBrand);
}
return R.ok();
}
品牌管理-统一异常处理
* 4、统一的异常处理 * @ControllerAdvice
注释之前的controller代码
@RequestMapping("/save")
public R save(@Valid @RequestBody PmsBrandEntity pmsBrand ){
// if (result.hasErrors()){
// Map map = new HashMap<>();
// //1. 获取校验的错误结果
// 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 {
// pmsBrandService.save(pmsBrand);
// }
pmsBrandService.save(pmsBrand);
return R.ok();
}
专门创建一个类GulimallExceptionControllerAdvice用来集中处理异常MethodArgumentNotValidException和其他异常
package com.example.gulimall.product.exception;
import com.example.common.exception.BizCodeEnume;
import com.example.common.utils.R;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice(basePackages = "com.example.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult();
Map errorMap = new HashMap<>();
bindingResult.getFieldErrors().forEach((fieldError) -> {
String field = fieldError.getField();
String message = fieldError.getDefaultMessage();
errorMap.put(field, message);
});
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());
}
}
其中错误状态码可以统一规定
·
在com.example.common.exception创建BizCodeEnume枚举类
package com.example.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;
}
}
品牌管理-分组校验
package com.example.common.validator.group;
public interface AddGroup {
}
package com.example.common.validator.group;
public interface UpdateGroup {
}
标明哪个分组
package com.example.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.example.common.validator.group.AddGroup;
import com.example.common.validator.group.UpdateGroup;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.*;
@Data
@TableName("pms_brand")
public class PmsBrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class})
@Null(message = "新增不能指定id", groups = {AddGroup.class})
@TableId
private Long brandId;
@NotBlank(message = "品牌名必须提交", groups = {UpdateGroup.class, AddGroup.class})
private String name;
@NotEmpty(groups = {AddGroup.class})
@URL(message = "logo必须是一个合法的url地址", groups = {AddGroup.class, UpdateGroup.class})
private String logo;
private String descript;
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;
}
加上@Validated注解
@RequestMapping("/update")
public R update(@Validated({UpdateGroup.class}) @RequestBody PmsBrandEntity pmsBrand){
pmsBrandService.updateById(pmsBrand);
return R.ok();
}
@RequestMapping("/save")
public R save(@Validated({AddGroup.class}) @RequestBody PmsBrandEntity pmsBrand ){
pmsBrandService.save(pmsBrand);
return R.ok();
}
品牌管理-自定义校验注解
比如在状态上加一个枚举
@ListValue(vals={0,1})
@ListValue(vals={0,1})
private Integer showStatus;
创建com.example.common.validator.ListValue
package com.example.common.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@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 {
String message() default "{com.example.common.validator.ListValue.message}";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
int[] vals() default {};
}
校验器ListValueConstraintValidator
package com.example.common.validator; import javax.validation.Constraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.lang.annotation.Annotation; import java.util.Set; public class ListValueConstraintValidator implements ConstraintValidator{ private Set set= new HashMap<>();; //初始化方法 @Override public void initialize(ListValue constraintAnnotation) { int[] vals = constraintAnnotation.vals(); for (int val : vals) { set.add(val); } } //判断是否校验成功 @Override public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) { return set.contains(integer); } }
错误信息配置ValidationMessages.properties
com.example.common.validator.ListValue.message=必须提交指定的值
加上指定校验器注解
@Constraint(
validatedBy = {ListValueConstraintValidator.class}
)



