ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Java构建智慧养老平台:护理代办与陪诊陪护全流程设计与实践

Java构建智慧养老平台:护理代办与陪诊陪护全流程设计与实践

陪诊陪护和护理代办这两个词,在智慧养老领域已经不算新鲜,但如果真正落地过一个完整系统,你会发现“全覆盖”这三个字才是难点中的难点。我前阵子刚完成一套基于Java技术栈的智慧养老服务平台,核心模块正好覆盖了护理代办和陪诊陪护全流程,从工单流转、陪诊预约、护理员匹配到费用结算、消息通知、服务评价,算是把整个线上闭环走通了。这篇文章就把这套系统的核心设计思路、技术选型、关键模块实现,以及我踩过的那些坑,一条条拆开讲清楚,希望能给正在做或准备做养老系统的朋友一些参考。

项目背景很简单:某养老服务机构要上线一套面向老年人、家属和护理人员的服务平台,老人或子女在小程序端下单,平台调度护理员提供上门护理,或由陪诊员陪同老人去医院就诊。听起来跟外卖系统有点像,但真做起来复杂度完全不同——服务对象是老人,服务的确定性要求极高,订单状态一旦出错、人到了楼下却进不了门,或者陪诊员跟老人走散了,这些问题都是不能拿“重试”糊弄过去的。这套系统的核心使命,是在保证服务质量和安全底线的前提下,把一个非常依赖线下沟通的行业,用数字化手段把流程固化下来,让家属放心、让护理员高效、让管理员能管得清。

1. 项目定位与核心需求拆解

1.1 为什么是“护理代办”而不是“护理预约”

最开始产品那边提的需求很简单:做一个“预约护理”的线上平台。但等我们深挖了几轮,发现“预约”这个词根本撑不起业务。

真实场景中,老人或家属需要的不是“预约一个护理员上门”这么简单,而是一整串事务:申请长期护理保险材料、代约体检、慢病续方、开药送药、预约上门测血压血糖、甚至代办住院手续。这些事务有一个共同特点:琐碎、低频、流程长、需要跨部门协作。对老人来说,最大的障碍不是“不知道要护理”,而是“不知道怎么把这件事办下来”。所以产品的核心定位就从“护理预约”转向了“护理代办”——平台要把这些事务性工作承接过来,由调度中心分派给护理员,跟踪办理进度,直到办结。

这个定位转变直接改变了系统的功能设计。如果是预约,系统只需要做时间表、排班和订单;但代办模式下,我们必须设计三套核心能力:工单状态流转引擎、服务SOP标准化、代办进度可视化。工单状态不只是“待接单、服务中、已完成”,而是要细化到“待补充材料、待提交申请、审核中、待领取结果、已办结”这类贴合政务和医院流程的状态。

还有一个易被忽略的需求:知情同步。代办通常由子女发起,但办理过程需要老人配合(比如人脸识别、签字),而子女在上班不可能全程盯着。所以我们做了一个动态订阅推送机制——老人端的每一步进展都会同步推送给家属端,包括“护理员已到达”“材料已提交至医保窗口”“审批已通过”这些节点。这个功能上线后,家属群体的好评率出乎意料地高,后来复盘原因也很简单:养老这件事,真正的付费决策者是子女,而子女的核心痛点是“看不见、管不着”,代办服务本质上是把“看不见”变成了“看得见”。

1.2 用户角色与权限边界设计

这套系统的用户角色比一般ToC产品复杂得多,至少包含四类:老人端、家属端、护理员/陪诊员端、运营管理端。角色之间还有委托授权的关系,比如家属可以代替老人下单、查看订单,但人脸核销这类敏感操作只能由老人本人完成。

权限设计上,我用的是Spring Security + 自定义数据权限注解的方案。简单说,就是同一套菜单资源,按照角色做两级过滤:先是功能级权限,能不能访问这个接口;再是数据级权限,能看到哪些数据。这里踩过一个坑,早期只做了功能权限,结果一个护理员登录后能通过遍历接口ID看到其他护理员的订单信息,属于重大越权漏洞。后来我们把所有查询接口都加上了数据范围判断,核心规则是:老人端只能看自己和被授权家属的订单,护理员只能看自己接的订单,运营端可按区域或机构过滤。

一个比较隐蔽的需求是短期授权。比如老人在医院住院,需要临时授权侄子帮忙办理手续,但出院后这个授权就自动失效。这种场景通过设置授权有效期来解决,有效期过了,数据权限自动收回到默认角色。

1.3 系统边界与流程闭环

系统边界这个问题,产品和技术之间反复拉扯了很久。最开始产品希望平台连医院的号源都能对接,但实际一调研发现,各家医院的系统接口、参数标准、开放程度差异太大,短期内全面对接根本不现实。

最终定的边界是:平台不直接对接医院业务系统,而是通过“陪诊服务”来衔接。也就是说,挂号、取号、缴费这些操作,由陪诊员在线下帮老人完成,平台负责的任务是让这个过程透明化和可控化。为此我们设计了一套完整的陪诊流程闭环:家属下单(或老人自己下单)→ 系统接单 → 匹配陪诊员 → 诊前提醒 → 院内诊疗陪伴 → 诊后关怀 → 服务评价。每一步都有对应的状态节点和操作人,缺一个环节都流转不下去。

展示服务详情取巧妙的方法:这里的展示是在线上完成的。陪诊员要上传实时的就诊排队记录、检验检查单等,像跑在手机上的巡查记录,完成一阶段,自动触发给家属的消息模板,让家属即使不在场也能知道“老人什么时候见到了医生、收费多少、取没取到药”。这套思路贯穿了后续所有模块,后来我总结了一下,想做成养老数字化,别纠结一步到位,先想清楚谁能线上完成,谁能线下铺开,再把衔接缝补好。

2. 核心技术选型与架构设计

2.1 为什么是Java而不是别的

项目启动时,团队内部不是没有讨论过用别的技术栈。但最终拍板用Java,主要是基于三个方面的考虑。

第一个是生态成熟度。养老服务平台涉及支付、短信、地图、消息推送、对象存储等大量第三方能力,Java生态里对应的SDK和中间件相当齐全,遇到问题能找到的参考资料也最多。第二个是团队技术栈匹配,团队成员基本都是Java背景,如果用别的技术栈,光是把团队拉齐就要花一个月,交付节奏不允许。第三个是长期维护和招人成本,这类政企合作项目通常要运行好几年,后期大概率会有新手补充进来,Java的方向稳定,招人容易,团队接手成本低。

框架选型上我选了Spring Boot 3.x作为基础框架,搭配MyBatis-Plus做数据持久化,Redis做缓存和分布式锁,RocketMQ做异步消息和事件通知。之所以选MyBatis-Plus而不是JPA,核心原因是系统里有大量复杂的动态查询,比如订单列表要根据时间、地区、服务类型、状态做多重筛选,MyBatis-Plus对这种场景的支持更灵活,SQL可读性和可控性也更好。

2.2 模块化拆分与数据库设计

从业务维度,系统拆分为六个核心模块:用户中心、工单中心、陪诊中心、支付结算中心、消息中心、运营后台。

用户中心管老人、家属、护理员、陪诊员、管理员五种身份的基础信息和授权关系。工单中心是护理代办的核心,负责工单的创建、派发、流转和归档。陪诊中心相对独立,因为它有特有的预约时段、陪诊员匹配、签到核销逻辑。支付结算中心负责订单费用的计算、支付流水、退款和护理员/陪诊员的佣金结算。消息中心管所有短信、App Push、微信模板消息。运营后台则是一系列管理端接口和报表。

数据库设计上,核心几张表的思路我简单列一下,比较好参考:

表名核心字段说明
service_order订单号、用户ID、服务类型、状态、金额、地区编码、服务地址主订单表,一个订单可包含多个服务项
service_work_order工单号、订单ID、工单类型、当前节点、处理人ID、优先级、时限代办业务的最小执行单元
order_status_log订单ID、旧状态、新状态、操作人、操作时间、备注状态变更追溯,审计用
appointment_info预约单ID、陪诊员ID、老人ID、医院名称、就诊时段、就诊科室陪诊预约的信息主表
nurse_profile护理员ID、姓名、服务区域、服务类型、评分、接单状态、GPS坐标护理员和陪诊员共用的服务者信息表
settlement_detail结算单ID、订单ID、服务金额、平台抽成、护理员收入、结算时间资金结算明细表

这里特别说一下schedule_time_slot表,也就是陪诊预约的时间段表。陪诊预约为防止高峰期扎堆,把每天的时间切割成半小时一个slot,每个slot最多允许N个陪诊员同时接单,这个N是根据区域护理员人数动态计算的。这样既能防超卖,又能让家属有足够的选择空间。

2.3 订单状态机与并发控制

状态机是整个系统最核心的部分,也是我花了最多时间打磨的地方。

一开始没意识到状态机的价值,订单状态的流转逻辑散落在各个Service方法里,每个方法都自己判断前置状态、更新后置状态。结果上线没多久就出事了:两个并发请求同时处理一个订单,都读到了“待接单”状态,然后一个把订单变成“已接单”,另一个把订单变成了“已取消”,最后数据直接乱掉。

后来痛定思痛,把所有订单状态流转统一收敛到一个状态机引擎里。核心设计是:每个业务状态定义出它允许的前置状态、允许的操作类型、以及执行操作后要触发的事件。比如“待接单”只允许执行“接单”和“超时取消”两个操作,“已接单”只允许执行“开始服务”“取消(需审批)”和“改约”。所有状态变更都经过同一个入口,在这个入口里先加锁、再校验、再变更、再发事件。并发控制用的Redis分布式锁,Key设计为order:lock:{orderNo},加锁和解锁必须配对,锁的超时时间根据业务类型区分,默认30秒。

还有一个细节值得分享:订单状态变化表和主订单表必须放在同一个事务里。早期为了减少数据库连接占用,状态变更记录是异步写入的,结果出现状态变了但没有log的情况,排查问题极其痛苦。把两者放到同一事务后,每条状态变更都有完整轨迹可查——比如用户发起退款,可以清楚看到是谁操作、什么时间操作、操作前状态是什么、操作后变成什么。这类系统里可追溯性就是生命线。

3. 核心模块的实操实现

3.1 护理代办工单引擎怎么落地

护理代办业务有一个典型特点:一个订单关联多个工单,而且工单之间存在先后依赖。

举个例子,老人需要办理长护险申请,这一个订单下可能拆出“材料准备工单”“申请递交工单”“审核跟进工单”“结果通知工单”四个工单。每个工单的负责人可能不同,处理时限也不同。如果只做到订单层面,根本无法精确控制每个环节的办理进度。

工单引擎的实现思路是:订单创建时根据配置好的“服务SOP模板”自动生成一张或多张工单,生成过程在数据库事务内完成,保证一张订单拆出的工单要么全部成功要么全部回滚。

每张工单都有自己独立的生命周期,状态包括:待执行、执行中、待确认、已完成、已退回、已关闭。护理员在App端接收到的是工单任务,而非抽象订单。“待确认”这个状态在养老场景里很关键,比如“材料已准备完毕”这个动作,需要老人或家属确认,确认后才算完成。老人对线上操作普遍不熟练,所以确认动作设计了“家人代确认+短信确认码”两种方式,短信确认码其实是个简化版的二次校验,防止护理员误提交。

工单超时提醒用的是RocketMQ的延迟消息机制。每张工单创建时会同步发送一个延迟消息,比如48小时的处理时限,就在48小时后触发检查,判断工单当前状态是否已完成。如果未完成,系统自动把工单标记为“超时待处理”,并通知运营人员介入。这个机制比我最初用定时任务轮询要精确得多,因为延迟消息是事件驱动的,不会出现整点扫描的延迟。

对于等待时间较长的政务类工单(如长护险审核),系统还有一个简化的“进度上报”功能。护理员可以在工单下以文本+图片的形式上报进度,比如上传“已取到受理回执”的照片,这个上报会实时推送给家属端,让家属对进度始终心里有数。

3.2 陪诊服务的预约、匹配与核销

陪诊服务和护理代办有一个本质区别:护理代办可以不那么在意“谁来做”,但陪诊服务需要匹配具体的人。老人去看病,家属会要求陪诊员耐心、熟悉医院、能帮老人拿药。这种对“人”的要求,决定了系统需要做匹配,而非简单的先到先得。

匹配策略上,第一版只按“距离最近”来推荐陪诊员,结果被家属投诉过:“最近的那个我一看照片就感觉不合适。”后来改为多因子评分策略:按服务区域距离、历史评分、接单量、好评关键词匹配度四个维度打分,做加权排序。比如一个陪诊员历史评价里被频繁提及“耐心”“熟悉取药流程”,那么下次匹配陪诊需求时会获得更高的分数。

实际推荐结果并不会直接锁定陪诊员,而是展示一组候选列表,由家属或老人端选择。“系统推荐+人工确认”的匹配方式比完全自动化更符合这个行业的特点,毕竟陪诊是很吃信任感的事情。

签到核销的设计同样值得说。陪诊员到达医院后,需要在App上点击“签到”,系统通过GPS定位判断陪诊员是否在规定范围内。但GPS存在偏差,尤其医院这种多栋楼、信号遮挡严重的场景,经常出现人已经到门诊楼门口但定位还在范围外的情况。我们兜底设计了扫码核销:家属端或老人端出示就诊预约二维码,陪诊员扫码完成签到,同时记录扫码时间和GPS坐标。这个“定位+扫码”的双通道机制,既保证了流程不会被GPS偏差卡死,又能留下关键节点的完整记录。

关键节点完成之后,陪诊员还要在App端填写陪诊小结:就诊科室、医生诊断(需脱敏)、下一阶段安排(比如“三天后复诊取报告”),然后自动触发家属端模板消息。为了让家属在老人不方便看手机的情况下也能舒心,我们没有等整体服务评价完才提醒,而是在每个核心节点完成后立即提醒,比如“已见到医生”“药已取好准备送回”,这类一句话消息往往才是家属最需要的。

3.3 费用结算与资金安全设计

养老服务的结算比一般电商复杂,因为涉及多方:平台、护理员(或陪诊员)、老人/家属,有时还有政府补贴。

设计上,我们把费用拆成两部分看:订单费用和结算费用。订单费用记录了用户应付的金额,包括服务费、交通补贴、材料费;而结算费用则记录了平台应收的佣金、护理员应得服务费用、以及可能的补贴抵扣。

这里有一个容易忽略的坑:订单金额不能等于结算金额。订单金额是用户视角的应付额,结算金额是平台视角的分账依据。比如同样是上门测血压,可能享受政府补贴后的订单金额是0元,但护理员依然需要获得劳务费用的——这笔钱是平台和政府之间结算的,不体现在用户订单上。如果把这两个金额混在表里,后续财务对账会非常痛苦。

支付环节,我们对接了微信支付和支付宝,并且全部走“预授权模式”。什么意思?用户下单时先冻结资金,等订单真正完成之后再执行扣款。如果中间取消,资金自动解冻。这种模式在服务行业很有用,能避免服务半途发生争议时资金已经被划走的问题。退款流程则需要运营端人工审核,毕竟护理员已经付出了部分劳动,系统很难自动判定该退多少。

护理员提现这块,为了简化财务流程,我们设计成结算周期模式:护理员完成的订单,在订单完成T+1日生成待结算账单,每周一统一结算上周全部账单到护理员的银行卡。这种批量结算比单笔实时转账要稳,也不容易触发银行的交易风控。

3.4 地图、推送与实时定位的实现

护理代办和陪诊业务都离不开地图能力和消息触达,这两个模块的细节直接影响使用体验。

地图选型上我用了高德地图Web服务API,主要用到三个能力:地理编码、路径规划、逆地理编码。下单时用户填写地址,前端通过地理编码把地址转成经纬度;匹配陪诊员时,系统用路径规划计算陪诊员到用户家的实际车程距离,替代直线距离;订单完成后,逆地理编码把陪诊员的实时轨迹点转成可读的位置描述,方便家属确认“陪诊员已经到小区门口了”。

护理员App的实时定位用的是高德地图的定位SDK,设置成每30秒上报一次位置。这里注意一个坑:高频定位会非常耗电,如果App在前台又开导航又传位置,手机掉电极快。我们后来把定位上报策略改成了“自适应模式”——订单进行中每30秒传一次,订单空闲时5分钟才传一次,省电效果非常明显,护理员端不再抱怨“接个单手机就没电了”。

消息推送这块,最初只接了一个短信平台来解决所有通知,但短信费用高、样式简陋、老人和家属阅读体验都不好。后来接入了微信公众号模板消息和小程序订阅消息两条通道。这里有一个特别重要的经验:线上环境必须准备通道降级方案。公众号模板消息偶尔会被微信官方限流,小程序订阅消息则需要用户主动授权且一次性有效。所以我们做了一套消息优先级机制:优先级最高的是App内推送和短信(用于订单状态变更、异常提醒),其次是公众号模板(用于服务完成、评价提醒),再次才是小程序订阅消息。一旦某个通道发生异常,系统自动把未投递成功的消息转成短信补发。

3.5 服务评价体系与异常上升机制

把评价体系放在技术篇最后讲,是因为它的逻辑比外表看起来重要得多。

每家平台都有评价功能,但大多数只做了“星星+评语”。这套系统在评价之上,增加了一个“服务标签”维度。服务完成后,家属端会展示一组服务标签,比如“准时到达”“服务专业”“态度亲切”“未按时完成”等。标签数据最终会和护理员的匹配算法挂钩,好的标签能给护理员加分,差的标签会直接降低护理员的接单权重。

更重要的是,我们做了一个异常上升机制。如果一位护理员在短期内连续收到差评或特定负面标签(比如“沟通困难”出现两次),系统会自动触发运营工单,把该护理员的接单状态改为“待观察”,限制其接受新订单,同时推送通知给运营人员进行回访。这种做法比单纯扣分要直观得多,也让护理员感受到系统对服务质量的实时约束力,而不只是事后统计。

从技术实现上看,评价数据和工单数据是分开的,评价完成后才允许关闭订单。这种设计可以避免“订单已关闭但评价还没写完”的状态不一致。另外评价内容支持图片上传,图片会先经过内容审核(对接了平台的图片内容安全接口),审核通过后才允许展示,防止出现广告、隐私泄露等不合规内容。

4. 落地过程中的坑与排查实录

4.1 订单状态不一致:一个典型的并发Bug

这个坑出现在上线后第三周,运营反馈说后台有个订单显示“已接单”,但护理员App上却显示“待接单”,两边数据对不上。

排查过程是这样的:先查订单状态日志,发现这个订单在极短的时间内发生了两次状态变更——“待接单→已接单”和“已接单→待接单”,而且两次操作的间隔只有几百毫秒。再往下查代码,发现是“改派”功能导致的:护理员A接了单但迟迟不出发,系统把订单重新放回池子里,此时护理员B接受了同样一单。A那边其实还没真正完成“放弃订单”的操作,B却已经“接单”成功,状态被写成了“待接单”。

这个Bug的本质是状态变更没有按照前置校验来执行。后来修复方案聚焦两点:一是把“改派”和“接单”两个操作强约束在同一把锁下,二是给订单状态机加上版本号——每次更新时检查版本号是否匹配,如果不匹配说明状态已经被别人改过,直接抛异常或走补偿逻辑。增加了这两道保护后,此类问题没有再复发。

4.2 消息通知丢失:延迟消息和最终一致性

消息模块早期出过丢消息的问题,具体场景是:护理员点击“开始服务”后,家属端应该收到一条模板消息,但有些家属反馈一直没收到。

排查结果很有意思。我们用的RocketMQ延迟消息,在“开始服务”成功的时候发送一条延迟消息给消息服务,消息服务再组装模板内容去调用微信接口。如果微信接口调用失败,代码里捕获了异常但没有做补偿,消息就石沉大海了。

修复方案是:把所有对外推送类消息都改成“先落库、再推送、异步确认”的模式。消息服务收到MQ消息后先把消息记录写入数据库(状态为“待发送”),再尝试推送。推送成功后更新消息状态为“已发送”;推送失败则保留“待发送”状态,由定时任务扫描并重试,超过三次仍失败则转短信通道。这套方案从根上解决了“推送失败后无感知”的问题。消息表加了状态字段和重试次数字段,同时为了性能,只保留最近三个月的消息日志,更早的归档到消息历史表。

4.3 陪诊员定位偏差导致签到失败

上面提过“GPS+扫码”双通道核销,其实这个方案是被逼出来的。

第一版只是纯GPS校验,上线后投诉率一下子飙升。有陪诊员反馈说,自己明明站在医院门口的台阶上,App却提示“不在服务范围内,签到失败”,只能打人工客服处理。

后来我们分析了医院周边的GPS信号情况,发现几家大医院的门诊楼区域普遍存在GPS偏移,偏移范围从几十米到两百米不等。虽然也试过把半径调大,但又面临新问题:陪诊员还没到医院就能“提前签到”,服务质量大打折扣。

最终采用“双通道”方案:以GPS为主、扫码为辅。系统判断GPS签到时允许一定范围的误差(人工设置了一个可配置的容忍值),如果GPS不在范围内,但又希望继续,可以请家属端或老人端出示动态二维码给陪诊员扫码。两种情况都记录时间和坐标,并且在管理后台会打上“GPS签到”或“扫码签到”的标签。运营人员在审核签到异常时,不需要打电话问双方就能直接判断是定位误差还是真没到岗,处理效率提升了一大截。

4.4 高并发场景下的“抢单”超卖问题

护理员接单的场景跟外卖骑手抢单很像,存在并发竞争:同一个订单同时被多个护理员点击接单。

第一版实现是典型的“先查询再更新”写法,结果不用想都知道,两个护理员同时点接单,都查询到订单是可接状态,然后都执行更新,最后订单被第二个护理员更新成功,第一个护理员的操作也显示成功,但订单落在第二个人手上。这个Bug在团队内部测试时没暴露,直到上线后有护理员反馈“我明明接单成功了,怎么一会儿显示被别人抢走了”。

修复方案用了MySQL悲观锁+Redis分布式锁双重保障。在Service层接单方法入口处,先通过SELECT ... FOR UPDATE锁定订单行,强制后续请求排队;同时在Redis里加一把以订单号为Key的短时锁,防止在事务提交前有缓存层的重复请求穿透。两把锁的区别在于,MySQL锁能防止事务层面的冲突,Redis锁能提前拦截无效请求、降低数据库压力。实践证明,这个方案在每天数千单的规模下非常稳定,负债率很低,也不影响用户体验。

5. 给同行的一些实在建议

做养老系统不比做普通电商系统,线上线下耦合度极高,所以我在项目收尾后最大的体会是:技术能力不是最大的瓶颈,产品边界意识和对服务流程的理解深度才是决定系统成败的关键。

技术选型上,Java生态足够支撑这类系统的复杂度。Spring Boot + MyBatis-Plus + Redis + RocketMQ这套组合,在相似规模的项目中表现得很稳,团队上手也快。但这套组合还很吃“规范”,尤其是事务边界、状态机、消息可靠性,三个环节任何一个放松约束,后期都会成为定时炸弹。

设计思路上,想把“护理代办、陪诊陪护全覆盖”落地,首要的是把角色和流程盘清楚,先做减法再做加法。早期讨论阶段我们恨不得把医院挂号、医保线上结算全部接入,后来才明白系统第一版要解决的核心问题只有三个:让家属看得见、让护理员跑得顺、让管理员管得住。其他功能可以放在后续迭代里慢慢加。

合规和数据安全方面,这个领域对老人隐私的保护要求很高。老人的健康数据、就诊信息、家庭住址、联系人电话都属于高度敏感数据。我们的做法是:数据库加密存储敏感字段,接口层统一做字段级脱敏,列表页不返回老人完整手机号和身份证号;操作日志完整记录所有人的查询行为,运营后台提供审计报表。虽然增加了一定的开发量,但这笔投入非常值得,既规避了法律风险,也赢得了合作方的信任。

最后再分享一个实用小技巧:这类系统的联调阶段,强烈建议准备一份完整的“端到端业务演示脚本”。从家属下单、陪诊员接单、签到、服务完成到评价结算,每一步用什么账号、操作什么按钮、预期看到什么结果,全部用文档固化下来。一方面验收时给合作方演示能节省大量沟通成本,另一方面新同事接手项目时,按着这份脚本跑一遍就能快速了解整个业务流程。这个方法在我们团队后期迭代开发中帮了大忙,强烈推荐。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进