一、功能概述
订单状态追踪是生鲜电商系统的核心功能之一,它能够让用户实时了解订单从下单到配送完成的整个流程,提升用户体验和信任度。叮咚买菜系统的订单状态追踪需要实现以下关键功能:
1. 实时状态更新
2. 多节点状态展示
3. 异常状态预警
4. 历史订单查询
5. 配送员位置追踪(可选)
二、系统架构设计
1. 整体架构
```
客户端(APP/小程序/Web)
↓
API网关
↓
订单服务 ←→ 状态机引擎 ←→ 消息队列
↓ ↑
数据库(订单主表、状态日志表、操作记录表)
↓
第三方服务(短信/推送/地图API)
```
2. 核心组件
- 状态机引擎:管理订单状态流转规则
- 消息队列:处理状态变更事件
- 实时推送服务:将状态变更通知用户
- 日志系统:记录完整状态变更历史
三、订单状态模型设计
1. 状态定义
```
1. 待支付
2. 已支付待接单
3. 已接单待分拣
4. 分拣中
5. 分拣完成待配送
6. 配送中
7. 已送达
8. 已取消
9. 异常订单(超时/缺货等)
```
2. 状态流转图
```
[待支付] →(支付成功)→ [已支付待接单] →(商家接单)→ [已接单待分拣]
↑ ↓
(取消订单) (开始分拣)→ [分拣中]
↓
[分拣完成待配送] →(分配骑手)→ [配送中]
↓
[已送达] ←(用户确认收货)← [配送中]
```
四、技术实现方案
1. 数据库设计
```sql
-- 订单主表
CREATE TABLE orders (
order_id VARCHAR(32) PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status TINYINT NOT NULL DEFAULT 0 COMMENT 0-待支付,1-已支付待接单,...,
create_time DATETIME NOT NULL,
update_time DATETIME NOT NULL,
-- 其他订单字段...
);
-- 订单状态日志表
CREATE TABLE order_status_log (
log_id BIGINT AUTO_INCREMENT PRIMARY KEY,
order_id VARCHAR(32) NOT NULL,
from_status TINYINT NOT NULL,
to_status TINYINT NOT NULL,
operator_type TINYINT NOT NULL COMMENT 0-系统,1-用户,2-商家,3-骑手,
operator_id VARCHAR(32),
change_time DATETIME NOT NULL,
remark VARCHAR(255),
INDEX idx_order_id (order_id),
INDEX idx_change_time (change_time)
);
```
2. 状态机引擎实现
使用有限状态机(FSM)模式管理订单状态流转:
```java
public class OrderStateMachine {
private static final Map
> STATE_TRANSITIONS = Map.of(
0, Set.of(1), // 待支付 → 已支付
1, Set.of(2, 8), // 已支付 → 已接单/已取消
// 其他状态转换规则...
);
public boolean canTransition(int fromStatus, int toStatus) {
return STATE_TRANSITIONS.getOrDefault(fromStatus, Collections.emptySet())
.contains(toStatus);
}
public void transition(Order order, int newStatus, String operator) {
if (!canTransition(order.getStatus(), newStatus)) {
throw new IllegalStateException("Invalid state transition");
}
// 更新订单状态
order.setStatus(newStatus);
// 记录状态变更日志
OrderStatusLog log = new OrderStatusLog(
order.getId(),
order.getStatus(),
newStatus,
operator,
new Date()
);
orderStatusLogRepository.save(log);
// 触发后续操作(通知、推送等)
eventPublisher.publish(new OrderStatusChangedEvent(order.getId(), newStatus));
}
}
```
3. 实时状态更新实现
方案一:轮询方式(简单实现)
```javascript
// 前端实现
function pollOrderStatus(orderId) {
setInterval(async () => {
const response = await fetch(`/api/orders/${orderId}/status`);
const data = await response.json();
updateUI(data.status);
}, 5000); // 每5秒轮询一次
}
```
方案二:WebSocket推送(推荐)
```java
// 后端WebSocket处理
@ServerEndpoint("/ws/order/{orderId}")
public class OrderStatusWebSocket {
@OnOpen
public void onOpen(Session session, @PathParam("orderId") String orderId) {
// 将session与orderId关联存储
WebSocketManager.addSession(orderId, session);
}
@OnClose
public void onClose(Session session, @PathParam("orderId") String orderId) {
// 移除session
WebSocketManager.removeSession(orderId, session);
}
}
// 状态变更时推送
@Component
public class OrderStatusEventLister {
@EventListener
public void handleOrderStatusChanged(OrderStatusChangedEvent event) {
String orderId = event.getOrderId();
int newStatus = event.getNewStatus();
// 获取所有订阅该订单的session
List sessions = WebSocketManager.getSessions(orderId);
sessions.forEach(session -> {
try {
session.getBasicRemote().sendText(
JSONObject.toJSONString(new StatusUpdate(orderId, newStatus))
);
} catch (IOException e) {
// 处理异常
}
});
}
}
```
4. 异常状态处理
```java
public class OrderExceptionHandler {
@Scheduled(fixedRate = 60000) // 每分钟检查一次
public void checkStuckOrders() {
List stuckOrders = orderRepository.findByStatusInAndUpdateTimeBefore(
Arrays.asList(1, 2, 5), // 已支付待接单、已接单待分拣、配送中
LocalDateTime.now().minusMinutes(30) // 30分钟未更新
);
for (Order order : stuckOrders) {
// 根据不同状态处理
switch (order.getStatus()) {
case 1: // 已支付待接单超时
handleUnacceptedOrder(order);
break;
case 2: // 已接单待分拣超时
handleUnpackedOrder(order);
break;
case 5: // 配送中超时
handleDelayedDelivery(order);
break;
}
}
}
private void handleUnacceptedOrder(Order order) {
// 自动取消或提醒商家
if (shouldAutoCancel()) {
orderStateMachine.transition(order, 8, "SYSTEM"); // 转为已取消
notifyUser(order.getUserId(), "订单因超时未接单已自动取消");
} else {
notifyMerchant(order.getMerchantId(), "您有订单已超时未处理,请尽快接单");
}
}
// 其他处理方法...
}
```
五、前端展示实现
1. 状态时间轴展示
```vue
v-for="(log, index) in statusLogs"
:key="index"
class="timeline-item"
:class="{active: log.status === currentStatus}"
>
{{ getStatusText(log.status) }}
{{ formatTime(log.changeTime) }}
<script>
export default {
data() {
return {
statusLogs: [], // 从API获取的状态日志
currentStatus: null // 当前订单状态
};
},
methods: {
getStatusText(status) {
const statusMap = {
0: 待支付,
1: 已支付待接单,
2: 已接单待分拣,
// 其他状态映射...
};
return statusMap[status] || 未知状态;
},
// 其他方法...
}
};
```
2. 地图配送追踪(可选)
```javascript
// 使用高德/百度地图API展示骑手位置
function initMap(orderId) {
const map = new AMap.Map(map-container);
// 订阅订单位置更新
const eventSource = new EventSource(`/api/orders/${orderId}/location-stream`);
eventSource.onmessage = function(e) {
const data = JSON.parse(e.data);
const marker = new AMap.Marker({
position: [data.longitude, data.latitude],
map: map
});
// 更新骑手位置
marker.setPosition([data.longitude, data.latitude]);
};
}
```
六、性能优化考虑
1. 状态变更事件批处理:将短时间内多个状态变更合并为一条通知
2. 缓存热点订单状态:使用Redis缓存频繁访问的订单状态
3. 异步日志记录:状态变更日志采用异步写入,避免阻塞主流程
4. 分区数据库:按订单ID哈希分区,提高并发处理能力
七、安全与合规
1. 状态变更操作日志完整记录,便于审计
2. 敏感操作(如取消订单)需要二次验证
3. 用户只能查看自己订单的状态
4. 遵守数据保护法规,不泄露用户隐私
八、测试方案
1. 单元测试:测试状态机引擎的各种状态转换
2. 集成测试:测试从状态变更到通知的完整流程
3. 压力测试:模拟高并发订单状态变更场景
4. 异常测试:测试网络中断、服务故障等情况下的恢复能力
通过以上方案,叮咚买菜系统可以实现可靠、实时的订单状态追踪功能,提升用户体验和运营效率。