<think>我们设计一个工单状态机,包含10种状态,并使用RabbitMQ来保证状态变更的最终一致性。
状态机设计要点:
1. 定义10种状态:例如:新建、已分配、处理中、待审核、已驳回、已解决、已关闭、重新打开、已取消、挂起等(具体根据业务需求)。
2. 状态流转规则:定义每个状态可以流转到哪些状态,例如“新建”可以流转到“已分配”或“已取消”。
3. 使用RabbitMQ:当工单状态发生变更时,将状态变更事件发布到RabbitMQ,然后由消费者异步处理状态更新,确保最终一致性。
实现步骤:
a. 状态定义:枚举类(State)定义所有状态。
b. 状态流转规则:使用状态机配置(例如使用Spring StateMachine或自定义状态机规则)。
c. 状态变更事件:当工单状态变更时,将变更事件(包括工单ID、原状态、目标状态、变更时间等)发送到RabbitMQ。
d. 消费者:监听队列,接收到事件后更新数据库中的工单状态,并记录状态变更历史。
注意:为了确保最终一致性,在生产者端(状态变更请求)需要将事件可靠地发送到RabbitMQ,消费者端需要保证幂等性。
下面是一个简化的Java实现示例(使用Spring Boot, Spring StateMachine, RabbitMQ):
由于状态机配置复杂,这里只展示核心流程。</think>### 工单状态机实现(10种状态流转 + RabbitMQ最终一致性)
#### 状态定义与流转规则(示例)
```mermaid
stateDiagram-v2
[*] --> 新建
新建 --> 已分配: 分配资源
已分配 --> 处理中: 开始处理
处理中 --> 待审核: 提交审核
待审核 --> 已驳回: 审核不通过
待审核 --> 已解决: 审核通过
已驳回 --> 处理中: 重新处理
处理中 --> 挂起: 需要外部协助
挂起 --> 处理中: 问题解决
处理中 --> 已取消: 用户取消
已解决 --> 已关闭: 用户确认
已解决 --> 重新打开: 用户反馈
重新打开 --> 处理中: 重新分配
已关闭 --> [*]
```
#### Java实现(Spring Boot + RabbitMQ)
```java
// 1. 状态枚举定义
public enum TicketState {
NEW, // 新建
ASSIGNED, // 已分配
IN_PROGRESS, // 处理中
PENDING_REVIEW,// 待审核
REJECTED, // 已驳回
RESOLVED, // 已解决
CLOSED, // 已关闭
REOPENED, // 重新打开
CANCELLED, // 已取消
SUSPENDED // 挂起
}
// 2. 状态转换服务(核心状态机)
@Service
@Transactional
public class StateTransitionService {
private static final Map<TicketState, Set<TicketState>> VALID_TRANSITIONS = Map.of(
TicketState.NEW, Set.of(TicketState.ASSIGNED, TicketState.CANCELLED),
TicketState.ASSIGNED, Set.of(TicketState.IN_PROGRESS, TicketState.CANCELLED),
TicketState.IN_PROGRESS, Set.of(TicketState.PENDING_REVIEW, TicketState.SUSPENDED, TicketState.CANCELLED),
TicketState.PENDING_REVIEW, Set.of(TicketState.REJECTED, TicketState.RESOLVED),
TicketState.REJECTED, Set.of(TicketState.IN_PROGRESS),
TicketState.RESOLVED, Set.of(TicketState.CLOSED, TicketState.REOPENED),
TicketState.REOPENED, Set.of(TicketState.IN_PROGRESS),
TicketState.SUSPENDED, Set.of(TicketState.IN_PROGRESS)
);
@Autowired private TicketRepository ticketRepo;
@Autowired private RabbitTemplate rabbitTemplate;
// 状态变更请求
public void changeState(Long ticketId, TicketState newState) {
Ticket ticket = ticketRepo.findById(ticketId)
.orElseThrow(() -> new TicketNotFoundException(ticketId));
// 验证状态转换合法性
if (!VALID_TRANSITIONS.get(ticket.getState()).contains(newState)) {
throw new InvalidStateTransitionException(ticket.getState(), newState);
}
// 发送状态变更事件到RabbitMQ(不立即更新DB)
StateChangeEvent event = new StateChangeEvent(ticketId, ticket.getState(), newState);
rabbitTemplate.convertAndSend("state-change-exchange", "state.change", event);
}
}
// 3. RabbitMQ事件定义
public class StateChangeEvent implements Serializable {
private Long ticketId;
private TicketState fromState;
private TicketState toState;
private LocalDateTime timestamp = LocalDateTime.now();
// 构造方法/getters/setters
}
// 4. 状态变更消费者(保证最终一致性)
@Component
@RabbitListener(queues = "state-change-queue")
public class StateChangeConsumer {
@Autowired private TicketRepository ticketRepo;
@RabbitHandler
@Transactional
public void handleEvent(StateChangeEvent event) {
// 幂等性检查(通过事件ID或时间戳)
if (isDuplicate(event)) return;
Ticket ticket = ticketRepo.findById(event.getTicketId())
.orElseThrow(() -> new TicketNotFoundException(event.getTicketId()));
// 二次验证状态转换(防止并发冲突)
if (VALID_TRANSITIONS.get(ticket.getState()).contains(event.getToState())) {
ticket.setState(event.getToState());
ticketRepo.save(ticket);
logStateChange(ticket, event.getFromState());
} else {
// 触发补偿机制(如发送告警)
handleInvalidTransition(event);
}
}
private void logStateChange(Ticket ticket, TicketState previousState) {
// 记录状态变更历史(单独历史表)
}
}
// 5. RabbitMQ配置
@Configuration
public class RabbitConfig {
@Bean
public TopicExchange stateExchange() {
return new TopicExchange("state-change-exchange");
}
@Bean
public Queue stateQueue() {
return new Queue("state-change-queue", true);
}
@Bean
public Binding stateBinding() {
return BindingBuilder.bind(stateQueue())
.to(stateExchange())
.with("state.change");
}
@Bean
public Jackson2JsonMessageConverter jsonConverter() {
return new Jackson2JsonMessageConverter();
}
}
```
#### 最终一致性保障机制
1. **生产者端可靠性**:
```java
// 添加发送确认回调
rabbitTemplate.setConfirmCallback((correlation, ack, reason) -> {
if (!ack) {
// 记录失败事件并重试
retryService.retrySend(event);
}
});
```
2. **消费者端幂等性**:
```java
private boolean isDuplicate(StateChangeEvent event) {
// 通过Redis SETNX检查事件ID
String key = "event:" + event.getTicketId() + ":" + event.getToState();
return !redisTemplate.opsForValue().setIfAbsent(key, "1", 1, TimeUnit.HOURS);
}
```
3. **死信处理**:
```java
@Bean
public Queue dlq() {
return QueueBuilder.durable("state-change-dlq")
.withArgument("x-dead-letter-exchange", "state-change-exchange")
.withArgument("x-dead-letter-routing-key", "state.change.retry")
.build();
}
```
#### 系统优势
1. **状态隔离**:前端请求与DB更新解耦
2. **最终一致性**:RabbitMQ保证事件至少投递一次
3. **可追溯性**:完整的状态变更历史记录
4. **弹性扩展**:可水平扩展消费者实例
5. **容错能力**:DLQ机制处理异常事件