八、品牌管理--谷粒商城
1.品牌管理前端代码
1.1逆向生成代码
把两个文件复制到
1.2修改展示权限
全局搜索isAuth
发现在index.js中
我们注释掉原有代码
一直让其返回True,后面加上return true
1.3品牌管理 效果优化 快速展示开关
我们定义一个开关来控制显示状态
那我们首先自定义表格列
去element-ui找到
获取其中代码粘贴到下图
<template slot-scope="scope"> <i class="el-icon-time"></i> <span style="margin-left: 10px">{{ scope.row.date }}</span> </template>
当1 时显示 0时不显示
当我们的switch状态更改时实时改变数据库中的showstaus状态代码如下
<el-table-column prop="showStatus" header-align="center" align="center" label="显示状态"> <template slot-scope="scope"> <el-switch v-model="scope.row.showStatus" active-color="#13ce66" inactive-color="#ff4949" :active-value="1" :inactive-value="0" @change="updateBrandStatus(scope.row)" ></el-switch> </template> </el-table-column>
监听函数代码如下
这样我们一点击数据库中的值也会实时更新
updateBrandStatus (data) { console.log('最新信息', data) let { brandId, showStatus } = data // 发送请求修改状态 this.$http({ // url: this.$http.adornUrl('/product/brand/update/status'), url: this.$http.adornUrl('/product/brand/update'), method: 'post', data: this.$http.adornData({ brandId, showStatus }, false) }).then(({ data }) => { this.$message({ type: 'success', message: '状态更新成功' }) }) },
2.阿里云设置
2.1注册阿里云
https://oss.console.aliyun.com/bucket
2.2开通oss服务
2.3创建子用户
2.4创建Bucket
2.5 设置跨域
3.第三方服务模块gulimall-third-party
3.1创建模块
3.2改pom文件
<?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.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.atguigu.gulimall</groupId> <artifactId>gulimall-third-party</artifactId> <version>1.0-SNAPSHOT</version> <name>gulimall-third-party</name> <description>第三方服务</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RC1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>com.atguigu.gulimall</groupId> <artifactId>gulimall-common</artifactId> <version>1.0-SNAPSHOT</version> <exclusions> <exclusion> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </exclusion> </exclusions> </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>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alicloud-oss</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </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> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>
3.3创建application.yml文件
spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848 alicloud: access-key:(使用在阿里云创建的子用户id) secret-key:(使用在阿里云创建的子用户密码) oss: endpoint: oss-cn-beijing.aliyuncs.com bucket:(使用在阿里云创建的bucket) application: name: gulimall-third-party server: port: 30000
3.4创建bootstrap.properties文件(需自行在nacos注册命名空间)
spring.application.name=gulimall-third-party spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.namespace= 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
3.5创建Controller文件
package com.atguigu.gulimall.thirdparty.controller;/* @author lailai @create 2023-05-19-下午1:27 */ 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 com.atguigu.common.utils.R; 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 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 R policy() { //https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg 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); 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); } }
3.6 配置网关转发路由
我们配置网关转发路由 访问网关来获取签名
代码如下
找到third_party_route
spring: cloud: gateway: routes: # - id: test_route # uri: https://www.baidu.com # predicates: # - Query=url,baidu # # - id: qq_route # uri: https://www.qq.com # predicates: # - Query=url,qq - id: product_route uri: lb://gulimall-product predicates: - Path=/api/product/** filters: - RewritePath=/api/(?<segment>.*),/$\{segment} - id: third_party_route uri: lb://gulimall-third-party predicates: - Path=/api/thirdparty/** filters: - RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment} - id: member_route uri: lb://gulimall-member predicates: - Path=/api/member/** filters: - RewritePath=/api/(?<segment>.*),/$\{segment} - id: ware_route uri: lb://gulimall-ware predicates: - Path=/api/ware/** filters: - RewritePath=/api/(?<segment>.*),/$\{segment} - id: admin_route uri: lb://renren-fast predicates: - Path=/api/** filters: - RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment} ## 前端项目,/api ## http://localhost:88/api/captcha.jpg http://localhost:8080/renren-fast/captcha.jpg ## http://localhost:88/api/product/category/list/tree http://localhost:10000/product/category/list/tree
配置好后重启网关
我们发送请求进行测试
http://localhost:88/api/thirdparty/oss/policy
3.7brand-add-or-update
完整代码示例如下
package com.atguigu.gulimall.thirdparty.controller;/* @author lailai @create 2023-05-19-下午1:27 */ 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 com.atguigu.common.utils.R; 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 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 R policy() { //https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg 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); 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); } }
4表单校验,自定义校验器
4.1 首先更新你前端项目使用的组件库
/** * UI组件, 统一使用饿了么桌面端组件库(https://github.com/ElemeFE/element) * * 使用: * 1. 项目中需要的组件进行释放(解开注释) * * 注意: * 1. 打包只会包含释放(解开注释)的组件, 减少打包文件大小 */ import Vue from 'vue' import { Pagination, Dialog, Autocomplete, Dropdown, DropdownMenu, DropdownItem, Menu, Submenu, MenuItem, MenuItemGroup, Input, InputNumber, Radio, RadioGroup, RadioButton, Checkbox, CheckboxButton, CheckboxGroup, Switch, Select, Option, OptionGroup, Button, ButtonGroup, Table, TableColumn, DatePicker, TimeSelect, TimePicker, Popover, Tooltip, Breadcrumb, BreadcrumbItem, Form, FormItem, Tabs, TabPane, Tag, Tree, Alert, Slider, Icon, Row, Col, Upload, Progress, Spinner, Badge, Card, Rate, Steps, Step, Carousel, CarouselItem, Collapse, CollapseItem, Cascader, ColorPicker, Transfer, Container, Header, Aside, Main, Footer, Timeline, TimelineItem, Link, Divider, Image, Calendar, Loading, MessageBox, Message, Notification } from 'element-ui'; Vue.use(Pagination); Vue.use(Dialog); Vue.use(Autocomplete); Vue.use(Dropdown); Vue.use(DropdownMenu); Vue.use(DropdownItem); Vue.use(Menu); Vue.use(Submenu); Vue.use(MenuItem); Vue.use(MenuItemGroup); Vue.use(Input); Vue.use(InputNumber); Vue.use(Radio); Vue.use(RadioGroup); Vue.use(RadioButton); Vue.use(Checkbox); Vue.use(CheckboxButton); Vue.use(CheckboxGroup); Vue.use(Switch); Vue.use(Select); Vue.use(Option); Vue.use(OptionGroup); Vue.use(Button); Vue.use(ButtonGroup); Vue.use(Table); Vue.use(TableColumn); Vue.use(DatePicker); Vue.use(TimeSelect); Vue.use(TimePicker); Vue.use(Popover); Vue.use(Tooltip); Vue.use(Breadcrumb); Vue.use(BreadcrumbItem); Vue.use(Form); Vue.use(FormItem); Vue.use(Tabs); Vue.use(TabPane); Vue.use(Tag); Vue.use(Tree); Vue.use(Alert); Vue.use(Slider); Vue.use(Icon); Vue.use(Row); Vue.use(Col); Vue.use(Upload); Vue.use(Progress); Vue.use(Spinner); Vue.use(Badge); Vue.use(Card); Vue.use(Rate); Vue.use(Steps); Vue.use(Step); Vue.use(Carousel); Vue.use(CarouselItem); Vue.use(Collapse); Vue.use(CollapseItem); Vue.use(Cascader); Vue.use(ColorPicker); Vue.use(Transfer); Vue.use(Container); Vue.use(Header); Vue.use(Aside); Vue.use(Main); Vue.use(Footer); Vue.use(Timeline); Vue.use(TimelineItem); Vue.use(Link); Vue.use(Divider); Vue.use(Image); Vue.use(Calendar); Vue.use(Loading.directive) Vue.prototype.$loading = Loading.service Vue.prototype.$msgbox = MessageBox Vue.prototype.$alert = MessageBox.alert Vue.prototype.$confirm = MessageBox.confirm Vue.prototype.$prompt = MessageBox.prompt Vue.prototype.$notify = Notification Vue.prototype.$message = Message Vue.prototype.$ELEMENT = { size: 'medium' }
4.2 接着引入在项目中展示你的Logo图片
<el-table-column prop="logo" header-align="center" align="center" label="品牌logo地址"> <template slot-scope="scope"> <!-- <i class="el-icon-time"></i>--> <!-- <span style="margin-left: 10px">{{ scope.row.date }}</span>--> <!-- <el-image--> <!-- style="width: 100px; height: 80px"--> <!-- :src="scope.row.logo"--> <!-- :fit="fit"></el-image>--> <img :src="scope.row.logo" style="width: 100px; height: 80px" /> </template> </el-table-column>
4.3最后在新增中增加前端表单校验
dataRule: { name: [ { required: true, message: '品牌名不能为空', trigger: 'blur' } ], logo: [ { required: true, message: '品牌logo地址不能为空', trigger: 'blur' } ], descript: [ { required: true, message: '介绍不能为空', trigger: 'blur' } ], showStatus: [ { required: true, message: '显示状态[0-不显示;1-显示]不能为空', trigger: 'blur' } ], 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 (!/^[0-9]$/.test(value) || value<0 ) { callback(new Error("排序必须是一个大于等于0的整数")); } else { callback(); } }, trigger: "blur" } ] }
完整代码如下:
<template> <el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible" > <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="140px" > <el-form-item label="品牌名" prop="name"> <el-input v-model="dataForm.name" placeholder="品牌名"></el-input> </el-form-item> <el-form-item label="品牌logo地址" prop="logo"> <!-- <el-input v-model="dataForm.logo" placeholder="品牌logo地址"></el-input> --> <single-upload v-model="dataForm.logo"></single-upload> </el-form-item> <el-form-item label="介绍" prop="descript"> <el-input v-model="dataForm.descript" placeholder="介绍"></el-input> </el-form-item> <el-form-item label="显示状态" prop="showStatus"> <el-switch v-model="dataForm.showStatus" active-color="#13ce66" inactive-color="#ff4949" :active-value="1" :inactive-value="0" ></el-switch> </el-form-item> <el-form-item label="检索首字母" prop="firstLetter"> <el-input v-model="dataForm.firstLetter" placeholder="检索首字母"></el-input> </el-form-item> <el-form-item label="排序" prop="sort"> <el-input v-model.number="dataForm.sort" placeholder="排序"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="visible = false">取消</el-button> <el-button type="primary" @click="dataFormSubmit()">确定</el-button> </span> </el-dialog> </template> <script> import SingleUpload from "@/components/upload/singleUpload"; export default { components: { SingleUpload }, data() { return { visible: false, dataForm: { brandId: 0, name: "", logo: "", descript: "", showStatus: 1, firstLetter: "", sort: 0 }, dataRule: { name: [{ required: true, message: "品牌名不能为空", trigger: "blur" }], logo: [ { required: true, message: "品牌logo地址不能为空", trigger: "blur" } ], descript: [ { required: true, message: "介绍不能为空", trigger: "blur" } ], showStatus: [ { required: true, message: "显示状态[0-不显示;1-显示]不能为空", trigger: "blur" } ], 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" } ] } }; }, methods: { init(id) { this.dataForm.brandId = id || 0; this.visible = true; this.$nextTick(() => { this.$refs["dataForm"].resetFields(); if (this.dataForm.brandId) { this.$http({ url: this.$http.adornUrl( `/product/brand/info/${this.dataForm.brandId}` ), method: "get", params: this.$http.adornParams() }).then(({ data }) => { if (data && data.code === 0) { this.dataForm.name = data.brand.name; this.dataForm.logo = data.brand.logo; this.dataForm.descript = data.brand.descript; this.dataForm.showStatus = data.brand.showStatus; this.dataForm.firstLetter = data.brand.firstLetter; this.dataForm.sort = data.brand.sort; } }); } }); }, // 表单提交 dataFormSubmit() { this.$refs["dataForm"].validate(valid => { if (valid) { this.$http({ url: this.$http.adornUrl( `/product/brand/${!this.dataForm.brandId ? "save" : "update"}` ), method: "post", data: this.$http.adornData({ brandId: this.dataForm.brandId || undefined, name: this.dataForm.name, logo: this.dataForm.logo, descript: this.dataForm.descript, showStatus: this.dataForm.showStatus, firstLetter: this.dataForm.firstLetter, sort: this.dataForm.sort }) }).then(({ data }) => { if (data && data.code === 0) { this.$message({ message: "操作成功", type: "success", duration: 1500, onClose: () => { this.visible = false; this.$emit("refreshDataList"); } }); } else { this.$message.error(data.msg); } }); } }); } } }; </script>
4.4JSR303校验
4.4.1 SR303概述
1)、给Bean添加校验注解:javax.validation.constraints,并定义自己的message提示
2)、开启校验功能@Valid
效果:校验错误以后会有默认的响应;
3)、给校验的bean后紧跟一个BindingResult,就可以获取到校验的结果
@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 @URL(message = "logo必须是一个合法的url地址") private String logo; /** * 介绍 */ private String descript; /** * 显示状态[0-不显示;1-显示] */ @NotNull(message = "展示状态不能为空") private Integer showStatus; /** * 检索首字母 */ @NotEmpty @Pattern(regexp="^[a-zA-Z]$",message = "检索首字母必须是一个字母") private String firstLetter; /** * 排序 */ @NotNull @Min(value = 0,message = "排序必须大于等于0") private Integer sort; }
4.4.3controller层开启校验功能
@RequestMapping("/save") //@RequiresPermissions("product:brand:save") public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){ if(result.hasErrors()){ Map<String,String> 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 { brandService.save(brand); return R.ok(); } }
4.5 统一异常处理
4.5.1统一异常处理概述
- 统一的异常处理
- @ControllerAdvice
- 1)、编写异常处理类,使用@ControllerAdvice。
- 2)、使用@ExceptionHandler标注方法可以处理的异常
4.5.2首先在product模块下建一个全局异常捕获类
package com.atguigu.gulimall.product.exception; import com.atguigu.common.exception.BizCodeEnume; import com.atguigu.common.utils.R; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.BindingResult; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.ModelAndView; import java.util.HashMap; import java.util.Map; /** * 集中处理所有异常 */ @Slf4j //@ResponseBody //@ControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller") @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.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap); } @ExceptionHandler(value = Throwable.class) public R handleException(Throwable throwable){ log.error("错误:",throwable); return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg()); } }
4.5.3在common模块下定义一个全局枚举
package com.atguigu.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; } }
4.6JSR303分组校验
4.6.1分组校验(多场景的复杂校验)
1)、@NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class}) 给校验注解标注什么情况需要进行校验 2)、@Validated({AddGroup.class}) 3)、默认没有指定分组的校验注解@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效;
4.6.2在 common模块下创建一个vaild包
4.6.3在实体类上定义组分类
形式(groups = {组接口.class})
@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-显示] */ // @Pattern() @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class}) @ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class}) 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; }
@RequestMapping("/update") //@RequiresPermissions("product:brand:update") public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand){ brandService.updateById(brand); return R.ok(); }
4.7JSR303 自定义校验注解
4.7.1自定义校验
1)、编写一个自定义的校验注解 2)、编写一个自定义的校验器 ConstraintValidator 3)、关联自定义的校验器和自定义的校验注解 * @Documented * @Constraint(validatedBy = { ListValueConstraintValidator.class【可以指定多个不同的校验器,适配不同类型的校验】 }) * @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) * @Retention(RUNTIME) * public @interface ListValue {
4.7.2 在自己对应实体类上编写自定义注解
/** * 显示状态[0-不显示;1-显示] */ // @Pattern() @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class}) @ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class}) private Integer showStatus;
4.7.3 编写注解接口和实现类
@Documented @Constraint(validatedBy = { ListValueConstraintValidator.class }) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) public @interface ListValue { String message() default "{com.atguigu.common.valid.ListValue.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; int[] vals() default { }; }
package com.atguigu.common.valid; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.HashSet; import java.util.Set; 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); } } //判断是否校验成功 /** * * @param value 需要校验的值 * @param context * @return */ @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { return set.contains(value); } }
4.7.4 controller层开启
/** * 修改状态 */ @RequestMapping("/update/status") //@RequiresPermissions("product:brand:update") public R updateStatus(@Validated(UpdateStatusGroup.class) @RequestBody BrandEntity brand){ brandService.updateById(brand); return R.ok(); }
4.7.5 大概总结
controller开启Vailid或者Validated进行校验 -> 到实体类的校验注解查看校验策略(分组 和注解内容,如notnull等) ->分支1 非定义注解 验证后通过或是返回错误信息——>分支2 自定义注解 去自定义注解中找到校验器
然后进行校验判断