家政预约小程序后端源码深度剖析:支付回调实战
本文适合有一定开发基础的后端/全栈工程师将从数据库设计、订单状态机、智能派单引擎、支付回调等核心模块入手逐层拆解家政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)) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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)) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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)) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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 状态转换规则与校验Servicepublic class OrderStateMachine {// 定义合法的状态转换映射private static final MapOrderStatus, SetOrderStatus 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));}Transactionalpublic void transition(Long orderId, OrderStatus targetStatus) {Order order orderMapper.selectById(orderId);OrderStatus current OrderStatus.values()[order.getStatus() - 1];// 校验状态转换是否合法SetOrderStatus 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数据结构存储服务人员实时位置实现高效的附近人员查询Servicepublic class WorkerLocationService {Autowiredprivate RedisTemplateString, 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 ListLong findNearbyWorkers(Double lng, Double lat, Integer radius) {GeoResultsRedisGeoCommands.GeoLocationObject 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 派单策略实现Servicepublic class DispatchService {Autowiredprivate WorkerLocationService locationService;Autowiredprivate WorkerService workerService;Autowiredprivate OrderService orderService;/*** 智能派单综合多维度评分选出最优服务人员*/Transactionalpublic DispatchResult autoDispatch(Order order) {// 1. 获取附近5公里内的服务人员ListLong nearbyWorkerIds locationService.findNearbyWorkers(order.getLng(), order.getLat(), 5);if (CollectionUtils.isEmpty(nearbyWorkerIds)) {return DispatchResult.fail(附近暂无可用服务人员);}// 2. 获取服务人员的详细信息评分、当前订单量、技能标签ListWorker 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向用户端推送实时位置ConfigurationEnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint(/track).setAllowedOrigins(*);}Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {registry.enableSimpleBroker(/topic);registry.setApplicationDestinationPrefixes(/app);}}Controllerpublic class LocationController {Autowiredprivate SimpMessagingTemplate messagingTemplate;/*** 服务人员位置更新 → 推送给用户*/public void pushLocation(Long orderId, Double lng, Double lat) {MapString, 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系统的开发者提供有价值的代码参考。