鸿蒙实现滴滴出行项目之乘客下单以及司机听单功能
鸿蒙的分布式能力(如分布式数据管理、跨设备通信)可以优化乘客与司机端的交互。
·
1、 系统架构设计
出行类应用通常分为三个模块:
- 乘客端:发布行程请求、选择车型、支付等。
- 司机端:接收订单、导航、接单/拒单等。
- 服务端:处理订单匹配、实时位置同步、计费等。
鸿蒙的分布式能力(如分布式数据管理、跨设备通信)可以优化乘客与司机端的交互。
2、乘客端下单功能实现
1. 前置准备
在module.json5中声明权限和依赖:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.LOCATION",
"reason": "获取乘客位置"
},
{
"name": "ohos.permission.INTERNET"
}
]
},
"dependencies": {
"@ohos/geolocation": "1.0.0", // 定位
"@ohos/net/http": "1.0.0", // 网络请求
"@ohos/notification": "1.0.0" // 通知
}
}
2. 乘客下单功能完整代码
// src/ets/pages/OrderPage.ets
import geolocation from '@ohos.geolocation';
import http from '@ohos.net.http';
import notification from '@ohos.notification';
import router from '@ohos.router';
@Entry
@Component
struct OrderPage {
@State startAddress: string = ''; // 起点地址
@State endAddress: string = ''; // 终点地址
@State carType: string = 'economy'; // 车型
@State orderStatus: string = '未下单';
// 获取当前位置
async getCurrentLocation() {
try {
const location = await geolocation.getCurrentLocation();
this.startAddress = `${location.latitude.toFixed(6)},${location.longitude.toFixed(6)}`;
console.info('当前位置:', this.startAddress);
} catch (err) {
console.error('定位失败:', err);
}
}
// 提交订单到服务端
async submitOrder() {
if (!this.startAddress || !this.endAddress) {
notification.show({ contentText: '请选择起点和终点' });
return;
}
this.orderStatus = '提交中...';
const orderData = {
start: this.startAddress,
end: this.endAddress,
carType: this.carType,
userId: 'user_123' // 实际替换为登录用户ID
};
try {
const httpRequest = http.createHttp();
const response = await httpRequest.request(
'https://your-api-server.com/orders/create',
{
method: 'POST',
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify(orderData)
}
);
if (response.responseCode === 200) {
this.orderStatus = '已接单';
notification.show({ contentText: '订单已发布,等待司机接单' });
this.listenOrderStatus(JSON.parse(response.result).orderId); // 开始监听订单状态
} else {
this.orderStatus = '提交失败';
}
} catch (err) {
console.error('订单提交失败:', err);
this.orderStatus = '网络错误';
}
}
// 监听订单状态变化(WebSocket)
listenOrderStatus(orderId: string) {
const ws = new WebSocket('wss://your-api-server.com/orders/ws/' + orderId);
ws.onopen = () => {
console.info('WebSocket连接已建立');
};
ws.onmessage = (event: MessageEvent) => {
const data = JSON.parse(event.data);
if (data.status === 'DRIVER_ACCEPTED') {
this.orderStatus = '司机已接单';
notification.show({
contentTitle: '司机已接单',
contentText: `车牌号: ${data.driverInfo.licensePlate}`
});
router.pushUrl({ url: 'pages/DriverTrackingPage' }); // 跳转到司机追踪页
}
};
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
};
}
build() {
Column() {
// 地址输入区域
TextInput({ placeholder: '起点' })
.onChange((value: string) => { this.startAddress = value })
.width('90%')
.margin(10);
TextInput({ placeholder: '终点' })
.onChange((value: string) => { this.endAddress = value })
.width('90%')
.margin(10);
// 车型选择
Picker({ range: ['economy', 'comfort', 'luxury'] })
.onChange((value: string) => { this.carType = value })
.width('90%')
.margin(10);
// 定位按钮
Button('获取当前位置')
.onClick(() => this.getCurrentLocation())
.width('90%')
.margin(10);
// 下单按钮
Button('呼叫车辆')
.onClick(() => this.submitOrder())
.width('90%')
.margin(10)
.backgroundColor('#FF007DFF')
.fontColor(Color.White);
// 订单状态显示
Text(`订单状态: ${this.orderStatus}`)
.fontSize(16)
.margin(10);
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
}
3. 关键代码解析
定位功能:
- 使用@ohos/geolocation获取经纬度坐标。
- 需在module.json5中声明LOCATION权限。
订单提交:
- 通过@ohos/net/http发送 POST 请求到服务端。
- 请求体包含起点、终点、车型等数据。
状态监听:
- 建立 WebSocket 连接实时接收订单状态(如司机接单)。
- 使用@ohos/notification显示接单通知。
跨页面跳转:
- 接单后通过router.pushUrl跳转到司机追踪页面。
4. 服务端接口要求
订单创建接口(POST):
https://your-api-server.com/orders/create
请求体: { start, end, carType, userId }
响应: { orderId, status }
WebSocket 推送地址:
wss://your-api-server.com/orders/ws/{orderId}
推送消息: { status: 'DRIVER_ACCEPTED', driverInfo: { ... } }
3、司机端听单功能实现
1. 前置准备
在module.json5中声明权限和依赖:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.LOCATION",
"reason": "获取司机实时位置"
},
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "后台持续监听订单"
}
]
},
"dependencies": {
"@ohos/geolocation": "1.0.0",
"@ohos/net/http": "1.0.0",
"@ohos/notification": "1.0.0",
"@ohos/websocket": "1.0.0"
}
}
2. 司机端听单功能完整代码
// src/ets/pages/DriverListenPage.ets
import geolocation from '@ohos/geolocation';
import http from '@ohos/net/http';
import notification from '@ohos/notification';
import websocket from '@ohos/websocket';
import router from '@ohos/router';
@Entry
@Component
struct DriverListenPage {
@State currentLocation: string = ''; // 当前经纬度
@State isListening: boolean = false; // 是否在听单中
@State newOrder: any = null; // 最新订单数据
private ws: websocket.WebSocket | null = null;
// 实时上报位置到服务端
startReportingLocation() {
setInterval(async () => {
try {
const location = await geolocation.getCurrentLocation();
this.currentLocation = `${location.latitude},${location.longitude}`;
// 如果已连接听单服务,上报位置
if (this.isListening) {
this.reportLocationToServer(this.currentLocation);
}
} catch (err) {
console.error('定位失败:', err);
}
}, 5000); // 每5秒上报一次
}
// 上报位置到服务端(HTTP)
async reportLocationToServer(location: string) {
const httpRequest = http.createHttp();
try {
await httpRequest.request(
'https://your-api-server.com/drivers/location',
{
method: 'POST',
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify({
driverId: 'driver_123', // 实际替换为司机ID
location: location
})
}
);
} catch (err) {
console.error('位置上报失败:', err);
}
}
// 开始听单(WebSocket连接)
startListeningOrders() {
this.ws = new websocket.WebSocket('wss://your-api-server.com/drivers/ws/listen');
this.ws.on('open', () => {
this.isListening = true;
console.info('听单服务已连接');
// 首次连接时发送司机信息
this.ws?.send(JSON.stringify({
type: 'register',
driverId: 'driver_123',
carType: 'economy'
}));
});
// 监听新订单推送
this.ws.on('message', (data: string) => {
const message = JSON.parse(data);
if (message.type === 'new_order') {
this.newOrder = message.order;
this.showNewOrderNotification();
}
});
this.ws.on('close', () => {
this.isListening = false;
console.warn('听单服务已断开');
});
}
// 显示新订单通知(鸿蒙通知+按钮)
showNewOrderNotification() {
notification.show({
id: 1, // 通知ID(用于后续更新)
contentTitle: '新订单待接单',
contentText: `从 ${this.newOrder.start} 到 ${this.newOrder.end}`,
buttons: [
{
text: '接单',
action: () => this.acceptOrder()
},
{
text: '拒单',
action: () => this.rejectOrder()
}
]
});
}
// 接单逻辑
async acceptOrder() {
if (!this.newOrder) return;
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(
'https://your-api-server.com/orders/accept',
{
method: 'POST',
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify({
orderId: this.newOrder.id,
driverId: 'driver_123'
})
}
);
if (response.responseCode === 200) {
notification.show({ contentText: '接单成功,请前往接客' });
router.pushUrl({ url: 'pages/NavigationPage' }); // 跳转到导航页
}
} catch (err) {
notification.show({ contentText: '接单失败,请重试' });
}
}
// 拒单逻辑
async rejectOrder() {
this.newOrder = null; // 清空当前订单
notification.cancel(1); // 关闭通知
}
// 停止听单
stopListening() {
this.ws?.close();
this.isListening = false;
}
onPageShow() {
this.startReportingLocation(); // 启动位置上报
this.startListeningOrders(); // 开始听单
}
onPageHide() {
this.stopListening(); // 页面隐藏时停止听单
}
build() {
Column() {
// 状态显示区域
Text(this.isListening ? '听单中...' : '未听单')
.fontSize(20)
.margin(10);
Text(`当前位置: ${this.currentLocation || '未知'}`)
.fontSize(16)
.margin(10);
// 手动操作按钮
Button(this.isListening ? '停止听单' : '开始听单')
.onClick(() => this.isListening ? this.stopListening() : this.startListeningOrders())
.width('90%')
.margin(10);
// 订单信息卡片(有新订单时显示)
if (this.newOrder) {
Card() {
Column() {
Text(`起点: ${this.newOrder.start}`).margin(5);
Text(`终点: ${this.newOrder.end}`).margin(5);
Text(`车型: ${this.newOrder.carType}`).margin(5);
}
.padding(10)
}
.margin(10)
.width('90%')
}
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center)
}
}
3. 关键代码解析
实时位置上报:
- 通过geolocation每5秒获取一次位置,通过HTTP上报到服务端。
- 服务端根据位置匹配附近订单(代码中未展示服务端逻辑)。
WebSocket听单:
- 建立长连接接收实时订单推送。
- 收到新订单后触发鸿蒙通知(带接单/拒单按钮)。
接单/拒单处理:
- 接单:调用服务端接口确认接单,跳转到导航页。
- 拒单:关闭通知并清空当前订单数据。
后台保活:
- 声明KEEP_BACKGROUND_RUNNING权限保证后台持续监听。
4. 服务端接口要求
接口类型 地址 说明
HTTP POST https://your-api-server.com/drivers/location 司机位置上报(经纬度字符串)
WebSocket wss://your-api-server.com/drivers/ws/listen 监听新订单(需发送driverId注册)
HTTP POST https://your-api-server.com/orders/accept 提交接单请求
5. 优化建议
断线重连:
this.ws.on('close', () => {
setTimeout(() => this.startListeningOrders(), 3000); // 3秒后重连
});
省电模式:
根据电量动态调整位置上报频率(低电量时降低频率)。
分布式协同:
// 在车机大屏上显示订单详情(需配合@ohos.distributedDeviceManager)
import deviceManager from '@ohos.distributedDeviceManager';
deviceManager.publish('order_detail', this.newOrder);
订单过滤:
司机端本地缓存拒单记录,避免重复推送同一订单。
4、服务端平台功能实现
1. 服务端架构设计

2. 数据库设计(MySQL)

3. 核心代码实现(Spring Boot)
(1) 订单创建接口
// OrderController.java
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/create")
public ResponseEntity<Order> createOrder(@RequestBody OrderCreateDTO dto) {
Order order = orderService.createOrder(
dto.getPassengerId(),
dto.getStartLat(),
dto.getStartLng(),
dto.getEndAddress(),
dto.getCarType()
);
return ResponseEntity.ok(order);
}
}
(2) 司机位置上报与听单服务
// DriverService.java
@Service
public class DriverService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 司机位置更新(存入Redis Geo)
public void updateDriverLocation(String driverId, double lat, double lng) {
redisTemplate.opsForGeo().add(
"driver_locations",
new Point(lng, lat),
driverId
);
// 更新司机状态到MySQL
driverRepository.updateLocation(driverId, lat, lng, new Date());
}
// 查找附近可用司机
public List<String> findNearbyDrivers(double lat, double lng, double radiusKm, String carType) {
Circle area = new Circle(new Point(lng, lat),
new Distance(radiusKm, Metrics.KILOMETERS));
RedisGeoCommands.GeoRadiusCommandArgs args = GeoRadiusCommandArgs
.newGeoRadiusArgs()
.includeCoordinates()
.sortAscending(); // 按距离排序
return redisTemplate.opsForGeo()
.radius("driver_locations", area, args)
.getContent()
.stream()
.map(geoResult -> geoResult.getContent().getName())
.collect(Collectors.toList());
}
}
(3) 订单-司机匹配逻辑
// OrderMatchingService.java
@Service
public class OrderMatchingService {
@Autowired
private WebSocketHandler webSocketHandler;
@Scheduled(fixedRate = 5000) // 每5秒执行一次订单匹配
public void matchOrders() {
List<Order> pendingOrders = orderRepository.findByStatus("pending");
for (Order order : pendingOrders) {
List<String> driverIds = driverService.findNearbyDrivers(
order.getStartLat(),
order.getStartLng(),
5.0, // 5公里半径
order.getCarType()
);
if (!driverIds.isEmpty()) {
// 推送给第一个司机(可根据评分等优化)
webSocketHandler.sendToDriver(
driverIds.get(0),
new OrderNotification(order)
);
}
}
}
}
(4) WebSocket实时通信
// OrderWebSocketHandler.java
@Component
public class OrderWebSocketHandler extends TextWebSocketHandler {
private static final Map<String, WebSocketSession> driverSessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
String driverId = session.getHandshakeHeaders().getFirst("driver-id");
driverSessions.put(driverId, session);
}
// 向指定司机推送订单
public void sendToDriver(String driverId, OrderNotification notification) {
WebSocketSession session = driverSessions.get(driverId);
if (session != null && session.isOpen()) {
session.sendMessage(new TextMessage(notification.toJSON()));
}
}
// 处理司机接单/拒单消息
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
JSONObject json = new JSONObject(message.getPayload());
if ("accept".equals(json.getString("action"))) {
orderService.acceptOrder(
json.getString("orderId"),
json.getString("driverId")
);
}
}
}
4. 关键API列表

5. 华为云部署建议


6. 扩展功能实现
(1) 订单状态机(防止重复接单)
// Order.java 中定义状态流转
public enum OrderStatus {
PENDING {
@Override
public boolean canTransitionTo(OrderStatus newStatus) {
return newStatus == MATCHED || newStatus == CANCELLED;
}
},
MATCHED {
@Override
public boolean canTransitionTo(OrderStatus newStatus) {
return newStatus == COMPLETED;
}
}
}
(2) 分布式锁(防止订单重复分配)
// 使用Redis实现分布式锁
public boolean tryLockOrder(String orderId) {
return redisTemplate.opsForValue()
.setIfAbsent("lock:" + orderId, "1", 10, TimeUnit.SECONDS);
}
(3) 鸿蒙服务端推送(集成华为Push Kit)
// 向鸿蒙设备推送通知
public void pushToHarmony(String deviceToken, String message) {
HttpClient.newBuilder()
.build()
.send(
HttpRequest.newBuilder()
.uri(URI.create("https://push-api.cloud.huawei.com/v1/notification"))
.header("Authorization", "Bearer {access_token}")
.POST(HttpRequest.BodyPublishers.ofString(
new JSONObject()
.put("device_tokens", new String[]{deviceToken})
.put("message", message)
.toString()
))
.build(),
HttpResponse.BodyHandlers.ofString()
);
}
5、疑点总结

1、Redis的GEO功能进行纯内存计算怎么找到附近的司机

实际就是司机都上报了位置信息并存入到redis中,然后构造中心位置距离参数和范围参数,去redis中过滤出来位置最近的10个司机。

更多推荐




所有评论(0)