以下场景均来自实际业务场景

1.字段不可为空

用户id不可为空

  1. /**
  2. * 被邀请的用户id
  3. */
  4. @Matcher(notNull = "true")
  5. private Long userId;

2.字符串属性不可为空

用户名不可为空

  1. /**
  2. * 用户名
  3. */
  4. @Matcher(notBlank = "true")
  5. private String name;

3.类型只可为0和1

对应属性的类型,只可为两个值,或者多个值

  1. /**
  2. * 是否必需,0:不必填,1:必填
  3. */
  4. @Matcher(value = {"0", "1"})
  5. private Integer needKey;

4.类型为多个固定的值

对应属性的状态,只可为0、1、2、3、4、5、6、7中的一个

  1. /**
  2. * 状态:0未构建,1编译中,2打包中,3部署中,4测试中,5测试完成,6发布中,7发布完成
  3. */
  4. @Matcher(range = "[0, 8]")
  5. private Integer deployStatus = 0;

5.对应的值为邮箱

字段为邮箱判断。除了邮箱之外,还有手机号、固定电话、身份证号和IP地址这么四个固定的类型判断。

  1. /**
  2. * 邮箱
  3. */
  4. @Matcher(notNull = "false", model = FieldModel.MAIL, errMsg = "邮件:#current 不符合邮件要求")
  5. private String email;

6.对应集合的长度

前端上传的图片最多为三个

  1. /**
  2. * 预览图最多为三个
  3. */
  4. @Matcher(range = "(, 3]")
  5. private List<String> prePicUrlList;

7.字符长度最长为128

对前端传递过来的字符长度限制为128,因为数据库字段存储为最长128

  1. /**
  2. * 地址长度
  3. */
  4. @Matcher(range = "[0, 128]")
  5. private String nameStr;

8.数据为空或者不空的话,长度不能超过200

其中@Matcher内部多个属性之间的匹配是或的关系

  1. /**
  2. * 项目描述,可以为空,但是最长不可超过200
  3. */
  4. @Matcher(notBlank = "false", range = "[0, 200]", errMsg = "描述的值不可过长,最长为200")
  5. private String proDesc;

9.前端传递过来的id必须在db中存在,而且数据不可为空

@Matcher支持多个叠加形式,表示多个条件的与操作

  1. @Matcher(notNull = "true")
  2. @Matcher(customize = "com.isyscore.iop.panda.validate.ExistMatch#proIdExist", errMsg = "proId:#current在db中不存在")
  3. private Long projectId;

其中匹配的写法

  1. @Service
  2. public class ExistMatch {
  3. @Autowired
  4. private ProjectService projectService;
  5. /**
  6. * appId存在
  7. */
  8. public boolean proIdExist(Long proId) {
  9. return projectService.exist(proId);
  10. }
  11. }

10.项目名不可为空,而且数据库中不能存在,如果存在则拒绝

其中属性accept表示如果前面的条件匹配则拒绝,默认情况下都是接受

  1. /**
  2. * 项目名称
  3. */
  4. @Matcher(notBlank = "true")
  5. @Matcher(customize = "com.isyscore.iop.panda.service.ProjectService#projectNameExist", accept = false, errMsg = "已经存在名字为 #current 的项目")
  6. private String proName;

11.在某个配置项下对应的字段值

业务场景:在字段为1的时候,下面对应的值不可为空,否则报异常

  1. /**
  2. * 处理类型:0,新增;1,编辑;2,搜索;3,表展示;4,表扩展
  3. */
  4. @Matcher(range = "[0, 4]", errMsg = "不识别类型 #current")
  5. private Integer handleType;
  6. /**
  7. * 在编辑模式下,禁用的表字段
  8. */
  9. @Matcher(condition = "#current == null || (!#current.isEmpty() && #root.handleType == 1)", errMsg = "cantEditColumnList 需要在handleType为1的时候才有值")
  10. private List<String> cantEditColumnList;

12.时间必须是过去的时间

  1. /**
  2. * 应用发布时间
  3. */
  4. @Matcher(range = "past")
  5. @ApiModelProperty(value = "应用发布时间")
  6. private Date createTime;

13.分页数据必须满足>0

  1. @Matcher(range = "[0, )", errMsg = "分页数据不满足")
  2. private Integer pageNo;
  3. @Matcher(range = "[0, )", errMsg = "pageSize数据不满足")
  4. private Integer pageSize;

14.【复杂场景】应用id在不同的场景下处理方式不同

  1. 在启动构建时候,状态必须在“开始”阶段
  2. 在测试完成动作,状态必须在‘测试中’阶段
  3. 在启动发布动作,状态必须在“测试完成”阶段
  4. 在停止动作,状态必须在“部署”状态之前
  5. 退出动作,要保证应用在“部署”状态之前

此外最基本的就是应用id不可为空,而且在db中必须存在。上面的几个动作都是不同的接口,但是所有的参数都相同,那么用group是最好的方式。其中group里面可以添加多个分组,其中group相同的,则表示两个@Mather之间是与的关系

  1. @Data
  2. public class AppIdReq {
  3. @Matchers({
  4. @Matcher(notNull = "true"),
  5. @Matcher(group = {MkConstant.DEFAULT_GROUP, "startBuild", "finishTest", "startDeploy", "stop", "quite"}, customize = "com.isyscore.iop.panda.validate.ExistMatch#appIdExist", errMsg = "应用id: #current 不存在"),
  6. // 启动构建 动作的状态核查
  7. @Matcher(group = "startBuild", customize = "com.isyscore.iop.panda.validate.DeployStatusMatch#startBuild", errMsg = "应用id: #current 不在阶段'未编译',请先退出"),
  8. // 测试完成 动作的状态核查
  9. @Matcher(group = "finishTest", customize = "com.isyscore.iop.panda.validate.DeployStatusMatch#finishTest", errMsg = "应用id: #current 不在阶段'测试中'"),
  10. // 启动发布 动作的状态核查
  11. @Matcher(group = "startDeploy", customize = "com.isyscore.iop.panda.validate.DeployStatusMatch#startDeploy", errMsg = "应用id: #current 不在阶段'测试完成'"),
  12. // 停止 动作的状态核查
  13. @Matcher(group = "stop", customize = "com.isyscore.iop.panda.validate.DeployStatusMatch#stopDeploy", errMsg = "停止的动作需要保证应用 #current 在部署状态之前"),
  14. // 退出 动作的状态核查
  15. @Matcher(group = "quite", customize = "com.isyscore.iop.panda.validate.DeployStatusMatch#stopDeploy", errMsg = "退出的动作需要保证应用 #current 在部署状态之前")
  16. })
  17. @ApiModelProperty(value = "应用id", example = "42342354")
  18. private Long appId;
  19. }

对应的接口使用

@AutoCheck
@RequestMapping("${api-prefix}/deploy")
@RestController
public class DeployController {

    ...

    /**
     * 启动构建
     */
    @AutoCheck("startBuild")
    @PutMapping("startBuild")
    public Integer startBuild(@RequestBody AppIdReq appIdReq) {
        Long appId = appIdReq.getAppId();
        devopsService.startBuild(appId);
        return 1;
    }

    /**
     * 完成测试
     */
    @AutoCheck("finishTest")
    @PutMapping("finishTest")
    public Integer finishTest(@RequestBody AppIdReq appIdReq) {
        Long appId = appIdReq.getAppId();
        appService.chgDeployStatus(appId, DeployStatusEnum.TEST_FINISH);
        return 1;
    }

    /**
     * 启动发布
     */
    @AutoCheck("startDeploy")
    @PutMapping("startDeploy")
    public Integer startDeploy(@RequestBody AppIdReq appIdReq) {
        Long appId = appIdReq.getAppId();
        appService.startDeploy(appId, UserInfoContext.getUserContext().getUserId());
        return 1;
    }

    /**
     * 停止
     * <p>只有在发布之前,停止按钮可见
     */
    @AutoCheck("stop")
    @PutMapping("stop")
    public Integer stop(@RequestBody AppIdReq appIdReq) {
        appService.stopApp(appIdReq.getAppId());
        return 1;
    }

    /**
     * 退出
     * <p>如果已经进行过“发布”,则不可退出,而且该退出按钮只有在停止后才能退出
     */
    @AutoCheck("quite")
    @PutMapping("quite")
    public Integer quite(@RequestBody AppIdReq appIdReq) {
        appService.quite(appIdReq.getAppId());
        return 1;
    }
}