LikeShop 上门家政 Java 版师傅派单:区域匹配、订单推送与改派逻辑

简介: LikeShop家政系统Java版聚焦派单这一核心环节,通过区域分层匹配、多通道推送、超时自动改派及全程状态留痕,解决跨区派单、接单率低、改派混乱等痛点。架构清晰、逻辑严谨、可配置可审计,助力商家提升运营效率与用户体验

上门家政的核心运营环节是派单。和实物电商的“发货”不同,上门服务的派单涉及地理围栏、师傅运力、服务时段、改派兜底等一系列复杂逻辑。很多做家政系统的团队,一开始把派单做成“简单分配”,结果上线后问题百出:跨区域派单、师傅接不到订单、改派混乱、用户投诉。

LikeShop 上门家政 Java 版的师傅派单模块,是作为核心运营链路设计的:区域分层匹配、多通道订单推送、超时自动改派、全程状态留痕。这篇文章从架构、区域匹配、推送机制、改派逻辑四个层面拆解实现思路,附核心代码片段。

一、派单整体架构

派单不是单点操作,是一条完整的状态链路。上门家政场景下,派单分为两种模式:自动派单(系统默认,按规则匹配推送)和手动改派(运营人工介入,异常场景兜底)。

1.1 完整派单链路

用户下单支付
    ↓
地址解析 → 区域校验
    ↓
匹配可用师傅池(区域+类目+状态+负载)
    ↓
生成派单任务 → 多通道推送师傅
    ↓
┌─ 师傅接单 → 更新状态 → 上门服务 → 完成确认
│
└─ 拒单/超时未接 → 自动改派 → 重新匹配 → 推送下一位

1.2 订单派单状态机

public enum DispatchStatus {
    PENDING(10, "待派单"),
    PUSHED(20, "已推送"),
    ACCEPTED(30, "师傅已接单"),
    SERVICING(40, "服务中"),
    FINISHED(50, "已完成"),
    REDISPATCH(60, "改派中"),
    CANCELLED(70, "已取消");
}

整个派单流程围绕这个状态机流转,所有操作前置校验状态,不允许非法跳转。LikeShop 上门家政版的设计思路是:能自动的不人工,能规则化的不例外,把绝大多数常规订单交给系统自动处理,运营只处理异常场景。

二、区域匹配:派单精准的基础

派单准不准,第一步就在区域。家政服务的半径属性很强,跨街道、跨商圈派单,师傅通勤成本高、用户等待时间长,投诉率会直线上升。

2.1 服务区域的层级设计

采用行政区域 + 自定义电子围栏双层设计:

  • 行政区域层:省-市-区-街道四级,基础匹配用,适合标准化城市服务;
  • 电子围栏层:自定义多边形围栏(小区、商圈、产业园),精细匹配用,适合高密度区域。

师傅可以绑定多个服务区域,同时设置最远服务半径,双重校验避免超远派单。

2.2 核心数据库表

-- 服务区域表
CREATE TABLE ls_service_area (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  city_id BIGINT NOT NULL,
  area_name VARCHAR(64) NOT NULL,
  area_type TINYINT NOT NULL COMMENT '1行政区域 2电子围栏',
  boundary TEXT COMMENT '电子围栏多边形坐标JSON',
  center_lng DECIMAL(10,6),
  center_lat DECIMAL(10,6),
  status TINYINT NOT NULL DEFAULT 1,
  create_time DATETIME NOT NULL
);

-- 师傅服务区域绑定
CREATE TABLE ls_worker_area (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  worker_id BIGINT NOT NULL,
  area_id BIGINT NOT NULL,
  priority INT NOT NULL DEFAULT 0 COMMENT '区域优先级',
  create_time DATETIME NOT NULL,
  UNIQUE KEY uk_worker_area(worker_id, area_id)
);

-- 师傅服务状态
CREATE TABLE ls_worker_status (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  worker_id BIGINT NOT NULL UNIQUE,
  online_status TINYINT NOT NULL COMMENT '1在线 2忙碌 3离线',
  current_area_id BIGINT COMMENT '当前所在区域',
  today_order_count INT NOT NULL DEFAULT 0,
  last_active_time DATETIME,
  update_time DATETIME NOT NULL
);

2.3 区域匹配核心逻辑

匹配采用层级降级 + 多条件筛选:优先精细围栏匹配,没有则逐步降级到行政区域;同时校验师傅的在线状态、服务类目、当日负载。

@Service
public class WorkerMatchService {

    @Autowired
    private ServiceAreaMapper areaMapper;
    @Autowired
    private WorkerStatusMapper statusMapper;

    /**
     * 匹配可用师傅列表(按优先级排序)
     */
    public List<Long> matchWorkers(ServiceOrder order) {
        // 1. 解析订单地址,匹配所属区域
        Long areaId = matchArea(order.getAddress(), order.getLng(), order.getLat());
        if (areaId == null) {
            throw new BizException("该地址暂不提供服务");
        }

        // 2. 查询区域内所有绑定的师傅
        List<Long> workerIds = workerAreaMapper.getWorkerIdsByArea(areaId);
        if (CollectionUtils.isEmpty(workerIds)) {
            // 降级:向上级区域匹配
            areaId = getParentArea(areaId);
            workerIds = workerAreaMapper.getWorkerIdsByArea(areaId);
        }
        if (CollectionUtils.isEmpty(workerIds)) {
            throw new BizException("当前区域暂无可用师傅");
        }

        // 3. 多条件筛选:在线、服务类目匹配、当日负载
        List<WorkerStatus> availableWorkers = statusMapper.selectAvailable(
                workerIds, order.getServiceId());

        // 4. 排序:评分优先 > 今日单量少优先 > 距离优先
        availableWorkers.sort((a, b) -> {
            int scoreCompare = b.getScore().compareTo(a.getScore());
            if (scoreCompare != 0) return scoreCompare;
            int countCompare = a.getTodayOrderCount() - b.getTodayOrderCount();
            if (countCompare != 0) return countCompare;
            return getDistance(order, a).compareTo(getDistance(order, b));
        });

        return availableWorkers.stream()
                .map(WorkerStatus::getWorkerId)
                .collect(Collectors.toList());
    }

    private Long matchArea(String address, BigDecimal lng, BigDecimal lat) {
        // 优先电子围栏匹配(点在多边形内判断)
        List<ServiceArea> fences = areaMapper.selectFences();
        for (ServiceArea fence : fences) {
            if (GeoUtils.isPointInPolygon(lng, lat, fence.getBoundary())) {
                return fence.getId();
            }
        }
        // 兜底行政区域匹配
        return areaMapper.getByAddress(address);
    }
}

匹配结果是一个有序的师傅列表,派单时按顺序推送,保证最优质的师傅优先接到订单。

三、订单推送:多通道与状态回执

匹配到师傅池之后,就是推送环节。推送的核心要求是:不丢单、不重复、有回执、可超时。

3.1 两种推送策略

策略 逻辑 适用场景
顺序派单 按优先级逐个推送,一位超时未接则推下一位 订单充足、师傅多,保证优质师傅优先
群派模式 同时推送给多位师傅,先抢先得 订单稀缺、高峰期,提高接单率

默认采用顺序派单,兼顾公平和效率;高峰期可配置切换成群派。

3.2 多通道推送矩阵

不是只靠 APP 推送,要做分层通道,保证触达率:

  • APP 推送:师傅在线时优先,实时性最高,免费;
  • 短信通知:师傅离线时兜底,带接单链接;
  • 语音电话:高价值订单、紧急订单,强提醒。

3.3 推送核心实现

@Service
public class OrderDispatchService {

    @Autowired
    private WorkerMatchService matchService;
    @Autowired
    private DispatchTaskMapper taskMapper;
    @Autowired
    private PushService pushService;
    @Autowired
    private AlertService alertService;

    /**
     * 订单支付成功,触发自动派单
     */
    @EventListener
    @Transactional(rollbackFor = Exception.class)
    public void onOrderPaid(ServiceOrderPaidEvent event) {
        ServiceOrder order = event.getOrder();

        // 1. 匹配师傅列表
        List<Long> workerIds = matchService.matchWorkers(order);

        // 2. 生成派单任务
        DispatchTask task = new DispatchTask();
        task.setOrderId(order.getId());
        task.setWorkerIds(JSON.toJSONString(workerIds));
        task.setCurrentIndex(0);
        task.setStatus(DispatchStatus.PENDING.getCode());
        taskMapper.insert(task);

        // 3. 推送第一位师傅
        pushToWorker(task, workerIds.get(0));
    }

    private void pushToWorker(DispatchTask task, Long workerId) {
        task.setStatus(DispatchStatus.PUSHED.getCode());
        task.setCurrentWorkerId(workerId);
        task.setPushTime(new Date());
        taskMapper.updateById(task);

        // 多通道推送
        boolean pushOk = pushService.pushWorker(workerId, 
                PushType.NEW_ORDER, task.getOrderId());

        if (!pushOk) {
            // APP推送失败,短信兜底
            pushService.sendSms(workerId, PushType.NEW_ORDER, task.getOrderId());
        }

        // 注册超时监听:15分钟未接单自动改派
        DispatchTimeoutJob.register(task.getId(), 15, TimeUnit.MINUTES);
    }

    /**
     * 师傅接单回调
     */
    public void acceptOrder(Long taskId, Long workerId) {
        DispatchTask task = taskMapper.selectById(taskId);
        if (task.getStatus() != DispatchStatus.PUSHED.getCode()) {
            throw new BizException("订单状态不可接单");
        }
        if (!task.getCurrentWorkerId().equals(workerId)) {
            throw new BizException("非当前派单师傅");
        }

        task.setStatus(DispatchStatus.ACCEPTED.getCode());
        task.setAcceptTime(new Date());
        taskMapper.updateById(task);

        // 更新师傅状态、今日单量
        workerStatusService.incOrderCount(workerId);

        // 通知用户:师傅已接单
        notifyService.notifyUserOrderAccept(task.getOrderId(), workerId);
    }
}

3.4 推送回执与幂等

所有推送都要有回执,不能“发出去就当成功了”。师傅端收到推送后,要回传接收确认;接单操作要做幂等校验,避免重复点击导致状态错乱。

四、改派逻辑:异常场景的兜底

派单不可能 100% 一次成功,拒单、超时、师傅临时有事、用户改约,都会触发改派。改派是派单系统的兜底能力,也是最容易出乱子的环节。

4.1 改派触发场景

  • 师傅主动拒单:师傅原因无法服务,填写拒单原因;
  • 超时未接单:推送后 15 分钟未操作,系统自动改派;
  • 运营人工改派:用户改时间/改地址、特殊需求,运营介入调整;
  • 师傅报备休息:师傅临时下线,未完成订单自动改派。

4.2 改派核心流程

  1. 回收原任务:标记原派单任务为改派中,原师傅释放订单;
  2. 校验改派次数:单订单最多改派 2~3 次,避免无限循环;
  3. 重新匹配:排除原师傅,重新匹配师傅池;
  4. 二次推送:按顺序推送给下一位师傅;
  5. 通知用户:改派后同步通知用户新师傅信息,避免投诉。

4.3 改派核心代码

@Service
public class OrderRedispatchService {

    @Autowired
    private DispatchTaskMapper taskMapper;
    @Autowired
    private WorkerMatchService matchService;
    @Autowired
    private DispatchLogMapper logMapper;

    /**
     * 改派订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void redispatch(Long taskId, String reason, Long operatorId) {
        DispatchTask task = taskMapper.selectById(taskId);

        // 1. 状态校验
        if (task.getStatus() != DispatchStatus.PUSHED
                && task.getStatus() != DispatchStatus.ACCEPTED) {
            throw new BizException("当前状态不可改派");
        }

        // 2. 校验改派次数
        if (task.getRedispatchCount() >= 2) {
            throw new BizException("订单改派次数已达上限,请人工处理");
        }

        // 3. 释放原师傅
        Long oldWorkerId = task.getCurrentWorkerId();
        workerStatusService.decOrderCount(oldWorkerId);

        // 4. 重新匹配师傅(排除原师傅)
        List<Long> workerIds = matchService.matchWorkers(
                getOrderByTask(taskId));
        workerIds.remove(oldWorkerId);
        if (CollectionUtils.isEmpty(workerIds)) {
            // 无可用师傅,转人工处理
            task.setStatus(DispatchStatus.PENDING);
            alertService.send("派单异常", "订单" + task.getOrderId() + " 改派无可用师傅");
            return;
        }

        // 5. 更新任务,推送给下一位
        task.setRedispatchCount(task.getRedispatchCount() + 1);
        task.setWorkerIds(JSON.toJSONString(workerIds));
        task.setCurrentIndex(0);
        taskMapper.updateById(task);

        // 6. 写改派日志
        DispatchLog log = new DispatchLog();
        log.setTaskId(taskId);
        log.setFromWorker(oldWorkerId);
        log.setToWorker(workerIds.get(0));
        log.setReason(reason);
        log.setOperatorType(operatorId == null ? 1 : 2);
        log.setOperatorId(operatorId);
        log.setCreateTime(new Date());
        logMapper.insert(log);

        // 7. 推送新师傅
        dispatchService.pushToWorker(task, workerIds.get(0));

        // 8. 通知用户
        notifyService.notifyUserRedispatch(task.getOrderId());
    }

    /**
     * 超时未接单自动改派
     */
    public void onTimeout(Long taskId) {
        DispatchTask task = taskMapper.selectById(taskId);
        if (task.getStatus() == DispatchStatus.PUSHED) {
            redispatch(taskId, "超时未接单", null);
        }
    }
}

每次改派都留痕,谁发起的、什么原因、从哪个师傅改到哪个师傅,全部可追溯。

五、常见坑与最佳实践

  1. 区域重叠导致重复派单
    坑:电子围栏和行政区域重叠,同一个师傅匹配到两次,重复推送。
    最佳实践:匹配结果按师傅 ID 去重,同一订单同一师傅只推一次。
  2. 师傅在线状态失真
    坑:师傅 APP 后台挂着,人其实不在,订单推送过去石沉大海。
    最佳实践:加心跳机制,5 分钟无动作自动置为离线,匹配时只选在线师傅。
  3. 推送无回执,重复派单
    坑:推送没收到回执,系统以为没发成功,重复推送,师傅收到好几次。
    最佳实践:推送有回执,接单操作幂等,按任务维度去重。
  4. 改派不通知用户
    坑:师傅换了用户不知道,上门了才发现换人,体验很差。
    最佳实践:改派成功立刻推送用户通知,附新师傅信息和联系电话。
  5. 无负载均衡
    坑:评分高的师傅单量爆炸,新师傅没单接,运力不均。
    最佳实践:排序加入当日单量因子,单量少的师傅优先级提升,均衡负载。

结语

上门家政的派单,看起来是个简单的分配功能,本质是地理、运力、时效的综合调度。区域准不准、推送快不快、改派顺不顺,直接影响师傅效率和用户体验。

LikeShop 上门家政 Java 版把这套逻辑做成了标准化模块,区域围栏可配置、推送策略可调、改派有兜底、全程留痕可审计,商家不用从零踩坑。对做上门服务的商家来说,派单这条链路跑顺了,整体运营效率和服务质量都能上一个台阶。

目录
相关文章
|
2月前
|
人工智能 API 开发工具
OpenSandbox · 面向 AI 应用的通用沙箱平台
OpenSandbox 是面向 AI 应用的轻量级沙箱平台,提供隔离、安全、可控的代码执行环境。支持一键部署、多语言 SDK、自动超时回收、端口暴露与凭证代理,让 Agent 安全运行生成代码。
OpenSandbox · 面向 AI 应用的通用沙箱平台
|
7月前
|
安全 API
广告敏感词过滤-敏感词-文本审核-敏感词过滤-敏感词检测
本工具提供文本合规检测服务,精准识别广告法违禁词、极限用语(如“顶级”“全网首发”)、色情低俗、辱骂攻击及低质灌水等内容,支持电商文案、短视频脚本、企业宣传物料等多场景审核,保障内容安全合规。
949 10
|
25天前
|
存储 JSON 异构计算
DeepInfer 实测:如何用 4 张 B200 提供 1000 并发
本文实测开源推理框架DeepInfer在4张B200 GPU上实现1000并发的可行性,通过EP4专家并行与去RDMA化改造(改用PCIe共享内存读取Engram表),在128k上下文下达成6.2万token/s总吞吐;分析Decode/Prefill性能、GPU利用率瓶颈、真实负载(GPQA/AIME)表现,并提供容器镜像与兼容性补丁,显著降低大模型推理部署门槛。
|
8月前
|
人工智能 自然语言处理 安全
企业自动化新模式:AI智能体+RPA
随着AI技术的发展,很多企业都感受到了提效的迫切性,从最开始的观望和了解状态,到今年纷纷把RPA和智能体的相关项目都列入了KPI。从表面看,这两类工具都可以帮我们干活,提升工作效率,都可以被称为“数字员工”,但在实际使用中,差异其实很大。如果对这两个概念理解不到位,很容易盲目跟风,选错方向,导致项目效果不理想。 一、RPA是什么?优劣势有哪些?RPA本质上是流程自动化工具,它可以模拟人工 ITPUB博客每天千篇余篇博文新资讯,40多万活跃博主,为IT技术人提供全面的IT资讯和交流互动的IT博客平台-中国专业的IT技术ITPUB博客。
|
7月前
|
安全 API
敏感词过滤-文本审核-敏感词审核-违禁词审核-敏感词检测-违禁词检测
本服务提供智能文本审核,精准识别违禁词、色情低俗、恶意推广、辱骂攻击及低质灌水内容,并校验广告法合规性,适用于评论过滤、注册信息筛查、文章发布等场景,保障内容安全与平台合规。
1352 1
|
9月前
|
数据采集 人工智能 自然语言处理
架构演进:从确定性工作流 (Workflow) 到自主智能体 (LLM Agent)
本文对比生成式AI中Workflow(确定性流程)与Agent(自主推理系统)的技术范式,以“智慧旅游规划”为案例,剖析二者在控制流、状态管理与不确定性处理上的本质差异,揭示其适用场景与融合实践路径。
1324 2
|
9月前
|
人工智能 API 数据库
AI智能体的开发流程
AI智能体开发远超写代码,本质是塑造具备感知、推理与行动力的数字生命。本文详解其五大核心环节:需求定义、架构设计(规划/记忆/工具/推理)、技术选型、提示词工程及迭代评估,助你系统构建自主Agent。(239字)
|
6月前
|
安全 机器人 Python
三个工具,让 agent 在一次对话里完成研究、写码、调试与保存
本文展示了一个真正“具身智能”的多工具Agent:它能自动研究网页、编写/调试Python代码(如列表推导式)、运行验证并保存结果。核心不在工具本身,而在模型自主规划工作流——研究→写→测→修→存,全程无需硬编码逻辑。三工具即成系统,智能涌现于规划。
296 1
三个工具,让 agent 在一次对话里完成研究、写码、调试与保存
|
存储 人工智能 监控
从代码生成到自主决策:打造一个Coding驱动的“自我编程”Agent
本文介绍了一种基于LLM的“自我编程”Agent系统,通过代码驱动实现复杂逻辑。该Agent以Python为执行引擎,结合Py4j实现Java与Python交互,支持多工具调用、记忆分层与上下文工程,具备感知、认知、表达、自我评估等能力模块,目标是打造可进化的“1.5线”智能助手。
2979 62
|
人工智能 供应链 安全
暸望塔丨三大投入,为中国企业走向全球铺路搭桥
面对新一轮的历史机遇,阿里云将以战略级的投入,为中国企业出海提供一流的基础设施、一流的技术和一流的服务,与最优秀的中国企业在全球并肩前行,共赴新的大航海时代。
暸望塔丨三大投入,为中国企业走向全球铺路搭桥