随着移动互联网的普及,代驾小程序因其轻量便捷的特点受到用户青睐。本文将深入探讨代驾小程序的开发实现,通过具体代码示例展示核心功能的实现方法。
一、开发环境搭建
技术栈选择
前端:微信小程序 + Taro多端框架
数据库:MySQL + Redis
地图服务:腾讯位置服务
推送服务:微信模板消息
项目结构设计
daiJia-project/
├── daiJia-miniprogram/ # 小程序前端
├── daiJia-backend/ # 后端服务
├── daiJia-admin/ # 管理后台
└── daiJia-driver/ # 司机端
二、用户端核心功能实现
1. 用户定位与地址选择
// 小程序端定位代码示例
class LocationService {
// 获取用户当前位置
async getCurrentLocation() {
return new Promise((resolve, reject) => {
wx.getLocation({
type: 'gcj02',
success: (res) => {
const location = {
latitude: res.latitude,
longitude: res.longitude,
accuracy: res.accuracy
};
this.reverseGeocode(location).then(resolve);
},
fail: reject
});
});
}
// 逆地址解析
async reverseGeocode(location) {
const qqMap = require('./libs/qqmap-wx-jssdk.min.js');
const mapSDK = new qqMap({
key: 'YOUR_MAP_KEY'
});
return new Promise((resolve) => {
mapSDK.reverseGeocoder({
location: location,
success: (res) => {
resolve({
...location,
address: res.result.address,
formatted_address: res.result.formatted_addresses.recommend
});
}
});
});
}
// 地址搜索自动补全
async searchAddress(keyword) {
const url = `https://apis.map.qq.com/ws/place/v1/suggestion`;
const params = {
keyword,
region: '全国',
key: 'YOUR_MAP_KEY'
};
const res = await wx.request({
url,
data: params
});
return res.data.data;
}
}
// 在页面中使用
Page({
data: {
currentAddress: '',
addressList: []
},
onLoad() {
this.locationService = new LocationService();
this.initLocation();
},
async initLocation() {
try {
const location = await this.locationService.getCurrentLocation();
this.setData({
currentAddress: location.formatted_address
});
} catch (error) {
console.error('获取位置失败:', error);
}
},
async onSearchInput(e) {
const keyword = e.detail.value;
if (keyword.length > 1) {
const list = await this.locationService.searchAddress(keyword);
this.setData({ addressList: list });
}
}
});
2. 订单创建与状态管理
// 后端订单服务核心代码
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private DriverService driverService;
@Autowired
private MessageService messageService;
/**
* 创建代驾订单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public OrderDTO createOrder(OrderCreateRequest request) {
// 1. 参数验证
validateOrderRequest(request);
// 2. 价格计算
BigDecimal estimatedPrice = calculatePrice(request);
// 3. 创建订单实体
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(request.getUserId());
order.setStartAddress(request.getStartAddress());
order.setEndAddress(request.getEndAddress());
order.setStartLocation(request.getStartLocation());
order.setEndLocation(request.getEndLocation());
order.setEstimatedPrice(estimatedPrice);
order.setStatus(OrderStatus.WAITING_DRIVER);
order.setCreateTime(new Date());
// 4. 保存订单
orderMapper.insert(order);
// 5. 寻找附近司机
List<Driver> nearbyDrivers = findNearbyDrivers(
request.getStartLocation(),
5.0 // 5公里范围内
);
// 6. 推送订单给司机
pushOrderToDrivers(order, nearbyDrivers);
// 7. 返回订单信息
return convertToDTO(order);
}
/**
* 智能匹配司机算法
*/
private List<Driver> findNearbyDrivers(Location location, double radius) {
// 使用Redis GEO查找附近司机
String geoKey = "driver:locations";
GeoResults<RedisGeoCommands.GeoLocation<Object>> results =
redisTemplate.opsForGeo().radius(
geoKey,
new Circle(
new Point(location.getLongitude(), location.getLatitude()),
new Distance(radius, Metrics.KILOMETERS)
)
);
List<Driver> drivers = new ArrayList<>();
for (GeoResult<RedisGeoCommands.GeoLocation<Object>> result : results) {
String driverId = (String) result.getContent().getName();
Driver driver = driverService.getDriverById(driverId);
if (driver != null && driver.getStatus() == DriverStatus.AVAILABLE) {
drivers.add(driver);
}
}
// 根据评分、距离等排序
return drivers.stream()
.sorted(Comparator
.comparing(Driver::getRating).reversed()
.thenComparing(d -> calculateDistance(d.getLocation(), location)))
.limit(10)
.collect(Collectors.toList());
}
/**
* 计算代驾价格
*/
private BigDecimal calculatePrice(OrderCreateRequest request) {
// 基础起步价
BigDecimal basePrice = new BigDecimal("39.00");
// 计算距离
double distance = calculateDistance(
request.getStartLocation(),
request.getEndLocation()
);
// 距离费用(3公里后每公里5元)
BigDecimal distancePrice = BigDecimal.ZERO;
if (distance > 3) {
distancePrice = new BigDecimal((distance - 3) * 5);
}
// 时段加价(夜间23:00-6:00)
BigDecimal timeSurcharge = getTimeSurcharge();
// 动态加价(高峰时段)
BigDecimal surgeMultiplier = getSurgeMultiplier();
// 总价计算
BigDecimal total = basePrice
.add(distancePrice)
.add(timeSurcharge)
.multiply(surgeMultiplier)
.setScale(2, RoundingMode.HALF_UP);
return total;
}
/**
* 司机接单处理
*/
@Override
@Transactional
public OrderDTO acceptOrder(String orderId, String driverId) {
// 使用分布式锁防止重复接单
String lockKey = "order:accept:" + orderId;
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new BusinessException("订单不存在");
}
if (order.getStatus() != OrderStatus.WAITING_DRIVER) {
throw new BusinessException("订单已被接单");
}
// 更新订单状态
order.setDriverId(driverId);
order.setStatus(OrderStatus.DRIVER_ACCEPTED);
order.setAcceptTime(new Date());
orderMapper.updateById(order);
// 更新司机状态
driverService.updateDriverStatus(driverId, DriverStatus.ON_ORDER);
// 发送推送通知
messageService.sendOrderAcceptedMessage(order);
return convertToDTO(order);
} else {
throw new BusinessException("系统繁忙,请稍后重试");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("接单失败");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 订单状态机
*/
@Override
public boolean updateOrderStatus(String orderId, OrderStatus newStatus) {
Order order = orderMapper.selectById(orderId);
OrderStatus currentStatus = order.getStatus();
// 验证状态转换是否合法
if (!isValidTransition(currentStatus, newStatus)) {
log.error("无效的状态转换: {} -> {}", currentStatus, newStatus);
return false;
}
// 更新状态
order.setStatus(newStatus);
order.setUpdateTime(new Date());
// 根据状态执行相应操作
switch (newStatus) {
case DRIVER_ARRIVED:
order.setArriveTime(new Date());
messageService.sendDriverArrivedMessage(order);
break;
case TRIP_STARTED:
order.setStartTime(new Date());
messageService.sendTripStartedMessage(order);
break;
case TRIP_COMPLETED:
order.setEndTime(new Date());
calculateFinalPrice(order);
messageService.sendTripCompletedMessage(order);
break;
case PAYMENT_COMPLETED:
order.setPaymentTime(new Date());
settleDriverIncome(order);
break;
}
return orderMapper.updateById(order) > 0;
}
/**
* 订单状态转换验证
*/
private boolean isValidTransition(OrderStatus from, OrderStatus to) {
Map<OrderStatus, Set<OrderStatus>> validTransitions = new HashMap<>();
validTransitions.put(OrderStatus.WAITING_DRIVER,
Set.of(OrderStatus.DRIVER_ACCEPTED, OrderStatus.CANCELLED));
validTransitions.put(OrderStatus.DRIVER_ACCEPTED,
Set.of(OrderStatus.DRIVER_ARRIVED, OrderStatus.CANCELLED));
validTransitions.put(OrderStatus.DRIVER_ARRIVED,
Set.of(OrderStatus.TRIP_STARTED, OrderStatus.CANCELLED));
validTransitions.put(OrderStatus.TRIP_STARTED,
Set.of(OrderStatus.TRIP_COMPLETED));
validTransitions.put(OrderStatus.TRIP_COMPLETED,
Set.of(OrderStatus.PAYMENT_COMPLETED));
Set<OrderStatus> allowed = validTransitions.get(from);
return allowed != null && allowed.contains(to);
}
}
// 订单状态枚举
public enum OrderStatus {
WAITING_DRIVER("等待接单"),
DRIVER_ACCEPTED("司机已接单"),
DRIVER_ARRIVED("司机已到达"),
TRIP_STARTED("行程开始"),
TRIP_COMPLETED("行程结束"),
PAYMENT_COMPLETED("支付完成"),
CANCELLED("已取消"),
COMPLAINT("投诉中"),
REFUNDED("已退款");
private final String description;
OrderStatus(String description) {
this.description = description;
}
}
三、实时通信实现
WebSocket实时位置同步
// 小程序端WebSocket实现
class RealTimeService {
constructor() {
this.socket = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
connect(userId, orderId) {
return new Promise((resolve, reject) => {
const wsUrl = `wss://your-domain.com/ws?userId=${userId}&orderId=${orderId}`;
this.socket = wx.connectSocket({
url: wsUrl,
success: () => {
this.bindEvents();
resolve();
},
fail: reject
});
});
}
bindEvents() {
wx.onSocketOpen(() => {
console.log('WebSocket连接已打开');
this.reconnectAttempts = 0;
});
wx.onSocketMessage((res) => {
const message = JSON.parse(res.data);
this.handleMessage(message);
});
wx.onSocketError((error) => {
console.error('WebSocket错误:', error);
this.reconnect();
});
wx.onSocketClose(() => {
console.log('WebSocket连接已关闭');
this.reconnect();
});
}
sendLocation(location) {
const message = {
type: 'location_update',
data: {
latitude: location.latitude,
longitude: location.longitude,
timestamp: Date.now()
}
};
if (this.socket && this.socket.readyState === 1) {
wx.sendSocketMessage({
data: JSON.stringify(message)
});
}
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(`尝试重新连接,第${this.reconnectAttempts}次`);
this.connect();
}, 3000 * this.reconnectAttempts);
}
}
}
四、安全与性能优化
1. 接口安全设计
// JWT认证拦截器
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
String token = request.getHeader("Authorization");
if (StringUtils.isEmpty(token)) {
throw new AuthenticationException("未提供认证令牌");
}
try {
Claims claims = JwtUtil.parseToken(token);
String userId = claims.getSubject();
// 验证用户状态
User user = userService.getUserById(userId);
if (user == null || user.getStatus() != UserStatus.NORMAL) {
throw new AuthenticationException("用户状态异常");
}
// 将用户信息存入请求上下文
RequestContext.setCurrentUser(user);
return true;
} catch (Exception e) {
throw new AuthenticationException("认证失败");
}
}
}
2. 数据库性能优化
-- 订单表分表策略
CREATE TABLE orders_202401 (
id BIGINT PRIMARY KEY,
order_no VARCHAR(32) UNIQUE,
user_id BIGINT,
driver_id BIGINT,
status TINYINT,
-- 其他字段...
INDEX idx_user_status (user_id, status),
INDEX idx_driver_status (driver_id, status),
INDEX idx_create_time (create_time)
) ENGINE=InnoDB PARTITION BY RANGE (YEAR(create_time)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026)
);
五、测试与部署
单元测试示例
@SpringBootTest
class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
void testCreateOrder() {
OrderCreateRequest request = new OrderCreateRequest();
request.setUserId("123456");
request.setStartAddress("北京市海淀区");
request.setEndAddress("北京市朝阳区");
OrderDTO order = orderService.createOrder(request);
assertNotNull(order);
assertEquals("123456", order.getUserId());
assertEquals(OrderStatus.WAITING_DRIVER, order.getStatus());
}
}
Docker部署配置
# docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
backend:
build: ./daiJia-backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
environment:
SPRING_PROFILES_ACTIVE: prod
结语
代驾系统的开发需要综合考虑功能完整性、系统稳定性和用户体验。本文通过具体的代码示例展示了核心功能的实现方法,但在实际开发中还需要根据具体业务需求进行调整和优化。建议采用持续集成、自动化测试和灰度发布等 DevOps 实践,确保系统的质量和稳定性。