应设计语义明确的自定义异常类(如InvalidOrderStatusTransitionException)并在业务层前置校验状态流转合法性,结合状态枚举封装转移规则,由Controller捕获并返回400响应。

在订单状态流转中抛出自定义异常,核心是用异常明确表达业务规则被违反,而不是掩盖问题或笼统报错。关键不在于“怎么抛”,而在于“为什么抛、抛什么、谁来处理”。
设计符合业务语义的自定义异常类
不要直接 throw new RuntimeException("状态非法")。应创建有业务含义的异常类型,比如:
- OrderStateException:作为所有订单状态相关异常的父类(可选,便于统一捕获)
- InvalidOrderStatusTransitionException:明确表示「当前状态 → 目标状态」不被允许(最常用)
- OrderAlreadyConfirmedException 或 OrderCannotBeCancelledException:针对具体不可逆操作,语义更强
示例:
public class InvalidOrderStatusTransitionException extends RuntimeException {
private final String currentStatus;
private final String targetStatus;
public InvalidOrderStatusTransitionException(String currentStatus, String targetStatus) {
super(String.format("订单状态无法从 [%s] 变更为 [%s]", currentStatus, targetStatus));
this.currentStatus = currentStatus;
this.targetStatus = targetStatus;
}
// getter 省略
}
在状态变更逻辑中主动校验并抛出
不要等数据库更新失败才报错。应在业务层(如 Service 方法)做前置校验,发现非法流转立即抛出:
立即学习“Java免费学习笔记(深入)”;
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
- 获取订单当前 status 字段值
- 根据业务规则判断 targetStatus 是否允许(可用枚举 + switch、状态机库、或预定义合法转移表)
- 不合法 → new 异常并 throw;合法 → 执行后续更新
示例片段:
public void changeOrderStatus(Long orderId, String targetStatus) {
Order order = orderMapper.selectById(orderId);
if (!OrderStatus.isValidTransition(order.getStatus(), targetStatus)) {
throw new InvalidOrderStatusTransitionException(order.getStatus(), targetStatus);
}
order.setStatus(targetStatus);
orderMapper.updateById(order);
}
配合状态枚举定义合法转移规则
把状态约束显式写进代码,避免散落各处的 if 判断。推荐用枚举封装转移逻辑:
public enum OrderStatus {
CREATED,
PAID,
SHIPPED,
COMPLETED,
CANCELLED;
public boolean canTransitionTo(OrderStatus target) {
return switch (this) {
case CREATED -> target == PAID || target == CANCELLED;
case PAID -> target == SHIPPED || target == CANCELLED;
case SHIPPED -> target == COMPLETED;
case COMPLETED, CANCELLED -> false; // 终态不可变
};
}
}
这样校验就变成:if (!order.getStatus().canTransitionTo(targetStatus)) { ... },清晰且易维护。
让调用方能区分处理,而非吞掉异常
Controller 层应捕获该异常并返回合适 HTTP 状态码和提示(如 400 Bad Request + 友好消息),而不是 try-catch 吞掉或转成 500:
- 前端需要知道是用户操作错误(如重复点击发货),不是系统故障
- 日志需记录异常类型、订单号、当前/目标状态,方便排查
- 避免在 DAO 层或通用拦截器里“静默兜底”,会丢失上下文
示例 Controller 处理:
@ExceptionHandler(InvalidOrderStatusTransitionException.class)
public ResponseEntity<ApiResponse> handleStatusTransitionError(
InvalidOrderStatusTransitionException e) {
return ResponseEntity.badRequest()
.body(ApiResponse.error("状态变更失败:" + e.getMessage()));
}

















