本文适合有一定开发基础的后端/全栈工程师,将从数据库设计、订单状态机、智能派单引擎、支付回调等核心模块入手,逐层拆解家政O2O系统的关键代码实现。

一、源码整体工程结构
一个标准的家政O2O系统通常采用如下工程结构:
jiazheng-parent/
├── jiazheng-common/ # 通用工具模块
│ ├── utils/ # JSON转换、加密解密、Excel导入导出
│ └── config/ # 全局配置(跨域、拦截器)
├── jiazheng-api/ # API接口定义模块(OpenAPI 3.0规范)
├── jiazheng-service/ # 核心业务逻辑模块
│ ├── user/ # 用户服务
│ ├── order/ # 订单服务
│ ├── dispatch/ # 派单服务
│ └── payment/ # 支付服务
├── jiazheng-dao/ # 数据访问层(MyBatis-Plus)
├── jiazheng-admin/ # 管理后台(Vue + Element UI)
└── jiazheng-web/ # 用户端API接口
这种模块化设计使得每个业务模块可以独立开发、测试与部署,便于团队协作和后续扩展。
二、核心数据库表结构设计
数据库设计是家政系统的"地基",直接影响系统的性能和可维护性。以下是几个核心表的DDL设计示例。
CREATE TABLE `user` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
`open_id` VARCHAR(64) UNIQUE COMMENT '微信openId',
`union_id` VARCHAR(64) COMMENT '微信unionId(多端打通)',
`phone` VARCHAR(11) UNIQUE COMMENT '手机号',
`password` VARCHAR(128) COMMENT 'BCrypt加密密码',
`nickname` VARCHAR(50) COMMENT '昵称',
`avatar` VARCHAR(255) COMMENT '头像URL',
`real_name` VARCHAR(20) COMMENT '真实姓名',
`id_card` VARCHAR(18) COMMENT '身份证号(AES加密)',
`user_type` TINYINT DEFAULT 0 COMMENT '0普通用户 1服务人员 2机构',
`status` TINYINT DEFAULT 1 COMMENT '1正常 0禁用',
`is_deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_phone` (`phone`),
INDEX `idx_open_id` (`open_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
这里有几个值得注意的设计细节:
-
密码使用BCrypt强哈希算法加盐存储
-
身份证号等敏感信息使用AES-GCM加密
-
引入逻辑删除字段
is_deleted,配合MyBatis-Plus的@TableLogic注解 -
审计字段(create_time/update_time)通过MyBatis-Plus自动填充
2.2 订单表(order)
订单表是系统的核心事务表,设计时需要在第三范式 和查询性能之间做平衡
CREATE TABLE `order` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`order_no` VARCHAR(32) UNIQUE NOT NULL COMMENT '订单编号',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`worker_id` BIGINT COMMENT '服务人员ID',
`service_id` BIGINT NOT NULL COMMENT '服务项ID',
`service_name` VARCHAR(100) COMMENT '服务名称(冗余)',
`worker_name` VARCHAR(20) COMMENT '服务者姓名(冗余)',
`worker_phone` VARCHAR(11) COMMENT '服务者电话(冗余)',
`worker_avatar` VARCHAR(255) COMMENT '服务者头像(冗余)',
`address` VARCHAR(255) NOT NULL COMMENT '服务地址',
`lat` DECIMAL(10,7) COMMENT '纬度',
`lng` DECIMAL(10,7) COMMENT '经度',
`appoint_time` DATETIME NOT NULL COMMENT '预约时间',
`total_amount` DECIMAL(10,2) NOT NULL COMMENT '总金额',
`paid_amount` DECIMAL(10,2) DEFAULT 0 COMMENT '已付金额',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1待支付 2待接单 3已接单 4服务中 5已完成 6已取消 7售后中',
`remark` VARCHAR(500) COMMENT '用户备注',
`cancel_reason` VARCHAR(200) COMMENT '取消原因',
`is_deleted` TINYINT DEFAULT 0,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_user_id` (`user_id`),
INDEX `idx_worker_id` (`worker_id`),
INDEX `idx_status` (`status`),
INDEX `idx_appoint_time` (`appoint_time`),
INDEX `idx_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
关键设计思路:
-
冗余服务者姓名、联系方式、头像URL,避免高频联表查询
-
订单编号使用独立序列生成,便于分库分表
-
经纬度字段为智能派单提供地理位置数据支撑
2.3 分销关系表(distribution_relations)
对于支持分销裂变的家政系统,分销关系表的设计至关重要:
CREATE TABLE `distribution_relations` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`inviter_id` INT NOT NULL COMMENT '邀请人ID',
`invitee_id` INT NOT NULL COMMENT '被邀请人ID',
`level` TINYINT NOT NULL COMMENT '分销层级 1/2/3',
`device_fingerprint` VARCHAR(64) COMMENT '设备指纹(防刷)',
`status` TINYINT DEFAULT 1 COMMENT '1有效 0失效',
`commission_rate` DECIMAL(5,2) COMMENT '佣金比例',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `uk_invitee` (`invitee_id`),
INDEX `idx_inviter` (`inviter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分销关系表';
三、订单状态机实现
订单状态管理是家政系统的核心业务逻辑之一,必须防止非法状态流转。推荐使用状态模式 或状态机来实现。
3.1 订单状态枚举定义
public enum OrderStatus {
PENDING_PAY(1, "待支付"),
PENDING_ACCEPT(2, "待接单"),
ACCEPTED(3, "已接单"),
IN_SERVICE(4, "服务中"),
COMPLETED(5, "已完成"),
CANCELLED(6, "已取消"),
AFTER_SALES(7, "售后中");
private final int code;
private final String desc;
OrderStatus(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() { return code; }
public String getDesc() { return desc; }
}
3.2 状态转换规则与校验
@Service
public class OrderStateMachine {
// 定义合法的状态转换映射
private static final Map<OrderStatus, Set<OrderStatus>> TRANSITIONS = new HashMap<>();
static {
// 待支付 → 待接单 / 已取消
TRANSITIONS.put(OrderStatus.PENDING_PAY,
Set.of(OrderStatus.PENDING_ACCEPT, OrderStatus.CANCELLED));
// 待接单 → 已接单 / 已取消
TRANSITIONS.put(OrderStatus.PENDING_ACCEPT,
Set.of(OrderStatus.ACCEPTED, OrderStatus.CANCELLED));
// 已接单 → 服务中 / 已取消
TRANSITIONS.put(OrderStatus.ACCEPTED,
Set.of(OrderStatus.IN_SERVICE, OrderStatus.CANCELLED));
// 服务中 → 已完成
TRANSITIONS.put(OrderStatus.IN_SERVICE,
Set.of(OrderStatus.COMPLETED));
// 已完成 → 售后中
TRANSITIONS.put(OrderStatus.COMPLETED,
Set.of(OrderStatus.AFTER_SALES));
}
@Transactional
public void transition(Long orderId, OrderStatus targetStatus) {
Order order = orderMapper.selectById(orderId);
OrderStatus current = OrderStatus.values()order.getStatus() - 1;
// 校验状态转换是否合法
Set<OrderStatus> allowed = TRANSITIONS.get(current);
if (allowed == null || !allowed.contains(targetStatus)) {
throw new BusinessException("非法状态流转: " + current + " → " + targetStatus);
}
// 更新订单状态
order.setStatus(targetStatus.getCode());
orderMapper.updateById(order);
// 触发状态变更事件(消息推送、日志记录等)
publishStatusChangeEvent(order, current, targetStatus);
}
}
这种设计确保了订单状态流转的原子性 和合规性,非法请求(如"已完成"状态不可取消)会被直接拦截。
四、智能派单引擎实现
智能派单是家政O2O系统的核心竞争力。派单逻辑通常综合考虑距离、评分、空闲状态、技能匹配等多个维度。
4.1 服务人员位置存储(Redis GEO)
使用Redis的GEO数据结构存储服务人员实时位置,实现高效的附近人员查询:
@Service
public class WorkerLocationService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String GEO_KEY = "worker:locations";
/**
* 更新服务人员位置
*/
public void updateLocation(Long workerId, Double lng, Double lat) {
redisTemplate.opsForGeo().add(GEO_KEY,
new Point(lng, lat), workerId.toString());
// 设置过期时间,超时未更新则认为离线
redisTemplate.expire(GEO_KEY, 5, TimeUnit.MINUTES);
}
/**
* 查询附近5公里内的服务人员
*/
public List<Long> findNearbyWorkers(Double lng, Double lat, Integer radius) {
GeoResults<RedisGeoCommands.GeoLocation<Object>> results =
redisTemplate.opsForGeo().search(
GEO_KEY,
new Circle(new Point(lng, lat), new Distance(radius, Metrics.KILOMETERS)),
GeoRadiusCommandArgs.newGeoRadiusArgs().limit(50)
);
return results.getContent().stream()
.map(result -> Long.valueOf(result.getContent().getName().toString()))
.collect(Collectors.toList());
}
}
4.2 派单策略实现
@Service
public class DispatchService {
@Autowired
private WorkerLocationService locationService;
@Autowired
private WorkerService workerService;
@Autowired
private OrderService orderService;
/**
* 智能派单:综合多维度评分,选出最优服务人员
*/
@Transactional
public DispatchResult autoDispatch(Order order) {
// 1. 获取附近5公里内的服务人员
List<Long> nearbyWorkerIds = locationService.findNearbyWorkers(
order.getLng(), order.getLat(), 5
);
if (CollectionUtils.isEmpty(nearbyWorkerIds)) {
return DispatchResult.fail("附近暂无可用服务人员");
}
// 2. 获取服务人员的详细信息(评分、当前订单量、技能标签)
List<Worker> workers = workerService.listByIds(nearbyWorkerIds);
// 3. 综合评分排序
Worker bestWorker = workers.stream()
.filter(w -> w.getStatus() == WorkerStatus.ONLINE)
.filter(w -> w.getCurrentOrderCount() < w.getMaxConcurrentOrders())
.filter(w -> matchSkill(w, order.getServiceId()))
.max(Comparator
.comparingDouble(Worker::getRating) // 评分优先
.thenComparingInt(Worker::getCurrentOrderCount) // 订单量少优先
)
.orElse(null);
if (bestWorker == null) {
// 无匹配结果时触发手动派单流程
return DispatchResult.fail("暂无匹配的服务人员,已转人工派单");
}
// 4. 派单:锁定服务人员并更新订单
order.setWorkerId(bestWorker.getId());
order.setStatus(OrderStatus.ACCEPTED.getCode());
orderService.updateOrder(order);
// 5. 发送派单通知(WebSocket + 模板消息)
notificationService.notifyWorker(bestWorker.getId(), order);
notificationService.notifyUser(order.getUserId(), order);
return DispatchResult.success(bestWorker);
}
/**
* 技能匹配校验
*/
private boolean matchSkill(Worker worker, Long serviceId) {
// 检查服务人员的技能标签是否包含该服务类型
return worker.getSkillTags().contains(getServiceCategory(serviceId));
}
}
派单策略还可以更复杂------基于改进型匈牙利算法,综合距离(40%)、评分(30%)、响应速度(20%)、技能匹配(10%)的权重进行最优匹配。
五、支付回调处理
支付是家政O2O系统的关键环节,支付回调的可靠处理直接影响用户体验和资金安全。
5.1 微信支付回调处理(Node.js版)
// Node.js 支付回调处理
app.post('/pay/wechat/notify', async (req, res) => {
try {
const xmlData = req.body;
const result = await wechatPay.verifyNotify(xmlData);
if (result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
const orderId = result.out_trade_no;
const transactionId = result.transaction_id;
const totalFee = result.total_fee / 100; // 分转元
// 更新订单状态(需保证幂等性)
const order = await Order.findOne({ where: { orderNo: orderId } });
if (order && order.status === OrderStatus.PENDING_PAY) {
await order.update({
status: OrderStatus.PENDING_ACCEPT,
paidAmount: totalFee,
transactionId: transactionId,
paidTime: new Date()
});
// 触发派单流程(异步)
await dispatchQueue.add({ orderId: order.id });
// 发送支付成功通知
await notificationService.sendPaymentSuccess(order.userId, orderId);
}
// 返回成功响应给微信
res.send(successXmlResponse());
} else {
// 支付失败处理
logger.error('Payment failed', result);
res.send(failXmlResponse(result.return_msg));
}
} catch (error) {
logger.error('Payment callback error', error);
res.send(failXmlResponse('处理异常'));
}
});
关键设计要点:
-
回调处理必须幂等------同一订单的多次回调不会重复处理
-
使用消息队列将派单流程异步化,避免阻塞回调响应
-
记录完整的支付日志,便于对账和问题排查
六、WebSocket实时通信
服务人员接单后,需要通过WebSocket向用户端推送实时位置
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/track").setAllowedOrigins("*");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
@Controller
public class LocationController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
/**
* 服务人员位置更新 → 推送给用户
*/
public void pushLocation(Long orderId, Double lng, Double lat) {
Map<String, Object> data = new HashMap<>();
data.put("orderId", orderId);
data.put("lng", lng);
data.put("lat", lat);
data.put("timestamp", System.currentTimeMillis());
messagingTemplate.convertAndSend("/topic/order/" + orderId + "/location", data);
}
}
总结
本文从数据库设计 、订单状态机 、智能派单引擎 、支付回调处理 和实时通信五个维度,深入剖析了家政O2O系统的核心代码实现。这些模块共同构成了一个完整的家政服务数字化平台的技术底座。
在实际开发中,还需要关注以下工程化要点:
-
使用JWT实现无状态身份认证
-
配合MyBatis-Plus的自动填充和逻辑删除简化开发
-
引入Docker + Kubernetes实现容器化部署与弹性伸缩
希望本文能为正在开发或研究家政O2O系统的开发者提供有价值的代码参考。