1. 概述
在涉及状态流转类操作的业务中,通常都需要控制 操作
和 状态
的对应关系,以控制数据在某些状态下可以进行哪些操作,哪些操作不被允许,如果是比较复杂的业务,可能会通过流程引擎控制,但如果是一般的状态控制,则可以通过状态转换和操作的对应关系去控制。
2. 需求
定义如下业务 状态
和全部可能的 操作
状态:输入、进行中、已终止、已完成
操作:开始订货、终止、删除、完成
当前状态下可进行的操作规则如下:
当前状态 | 可进行的操作 | 操作 | 操作后状态 |
输入 | 开始订货/删除 | 开始订货 | 进行中 |
输入 | 开始订货/删除 | 删除 | 已删除 |
进行中 | 完成/终止 | 完成 | 已完成 |
进行中 | 完成/终止 | 终止 | 已终止 |
已终止 | 开始订货 | 开始订货 | 进行中 |
已完成 | 无 | - | - |
表现在页面如下(可进行的操作是蓝色按钮,不可进行的操作则置灰):
3. 分析
一个操作可以有多个可操作状态,只有当数据处于可操作状态下时才可进行该操作
一个操作只会有一个结果状态
4. 实现
我们可以定义两个枚举,状态枚举(StatusEnum)
和 操作枚举(OperationEnum)
,在操作枚举中包含结果状态和可操作状态,由于可操作性状态是多个,则用可变参数(数组)表示(其他类型参数亦可)。通过定义可操作性状态,我们可以很容易控制某个操作
只有在这些特定的状态
下才可操作,而无需通过if-else
逻辑控制。
状态枚举(StatusEnum)
@NoArgsConstructor
@AllArgsConstructor
public enum StatusEnum {
INIT(0, "输入"),
EXECUTING(1, "进行中"),
ABORT(2, "已终止"),
FINISHED(3, "已完成");
@Getter
private Integer code;
@Getter
private String desc;
public static String getDescByCode(Integer code) {
return Arrays.stream(StatusEnum.values())
.filter(e -> e.getCode().equals(code))
.map(StatusEnum::getDesc)
.findFirst()
.orElse(null);
}
}
操作枚举(OperationEnum)
在操作枚举中定义结果状态 StatusEnum resultStatus
和 可操作状态 StatusEnum[] availableStatus
。例如,只有在数据处于EXECUTING(1, "进行中")
时才可进行 FINISH(2, "完成")
操作。
@NoArgsConstructor
public enum OperationEnum {
START_ORDER(1, "开始订货", StatusEnum.EXECUTING, StatusEnum.INIT, StatusEnum.ABORT),
FINISH(2, "完成", StatusEnum.FINISHED, StatusEnum.EXECUTING),
STOP(3, "终止", StatusEnum.ABORT, StatusEnum.EXECUTING),
DELETE(4, "删除", null, StatusEnum.INIT);
@Getter
private Integer code;
@Getter
private String desc;
@Getter
private StatusEnum resultStatus;
@Getter
private StatusEnum[] availableStatus;
OperationEnum(Integer code, String desc, StatusEnum resultStatus, StatusEnum... availableStatus) {
this.code = code;
this.desc = desc;
this.resultStatus = resultStatus;
this.availableStatus = availableStatus;
}
/**
* 获取全部操作 code 集合
*
* @return 全部操作 code 集合
*/
public static List<Integer> getOperationCodeList() {
return Arrays.stream(OperationEnum.values()).map(OperationEnum::getCode).collect(Collectors.toList());
}
/**
* 获取某项操作的结果状态
*
* @param code 操作code
* @return 结果状态
*/
public static Integer getResultStatusCodeByCode(Integer code) {
for (OperationEnum operationEnum : OperationEnum.values()) {
if (operationEnum.getCode().equals(code)) {
StatusEnum resultStatus = operationEnum.getResultStatus();
if (resultStatus != null) {
return resultStatus.getCode();
}
}
}
return null;
}
/**
* 获取某项操作的可操作状态
*
* @param code 操作code
* @return 可操作状态code集合
*/
public static List<Integer> getAvailableStatusCodeList(Integer code) {
for (OperationEnum operationEnum : OperationEnum.values()) {
if (operationEnum.getCode().equals(code)) {
StatusEnum[] availableStatus = operationEnum.getAvailableStatus();
return Arrays.stream(availableStatus).map(StatusEnum::getCode).collect(Collectors.toList());
}
}
return new ArrayList<>();
}
}
有了上述操作
枚举及对应状态
枚举的关系,可以定义获取某项操作的可操作状态的方法getAvailableStatusCodeList(Integer)
,进而在接口中判断当前操作是否可执行,如果可以执行,则可通过获取结果状态方法getResultStatusCodeByCode(Integer)
设置操作的结果状态。