5 新增课程

5.1 需求分析

5.1.1 业务流程

根据前边对内容管理模块的数据模型分析,课程相关的信息有:课程基本信息、课程营销信息、课程图片信息、课程计划、课程师资信息,所以新增一门课程需要完成这几部分信息的填写。

以下是业务流程:

1、进入课程查询列表

2、点击添加课程,选择课程形式为录播。

3、选择完毕,点击下一步,进入课程基本信息添加界面。

本界面分两部分信息,一部分是课程基本信息上,一部分是课程营销信息。

课程基本信息:

课程营销信息:

在这个界面中填写课程的基本信息、课程营销信息上。

填写完毕,保存并进行下一步。

4、在此界面填写课程计划信息

课程计划即课程的大纲目录。

课程计划分为两级,章节和小节。

每个小节需要上传课程视频,用户点击 小节的标题即开始播放视频。

如果是直播课程则会进入直播间。

5、课程 计划填写完毕进入课程师资的管理。

在课程师资界面维护该课程的授课老师。

至此,一门课程新增完成。

5.1.2 数据模型

通过业务流程可知,一门课程信息涉及:课程基本信息、课程营销信息、课程计划信息、课程师资信息。

本节开发新增课程按钮功能, 只向课程基本信息、课程营销信息添加记录。

这两部分信息分别在course_base、course_market两张表存储。当点击保存按钮时向这两张表插入数据。这两张表是一对一关联关系。

新建课程的初始审核状态为“未提交”、初始发布状态为“未发布”。

生成课程基本信息、课程营销信息的PO、Mapper文件

5.2 接口定义

根据业务流程,这里先定义提交课程基本信息的接口。

1、接口协议 :HTTP POST,Content-Type为application/json

2、请求及响应结果如下

3、接口请求示例如下

Java
### 创建课程
POST {{content_host}}/content/course
Content-Type: application/json

{

  "mt": "",
  "st": "",
  "name": "",
  "pic": "",
  "teachmode": "200002",
  "users": "初级人员",
  "tags": "",
  "grade": "204001",
  "description": "",
  "charge": "201000",
  "price": 0,
  "originalPrice":0,
  "qq": "",
  "wechat": "",
  "phone": "",
  "validDays": 365
}

###响应结果如下
#成功响应结果如下
{
  "id": 109,
  "companyId": 1,
  "companyName": null,
  "name": "测试课程103",
  "users": "初级人员",
  "tags": "",
  "mt": "1-1",
  "mtName": null,
  "st": "1-1-1",
  "stName": null,
  "grade": "204001",
  "teachmode": "200002",
  "description": "",
  "pic": "",
  "createDate": "2022-09-08 07:35:16",
  "changeDate": null,
  "createPeople": null,
  "changePeople": null,
  "auditStatus": "202002",
  "status": 1,
  "coursePubId": null,
  "coursePubDate": null,
  "charge": "201000",
  "price": null,
  "originalPrice":0,
  "qq": "",
  "wechat": "",
  "phone": "",
  "validDays": 365
}

3、定义请求参数类型和响应结构类型

根据接口定义内容,请求参数相比 CourseBase模型类不一致,需要在dto包下自定义,模型类从课程资料/工程目录获取。

4、定义接口如下

Java
@ApiOperation("新增课程基础信息")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody AddCourseDto addCourseDto){
    return null;
}

5.3 接口开发

5.3.1 保存课程基本信息

根据需求分析,新增课程表单中包括了课程基本信息、课程营销信息,需要分别向课程基本信息表、课程营销表保证数据。

首先定义service接口,

Java
 /**
  * @description 添加课程基本信息
  * @param companyId  教学机构id
  * @param addCourseDto  课程基本信息
  * @return com.xuecheng.content.model.dto.CourseBaseInfoDto
  * @author Mr.M
  * @date 2022/9/7 17:51
 */
CourseBaseInfoDto createCourseBase(Long companyId,AddCourseDto addCourseDto);

编写service接口实现类,实现向课程基本信息表保存数据:

Java
@Transactional
@Override
public CourseBaseInfoDto createCourseBase(Long companyId,AddCourseDto dto) {

 //合法性校验
 if (StringUtils.isBlank(dto.getName())) {
  throw new RuntimeException("课程名称为空");
 }

 if (StringUtils.isBlank(dto.getMt())) {
  throw new RuntimeException("课程分类为空");
 }

 if (StringUtils.isBlank(dto.getSt())) {
  throw new RuntimeException("课程分类为空");
 }

 if (StringUtils.isBlank(dto.getGrade())) {
  throw new RuntimeException("课程等级为空");
 }

 if (StringUtils.isBlank(dto.getTeachmode())) {
  throw new RuntimeException("教育模式为空");
 }

 if (StringUtils.isBlank(dto.getUsers())) {
  throw new RuntimeException("适应人群为空");
 }

 if (StringUtils.isBlank(dto.getCharge())) {
  throw new RuntimeException("收费规则为空");
 }
   //新增对象
  CourseBase courseBaseNew = new CourseBase();
  //将填写的课程信息赋值给新增对象
  BeanUtils.copyProperties(dto,courseBaseNew);
  //设置审核状态
  courseBaseNew.setAuditStatus("202002");
  //设置发布状态
  courseBaseNew.setStatus("203001");
  //机构id
  courseBaseNew.setCompanyId(companyId);
  //添加时间
  courseBaseNew.setCreateDate(LocalDateTime.now());
 //插入课程基本信息表
  int insert = courseBaseMapper.insert(courseBaseNew);
  if(insert<=0){
    throw new RuntimeException("新增课程基本信息失败");
}
//todo:向课程营销表保存课程营销信息
//todo:查询课程基本信息及营销信息并返回
 
}

5.3.2 保存营销信息

下边实现向课程营销表保存课程营销信息

Java
public CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto dto) {

    //合法性校验
    if (StringUtils.isBlank(dto.getName())) {
        throw new RuntimeException("课程名称为空");
    }

    if (StringUtils.isBlank(dto.getMt())) {
        throw new RuntimeException("课程分类为空");
    }

    if (StringUtils.isBlank(dto.getSt())) {
        throw new RuntimeException("课程分类为空");
    }

    if (StringUtils.isBlank(dto.getGrade())) {
        throw new RuntimeException("课程等级为空");
    }

    if (StringUtils.isBlank(dto.getTeachmode())) {
        throw new RuntimeException("教育模式为空");
    }

    if (StringUtils.isBlank(dto.getUsers())) {
        throw new RuntimeException("适应人群为空");
    }

    if (StringUtils.isBlank(dto.getCharge())) {
        throw new RuntimeException("收费规则为空");
    }
    //新增对象
    CourseBase courseBaseNew = new CourseBase();
    //将填写的课程信息赋值给新增对象
    BeanUtils.copyProperties(dto,courseBaseNew);
    //设置审核状态
    courseBaseNew.setAuditStatus("202002");
    //设置发布状态
    courseBaseNew.setStatus("203001");
    //机构id
    courseBaseNew.setCompanyId(companyId);
    //添加时间
    courseBaseNew.setCreateDate(LocalDateTime.now());
    //插入课程基本信息表
    int insert = courseBaseMapper.insert(courseBaseNew);
    if(insert<=0){
        throw new RuntimeException("新增课程基本信息失败");
    }
    //向课程营销表保存课程营销信息
    //课程营销信息
    CourseMarket courseMarketNew = new CourseMarket();
    Long courseId = courseBaseNew.getId();
    BeanUtils.copyProperties(dto,courseMarketNew);
    courseMarketNew.setId(courseId);
    int i = saveCourseMarket(courseMarketNew);
    if(i<=0){
        throw new RuntimeException("保存课程营销信息失败");
    }
    //查询课程基本信息及营销信息并返回
    return getCourseBaseInfo(courseId);

}
//保存课程营销信息
private int saveCourseMarket(CourseMarket courseMarketNew){
    //收费规则
    String charge = courseMarketNew.getCharge();
    if(StringUtils.isBlank(charge)){
        throw new RuntimeException("收费规则没有选择");
    }
    //收费规则为收费
    if(charge.equals("201001")){
        if(courseMarketNew.getPrice() == null || courseMarketNew.getPrice().floatValue()<=0){
            throw new RuntimeException("课程为收费价格不能为空且必须大于0");
        }
    }
    //根据id从课程营销表查询
    CourseMarket courseMarketObj = courseMarketMapper.selectById(courseMarketNew.getId());
    if(courseMarketObj == null){
        return courseMarketMapper.insert(courseMarketNew);
    }else{
        BeanUtils.copyProperties(courseMarketNew,courseMarketObj);
        courseMarketObj.setId(courseMarketNew.getId());
        return courseMarketMapper.updateById(courseMarketObj);
    }
}
//根据课程id查询课程基本信息,包括基本信息和营销信息
 public CourseBaseInfoDto getCourseBaseInfo(long courseId){

  CourseBase courseBase = courseBaseMapper.selectById(courseId);
  if(courseBase == null){
   return null;
  }
  CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
  CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto();
  BeanUtils.copyProperties(courseBase,courseBaseInfoDto);
  if(courseMarket != null){
   BeanUtils.copyProperties(courseMarket,courseBaseInfoDto);
  }

  //查询分类名称
  CourseCategory courseCategoryBySt = courseCategoryMapper.selectById(courseBase.getSt());
  courseBaseInfoDto.setStName(courseCategoryBySt.getName());
  CourseCategory courseCategoryByMt = courseCategoryMapper.selectById(courseBase.getMt());
  courseBaseInfoDto.setMtName(courseCategoryByMt.getName());

  return courseBaseInfoDto;

 }

5.4 接口测试

1、首先去完善controller方法:

Java
@ApiOperation("新增课程基础信息")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody AddCourseDto addCourseDto){
    //机构id,由于认证系统没有上线暂时硬编码
    Long companyId = 1232141425L;
  return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}

2、使用httpclient测试

在xc-content-api.http中定义:

Java
### 创建课程
POST {{content_host}}/content/course
Content-Type: application/json

{
  "charge": "201000",
  "price": 0,
  "originalPrice":0,
  "qq": "22333",
  "wechat": "223344",
  "phone": "13333333",
  "validDays": 365,
  "mt": "1-1",
  "st": "1-1-1",
  "name": "测试课程103",
  "pic": "",
  "teachmode": "200002",
  "users": "初级人员",
  "tags": "",
  "grade": "204001",
  "description": ""
}

3、前后端联调

打开新增课程页面,除了课程图片其它信息全部输入。

点击保存,观察浏览器请求接口参数及响应结果是否正常。

5.6 异常处理

5.6.1 异常问题分析

在service方法中有很多的参数合法性校验,当参数不合法则抛出异常,下边我们测试下异常处理。

请求创建课程基本信息,故意将必填项设置为空。

测试发现报500异常,如下:

Java
http://localhost:63040/content/course

HTTP/1.1 500
Content-Type: application/json
Transfer-Encoding: chunked
Date: Wed, 07 Sep 2022 11:40:29 GMT
Connection: close

{
  "timestamp": "2022-09-07T11:40:29.677+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "message": "",
  "path": "/content/course"
}

问题:并没有输出我们抛出异常时指定的异常信息。

所以,现在我们的需求是当正常操作时按接口要求返回数据,当非正常流程时要获取异常信息进行记录,并提示给用户。

异常处理除了输出在日志中,还需要提示给用户,前端和后端需要作一些约定:

1、错误提示信息统一以json格式返回给前端。

2、以HTTP状态码决定当前是否出错,非200为操作异常。

如何规范异常信息?

代码中统一抛出项目的自定义异常类型,这样可以统一去捕获这一类或几类的异常。

规范了异常类型就可以去获取异常信息。

如果捕获了非项目自定义的异常类型统一向用户提示“执行过程异常,请重试”的错误信息。

如何捕获异常?

代码统一用try/catch方式去捕获代码比较臃肿,可以通过SpringMVC提供的控制器增强类统一由一个类去完成异常的捕获。

如下图:

根据上边分析的方案,统一在base基础工程实现统一异常处理,各模块依赖了base基础工程都 可以使用。

首先在base基础工程添加需要依赖的包:

Java
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

1、定义一些通用的异常信息

从课程资料/工程目录 拷贝CommonError 类到base工程com.xuecheng.base.execption下。

Java
package com.xuecheng.base.execption;


/**
 * @description 通用错误信息
 * @author Mr.M
 * @date 2022/9/6 11:29
 * @version 1.0
 */
public enum CommonError {

   UNKOWN_ERROR("执行过程异常,请重试。"),
   PARAMS_ERROR("非法参数"),
   OBJECT_NULL("对象为空"),
   QUERY_NULL("查询结果为空"),
   REQUEST_NULL("请求参数为空");

   private String errMessage;

   public String getErrMessage() {
      return errMessage;
   }

   private CommonError( String errMessage) {
      this.errMessage = errMessage;
   }

}

2、自定义异常类型

在base工程com.xuecheng.base.execption下自定义异常类。

Java
package com.xuecheng.base.execption;


/**
 * @description 学成在线项目异常类
 * @author Mr.M
 * @date 2022/9/6 11:29
 * @version 1.0
 */
public class XueChengPlusException extends RuntimeException {

   private String errMessage;

   public XueChengPlusException() {
      super();
   }

   public XueChengPlusException(String errMessage) {
      super(errMessage);
      this.errMessage = errMessage;
   }

   public String getErrMessage() {
      return errMessage;
   }

   public static void cast(CommonError commonError){
       throw new XueChengPlusException(commonError.getErrMessage());
   }
   public static void cast(String errMessage){
       throw new XueChengPlusException(errMessage);
   }

}

3、响应用户的统一类型

Java
package com.xuecheng.base.execption;

import java.io.Serializable;

/**
 * 错误响应参数包装
 */
public class RestErrorResponse implements Serializable {

    private String errMessage;

    public RestErrorResponse(String errMessage){
        this.errMessage= errMessage;
    }

    public String getErrMessage() {
        return errMessage;
    }

    public void setErrMessage(String errMessage) {
        this.errMessage = errMessage;
    }
}

4、全局异常处理器

        从 Spring 3.0 - Spring 3.2 版本之间,对 Spring 架构和 SpringMVC 的Controller 的异常捕获提供了相应的异常处理。

  • @ExceptionHandler
  • Spring3.0提供的标识在方法上或类上的注解,用来表明方法的处理异常类型。
  • @ControllerAdvice
  • Spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强,        在项目中来增强SpringMVC中的Controller。通常和@ExceptionHandler 结合使用,来处理SpringMVC的异常信息。
  • @ResponseStatus
  • Spring3.0提供的标识在方法上或类上的注解,用状态代码和应返回的原因标记方法或异常类。
    调用处理程序方法时,状态代码将应用于HTTP响应。

通过上面的两个注解便可实现微服务端全局异常处理,具体代码如下:

Java
package com.xuecheng.base.execption;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
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.ResponseStatus;

/**
 * @description 全局异常处理器
 * @author Mr.M
 * @date 2022/9/6 11:29
 * @version 1.0
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

   @ResponseBody
   @ExceptionHandler(XueChengPlusException.class)
   @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
   public RestErrorResponse customException(XueChengPlusException e) {
      log.error("【系统异常】{}",e.getErrMessage(),e);
      return new RestErrorResponse(e.getErrMessage());

   }

   @ResponseBody
   @ExceptionHandler(Exception.class)
   @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
   public RestErrorResponse exception(Exception e) {

      log.error("【系统异常】{}",e.getMessage(),e);

      return new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());

   }
}

5.6.3 异常处理测试

在内容管理的api工程添加base工程的依赖

Java
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xuecheng-plus-base</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

在异常处理测试之前首先在代码中抛出自定义类型的异常,这里以新增课程的service方法为例进行代码修改。

Java
 @Override
 public CourseBaseInfoDto createCourseBase(Long companyId,AddCourseDto dto) {
 ...
//合法性校验
  if (StringUtils.isBlank(dto.getName())) {
   throw new XueChengPlusException("课程名称为空");
  }

  if (StringUtils.isBlank(dto.getMt())) {
   throw new XueChengPlusException("课程分类为空");
  }

  if (StringUtils.isBlank(dto.getSt())) {
   throw new XueChengPlusException("课程分类为空");
  }

  if (StringUtils.isBlank(dto.getGrade())) {
   throw new XueChengPlusException("课程等级为空");
  }

  if (StringUtils.isBlank(dto.getTeachmode())) {
   throw new XueChengPlusException("教育模式为空");
  }

  if (StringUtils.isBlank(dto.getUsers())) {
   throw new XueChengPlusException("适应人群");
  }

  if (StringUtils.isBlank(dto.getCharge())) {
   throw new XueChengPlusException("收费规则为空");
  }
  。。。
    if ("201001".equals(charge)) {
   BigDecimal price = dto.getPrice();
   if (ObjectUtils.isEmpty(price)) {
    throw new XueChengPlusException("收费课程价格不能为空");
   }
   courseMarketNew.setPrice(dto.getPrice().floatValue());
  }
  。。。

1、首先使用httpclient测试

请求新增课程接口,故意将必填项课程名称设置为空。

测试结果与预期一致,可以捕获异常并响应异常信息,如下:

Java
http://localhost:63040/content/course

HTTP/1.1 500
Content-Type: application/json
Transfer-Encoding: chunked
Date: Wed, 07 Sep 2022 13:17:14 GMT
Connection: close

{
  "errMessage": "课程名称为空。"
}

2、前后端调试

仍然测试新增课程接口,当课程收费的时候必须填写价格,这里设置课程为收费,价格设置为空。

通过测试发现,前端正常提示代码 中抛出的异常信息。

至此,项目异常处理的测试完毕,我们在开发中对于业务分支中错误的情况要抛出项目自定义的异常类型。

5.7 JSR303校验

5.7.1 统一校验的需求

前端请求后端接口传输参数,是在controller中校验还是在Service中校验?

答案是都需要校验,只是分工不同。

Contoller中校验请求参数的合法性,包括:必填项校验,数据格式校验,比如:是否是符合一定的日期格式,等。

Service中要校验的是业务规则相关的内容,比如:课程已经审核通过所以提交失败。

Service中根据业务规则去校验不方便写成通用代码,Controller中则可以将校验的代码写成通用代码。

早在JavaEE6规范中就定义了参数校验的规范,它就是JSR-303,它定义了Bean Validation,即对bean属性进行校验。

SpringBoot提供了JSR-303的支持,它就是spring-boot-starter-validation,它的底层使用Hibernate Validator,Hibernate Validator是Bean Validation 的参考实现。

所以,我们准备在Controller层使用spring-boot-starter-validation完成对请求参数的基本合法性进行校验。

5.7.2 统一校验实现

首先在Base工程添加spring-boot-starter-validation的依赖

Java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

在javax.validation.constraints包下有很多这样的校验注解,直接使用注解定义校验规则即可。

规则如下:

现在准备对内容管理模块添加课程接口进行参数校验,如下接口

Java
@ApiOperation("新增课程基础信息")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody AddCourseDto addCourseDto){
    //机构id,由于认证系统没有上线暂时硬编码
    Long companyId = 1232141425L;
  return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}

此接口使用AddCourseDto模型对象接收参数,所以进入AddCourseDto类,在属性上添加校验规则。

Java
package com.xuecheng.content.model.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.math.BigDecimal;

/**
 * @description 添加课程dto
 * @author Mr.M
 * @date 2022/9/7 17:40
 * @version 1.0
 */
@Data
@ApiModel(value="AddCourseDto", description="新增课程基本信息")
public class AddCourseDto {

 @NotEmpty(message = "课程名称不能为空")
 @ApiModelProperty(value = "课程名称", required = true)
 private String name;

 @NotEmpty(message = "适用人群不能为空")
 @Size(message = "适用人群内容过少",min = 10)
 @ApiModelProperty(value = "适用人群", required = true)
 private String users;

 @ApiModelProperty(value = "课程标签")
 private String tags;

 @NotEmpty(message = "课程分类不能为空")
 @ApiModelProperty(value = "大分类", required = true)
 private String mt;

 @NotEmpty(message = "课程分类不能为空")
 @ApiModelProperty(value = "小分类", required = true)
 private String st;

 @NotEmpty(message = "课程等级不能为空")
 @ApiModelProperty(value = "课程等级", required = true)
 private String grade;

 @ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true)
 private String teachmode;

 @ApiModelProperty(value = "课程介绍")
 private String description;

 @ApiModelProperty(value = "课程图片", required = true)
 private String pic;

 @NotEmpty(message = "收费规则不能为空")
 @ApiModelProperty(value = "收费规则,对应数据字典", required = true)
 private String charge;

 @ApiModelProperty(value = "价格")
 private BigDecimal price;

}

上边用到了@NotEmpty和@Size两个注解,@NotEmpty表示属性不能为空,@Size表示限制属性内容的长短。

定义好校验规则还需要开启校验,在controller方法中添加@Validated注解,如下:

Bash
@ApiOperation("新增课程基础信息")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody @Validated AddCourseDto addCourseDto){
    //机构id,由于认证系统没有上线暂时硬编码
    Long companyId = 1L;
  return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}

如果校验出错Spring会抛出MethodArgumentNotValidException异常,我们需要在统一异常处理器中捕获异常,解析出异常信息。

代码 如下:

Bash
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {
    BindingResult bindingResult = e.getBindingResult();
    List<String> msgList = new ArrayList<>();
    //将错误信息放在msgList
    bindingResult.getFieldErrors().stream().forEach(item->msgList.add(item.getDefaultMessage()));
    //拼接错误信息
    String msg = StringUtils.join(msgList, ",");
    log.error("【系统异常】{}",msg);
    return new RestErrorResponse(msg);
}

重启内容管理服务。

使用httpclient进行测试,将必填项设置为空,“适用人群” 属性的内容设置1个字。

执行测试,接口响应结果如下:

Bash

{
  "errMessage": "课程名称不能为空 课程分类不能为空 课程分类不能为空 适用人群内容过少 "
}

可以看到校验器生效。

5.7.3 分组校验

有时候在同一个属性上设置一个校验规则不能满足要求,比如:订单编号由系统生成,在添加订单时要求订单编号为空,在更新 订单时要求订单编写不能为空。此时就用到了分组校验,同一个属性定义多个校验规则属于不同的分组,比如:添加订单定义@NULL规则属于insert分组,更新订单定义@NotEmpty规则属于update分组,insert和update是分组的名称,是可以修改的。

下边举例说明

我们用class类型来表示不同的分组,所以我们定义不同的接口类型(空接口)表示不同的分组,由于校验分组是公用的,所以定义在 base工程中。如下:

Bash
package com.xuecheng.base.execption;
 /**
 * @description 校验分组
 * @author Mr.M
 * @date 2022/9/8 15:05
 * @version 1.0
 */
public class ValidationGroups {

 public interface Inster{};
 public interface Update{};
 public interface Delete{};

}

下边在定义校验规则时指定分组:

Bash
@NotEmpty(groups = {ValidationGroups.Inster.class},message = "添加课程名称不能为空")
 @NotEmpty(groups = {ValidationGroups.Update.class},message = "修改课程名称不能为空")
// @NotEmpty(message = "课程名称不能为空")
 @ApiModelProperty(value = "课程名称", required = true)
 private String name;

在Controller方法中启动校验规则指定要使用的分组名:

Bash
@ApiOperation("新增课程基础信息")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody @Validated({ValidationGroups.Inster.class}) AddCourseDto addCourseDto){
    //机构id,由于认证系统没有上线暂时硬编码
    Long companyId = 1L;
  return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}

再次测试,由于这里指定了Insert分组,所以抛出 异常信息:添加课程名称不能为空。

如果修改分组为ValidationGroups.Update.class,异常信息为:修改课程名称不能为空。

5.7.4 校验规则不满足?

如果javax.validation.constraints包下的校验规则满足不了需求怎么办?

1、手写校验代码 。

2、自定义校验规则注解。

如何自定义校验规则注解,请自行查阅资料实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/646601.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全链路压测:提升业务可靠性和可用性

全链路压测是一种全面评估系统性能和稳定性的测试方法&#xff0c;通过模拟真实用户场景和流程来验证整个应用系统在高负载情况下的表现。全链路压测的主要作用涵盖了多个方面&#xff1a; 性能评估与优化&#xff1a; 全链路压测可以全面评估系统在高负载下的性能表现&#xf…

代码评审——随机数Random问题

问题描述&#xff1a; 为了获取唯一值&#xff0c;经常会依赖产生随机数来保证唯一性。在获取随机数时&#xff0c;如果使用错误的方法&#xff0c;会比较低效。 可以参考以下代码&#xff1a; public static String geneRundomNo(){Random rnew Random();int numr.nextInt(…

day31_CSS

今日内容 CSS概述引入方式 (where)选择器(how)属性(how) 1 CSS介绍 层叠样式表&#xff08;cascading style sheet&#xff09; CSS 用来美化HTML页面,可以让页面更好看,还可以布局页面. 好处 美化页面,布局页面使用外部css文件,可以实现样式文件和html文件分离,便于维护使用外…

5.ROC-AUC机器学习模型性能的常用的评估指标

最近回顾机器学习基础知识部分的时候&#xff0c;看到了用于评估机器学习模型性能的ROC曲线。再次记录一下&#xff0c;想起之前学习的时候的茫然&#xff0c;希望这次可以更加清晰的了解这一指标。上课的时候听老师提起过&#xff0c;当时没有认真去看&#xff0c;所以这次可以…

SpeechGPT-Gen;使用Agents编辑图像;多模态扩散模型图像生成

本文首发于公众号&#xff1a;机器感知 SpeechGPT-Gen&#xff1b;使用Agents编辑图像&#xff1b;多模态扩散模型图像生成&#xff1b; CCA: Collaborative Competitive Agents for Image Editing This paper presents a novel generative model, Collaborative Competitive…

多流转换 (分流,合流,基于时间的合流——双流联结 )

目录 一&#xff0c;分流 1.实现分流 2.使用侧输出流 二&#xff0c;合流 1&#xff0c;联合 2&#xff0c;连接 三&#xff0c;基于时间的合流——双流联结 1&#xff0c;窗口联结 1.1 窗口联结的调用 1.2 窗口联结的处理流程 2&#xff0c;间隔联结 2.1 间隔联…

<网络安全>《2 国内主要企业网络安全公司概览(二)》

4 北京天融信科技有限公司(简称天融信) 信息内容LOGO成立日期创始于1995年总部北京市海淀区上地东路1号院3号楼北侧301室背景民营企业是否上市天融信[002212]A股市值99亿主要产品网络安全大数据云服务员工规模6000多人简介天融信科技集团&#xff08;证券代码&#xff1a;0022…

书生·浦语大模型实战营-学习笔记6

目录 OpenCompass大模型测评1. 关于评测1.1 为什么要评测&#xff1f;1.2 需要评测什么&#xff1f;1.3 如何评测&#xff1f;1.3.1 客观评测1.3.2 主观评测1.3.3 提示词工程评测 2. 介绍OpenCompass工具3. 实战演示 OpenCompass大模型测评 1. 关于评测 1.1 为什么要评测&#…

人工智能系列 :与机器共生的未来

大家好&#xff0c;身处一个日新月异的时代&#xff0c;科技的浪潮汹涌而至&#xff0c;将人们推向未知的前方&#xff0c;一个充满人工智能与机器的世界。 这个未知的境地&#xff0c;或许令人心生恐慌&#xff0c;因为它的庞大未知性仿佛一团迷雾&#xff0c;模糊了大家的视…

Unity Mask合批情况验证

1.首先是两个Mask完全重合的情况下 每张图片使用的image都来自同一个图集 发现彼此之间是没有合批的&#xff0c;但是每个Mask内部是实现了合批的 经过计算此种情况的visiableList&#xff1a;mask1&#xff0c;IM1&#xff0c;IM2&#xff0c;mask2&#xff0c;IM3&#xf…

Docker安装Clickhouse详细教程

简介 ClickHouse是一种列式数据库管理系统&#xff0c;专门用于高性能数据分析和数据仓库应用。它是一个开源的数据库系统&#xff0c;最初由俄罗斯搜索引擎公司Yandex开发&#xff0c;用于满足大规模数据分析和报告的需求。 特点 开源的列式存储数据库管理系统&#xff0c;…

不学前沿技术与朽木浮草何异 ?Java18新特性

不学前沿技术与朽木浮草何异 &#xff1f;Java18新特性 文章目录 不学前沿技术与朽木浮草何异 &#xff1f;Java18新特性JEP 400:默认字符集为 UTF-8JEP 408:简易的 Web 服务器JEP 413:优化 Java API 文档中的代码片段JEP 416:使用方法句柄重新实现反射核心JEP 417: 向量 API&a…

06.搭建一个自己的私有仓库-Gitea

06.搭建一个自己的私有仓库-Gitea | DLLCNX的博客 如果你是一位程序员或者IT相关领域的从业者&#xff0c;那么肯定知道git&#xff0c;而且也或多或少接触了不少开源仓库以及公司的私有仓库&#xff0c;但是我们有没有想过自己也搭建一个私有仓库呢。 这么多开源仓库&#xf…

【51单片机系列】proteus中的LCD12864液晶屏

文章来源&#xff1a;《单片机C语言编程与Proteus仿真技术》。 点阵字符型LCD显示模块只能显示英文字符和简单的汉字&#xff0c;要想显示较为复杂的汉字或图形&#xff0c;就必须采用点阵图型LCD显示模块&#xff0c;比如12864点阵图型LCD显示模块。 文章目录 一、 LCD12864点…

The Sandbox 2024 Game Jam 进行中|游戏创作比赛!冲!

The Sandbox Game Jam 是面向所有游戏制作爱好者的创作比赛&#xff01;我们诚邀您加入 The Sandbox 的生态系统&#xff0c;这里充满活力&#xff0c;游戏与文化相融&#xff0c;创作者彼此切磋&#xff0c;共同实现梦想。请尽情发挥你的想象力&#xff01;The Sandbox 游戏由…

深度学习与大数据在自然语言处理中的应用与进展

引言 在当今社会&#xff0c;深度学习和大数据技术的快速发展为自然语言处理&#xff08;NLP&#xff09;领域带来了显著的进步。这种技术能够使计算机更好地理解和生成人类语言&#xff0c;从而推动了搜索引擎、语音助手、机器翻译等领域的创新和改进。 NLP的发展与技术进步…

使用Robot Framework实现多平台自动化测试

基于Robot Framework、Jenkins、Appium、Selenium、Requests、AutoIt等开源框架和技术&#xff0c;成功打造了通用自动化测试持续集成管理平台&#xff08;以下简称“平台”&#xff09;&#xff0c;显著提高了测试质量和测试用例的执行效率。 01、设计目标 平台通用且支持不…

Linux操作系统——进程间通信简单介绍

1.进程间通信的介绍 我们之前所谈的进程很多都是通过一个进程来进行理解的&#xff0c;可是很多情况下有一些任务呢&#xff0c;他是有很多的进程通过协作完成&#xff0c;比如说我们之前谈到的命令行&#xff0c;一条命令也是用一个进程去执行的。 像这样&#xff0c;我们是通…

一套令我获益颇多的生活模式

你有没有过这样的感受&#xff1a; 有时候&#xff0c;会觉得自己不够好&#xff0c;于是下定决心&#xff0c;做好计划&#xff0c;打算作出一些改变。 但坚持了两天&#xff0c;却又故态复萌&#xff0c;回到旧的模式里。仿佛有一种力量把你往回拉扯&#xff0c;强迫你重复着…

用这个烟感监测技术!同事下巴都惊掉了!

在当今社会&#xff0c;火灾作为一种极具破坏性的灾害&#xff0c;对人们的生命和财产安全构成着严峻的威胁。 为了更好地预防和管理火灾风险&#xff0c;烟感监控系统成为一项不可或缺的技术创新。为各行各业提供了全方位、高效的火灾预警和防范手段。 客户案例 商业办公楼 …