汽车养护同城服务Java源码全解析:架构、订单与调度实战
发布时间:2026/9/11 7:54:05 锦皓数字建站

从年初到现在我一直在折腾一套面向汽车养护同城服务的 Java 后端源码项目。起因很简单我认识的一位养护店老板手里有 4 家直营店每天订单全靠电话和微信技师干完一单之后有无空档完全靠喊车主在高峰期排队两小时没人通知。他说了一句话让我印象特别深——“平台上的订单是不少可平台抽佣高、客户数据也不是我的活动一停立刻没单”。这句话基本上就是汽车养护行业做同城数字化服务升级的核心逻辑起点。这篇文章就是把我这套 Java 源码从零搭建到最后落地运营踩坑的全过程做一次系统拆解。适合三类人看一是想从传统门店向同城线上化转型的经营者二是刚接触本地生活类业务系统、想找一个完整业务闭环练手的 Java 开发者三是对订单履约、LBS、派单调度这类经典场景感兴趣的技术读者。我会把技术选型、源码级别的核心链路、以及实际部署运营中踩过的坑都摊开来讲尽量做到能直接复现。1. 传统汽车养护门店的订单断点为什么需要自建同城服务汽车养护和外卖、网约车最大的不同在于它不是一个30分钟必须送达的即时服务而是一个预约属性很强、履约过程很长、复购周期稳定的本地生活服务。洗车可能 30 分钟基础保养 40 分钟深度养护、贴膜、钣喷往往要半天甚至一整天才跑完。这个特性决定了系统设计不能照搬外卖那套高并发瞬时订单模型而是要把重心放在预约、调度、跟踪、结算这四条业务主线的闭环上。1.1 三方平台的双刃剑有订单但没沉淀绝大多数养护门店现在的订单来源无非两种线下自然到店以及美团、途虎、京东养车这类三方平台导流。线下到店不稳定受天气、季节、节假日影响极大三方平台倒是能稳定给单问题在于每单抽佣 5%-10% 不说车主的下单习惯、消费频次、车辆档案全部沉淀在平台上门店充其量只是履约方。最难受的是一旦门店想搞自己的会员营销、保养提醒、老客户回访平台给不了任何数据支撑甚至不允许跳出平台私下联系。这也是同城服务自建系统的第一个出发点把流量和数据的控制权拿回来。门店需要的不是要重新发明一个美团而是搭建一套从线上展示 - 预约下单 - 就近派单 - 技师上门/到店 - 完工结算 - 评价复购的完整养修履约链路将每一次服务产生的车型、里程、保养项目、技师手工会员卡数据形成资产沉淀。这正是 Java 后端 微信小程序/H5 端可以低成本实现的目标。1.2 养护行业的时间和空间特征决定了系统重心先看一组很典型的业务数据特征。以一个中等城市的汽车养护市场为例订单高峰集中在周末上午 9 点到 12 点以及工作日晚间 6 点到 9 点周一至周四白天大量技师处于空闲状态洗车和基础保养的到店预约提前期通常只有 1-3 小时而深度养护、贴膜这类项目车主会提前 2-5 天预约。业务维度典型特征对系统的要求订单触发预约为主、即时到店为次预约日历、时段库存管理服务半径门店周边 3-5 公里LBS 门店匹配与距离排序履约过程30 分钟至 2 天不等状态机跟踪、技师调度复购节奏保养周期 5000-10000 公里车辆档案 保养到期提醒支付结算到店支付 线上预付 优惠券订单核销、分账、对账体系这些特性推到系统设计上会形成一个判断同城汽车养护系统的技术重心不在高并发流量入口而在订单全生命周期的状态管理、门店与技师的调度算法、以及基于车辆档案的数据运营能力。而 Java 生态里 Spring Boot MyBatis-Plus Redis RabbitMQ 这套组合恰好是这个领域最成熟的基座。它不像自研一套实时撮合引擎那么重也比 PHP 或纯前端方案更容易撑起后续多门店、财务结算、员工绩效这类复杂业务模型。2. 系统技术选型与整体架构Java 生态下的一套可落地组合很多朋友一听到同城服务就觉得必须上微服务、必须搞 Docker/K8s、必须上 Spring Cloud Alibaba 全家桶。我的真实建议是在日订单量没有超过 3000 单之前单体应用 模块化分包就是最优解。微服务拆分解决的是团队协作效率和独立扩容问题不是业务跑不跑得动的问题。早期养护平台的瓶颈根本不在接口吞吐而在于业务规则复杂度和数据一致性问题这些恰恰是单体架构更容易把持住的。2.1 Spring Boot 3.x 模块化单体先别急着拆微服务我基于 JDK 17 Spring Boot 3.1.x 搭建了这套源码按业务域划分 Maven Module而不是按技术层划分。这个设计思路在后期扩展时帮了大忙。parent-bom 统一依赖版本管理 ├── quanshe-server # 启动主类、公共配置 ├── quanshe-api # 对外接口、DTO/VO 定义 ├── quanshe-core # 核心工具、公共异常、枚举 ├── quanshe-store # 门店管理门店、工位、设备、营业时间 ├── quanshe-order # 订单域预约、状态机、核销 ├── quanshe-worker # 技师域技师、考勤、调度、任务 ├── quanshe-member # 用户域JWT 认证、车辆档案、会员卡 ├── quanshe-marketing # 营销域优惠券、活动、邀请有礼 ├── quanshe-finance # 结算域价格、佣金、分账、对账报表 └── quanshe-common # Redis/MQ/短信/OSS 等基础设施封装这样分包的好处是代码结构上已经具备微服务的边界感但部署时还是一个 Jar 包不需要处理分布式事务和 RPC 调试成本。等门店数超过 50 家、需要按区域做独立的调度和容灾时再根据订单域和技师域去拆微服务就行了。下面这张表是我实测过比较稳妥的技术选型组件版本选型理由JDK17长期支持版本Spring Boot 3.x 官方推荐虚拟线程在 JDK21 才完整建议后迁Spring Boot3.1.x稳定且生态成熟MyBatis-Plus3.5.x业务 CRUD 效率高分页和条件构造器好用减少大量重复 SQLMySQL8.0业务主存储事务与索引能力完全足够Redis7.0缓存、分布式锁、技师忙碌状态、验证码RabbitMQ3.12订单超时未支付、技师任务通知、短信消息异步化XXL-JOB2.4定时任务派单超时重派、优惠券过期、日报对账高德地图 APIWeb服务门店检索、距离计算、路径规划、逆地理编码2.2 核心业务模块的边界与服务关系我画服务关系图时比较克制把系统收敛成了五个关键域这五个域基本能覆盖同城汽车养护 90% 的日常业务门店域是基础数据源负责维护门店位置、营业时间、服务项目、工位数、服务范围半径。用户域负责 C 端车主身份体系的建立包含一键登录、微信授权、车辆档案车牌号、车型、里程、下次保养里程。订单域承接车主下单动作生成预约单后根据门店距离和当前负载做推荐分配。技师域是履约核心每个技师绑定到工位通过任务永续查询的方式实时更新忙碌/空闲状态。结算域记录每一笔订单的收入、成本、优惠券分摊、技师提成后续对账全靠它。模块拆分最怕的就是把关系搞复杂我的原则很朴素所有跨域调用一律通过服务接口禁止直接操作别的模块的表所有状态变更必须走状态机禁止 UPDATE ... SET status 3 这种裸改。这个原则在后面派单、核销、退款流程中反复发挥了作用。3. 源码级拆解从车主下单到履约完成的核心链路接下来这节是整个项目含金量最高的部分。我先按一条最主线的路径梳理车主打开小程序 - 选择附近门店 - 选择服务项目 - 预约下单 - 平台派单给技师 - 技师接单 - 门店到店完成服务 - 核销收款 - 评价与复购。这里挑四个最值得抄作业的环节单独展开源码。3.1 门店推荐的 LBS 距离计算与服务覆盖圈下单第一步系统要根据车主的定位坐标找出 3-5 公里内能承接服务的门店。最简单的做法是 API 请求里带经纬度然后用 MySQL 的 ST_Distance_Sphere 函数直接 SQL 排序但门店量起来之后这个方法非常伤数据库。我的做法是先粗筛、后精算先用最大范围 5 公里换算经纬度差生成一个矩形边界把候选门店过滤到十几家以内再用 Haversine 公式精算真实球面距离最后结合营业时间和预约负载排序返回。public ListStoreDistanceDTO findNearestAvailableStores(double lat, double lng, int limit) { // 1. 粗筛以大约5公里为半径换算经纬度边界先在MySQL做矩形范围过滤 double latOffset 5.0 / 110.574; double lngOffset 5.0 / (111.320 * Math.cos(Math.toRadians(lat))); LambdaQueryWrapperStore wrapper Wrappers.lambdaQuery(Store.class) .eq(Store::getStatus, StoreStatusEnum.OPEN) .between(Store::getLatitude, lat - latOffset, lat latOffset) .between(Store::getLongitude, lng - lngOffset, lng lngOffset); ListStore candidates storeMapper.selectList(wrapper); // 2. 精算距离过滤出服务范围内的门店 ListStoreDistanceDTO result candidates.parallelStream() .map(store - { double distance GeoUtils.haversineDistance(lat, lng, store.getLatitude(), store.getLongitude()); return new StoreDistanceDTO(store.getId(), store.getName(), distance, store.getStoreLevel()); }) .filter(dto - dto.getDistance() storeService.getServiceRadius(dto.getStoreId())) .sorted(Comparator.comparing(StoreDistanceDTO::getDistance)) .limit(limit) .collect(Collectors.toList()); // 3. 结合营业状态和当前预约饱和度给门店加权排序 return businessService.applyLoadFactor(result); }public static double haversineDistance(double lat1, double lng1, double lat2, double lng2) { double radLat1 Math.toRadians(lat1); double radLat2 Math.toRadians(lat2); double a Math.sin((radLat1 - radLat2) / 2) * Math.sin((radLat1 - radLat2) / 2) Math.cos(radLat1) * Math.cos(radLat2) * Math.sin((Math.toRadians(lng1) - Math.toRadians(lng2)) / 2) * Math.sin((Math.toRadians(lng1) - Math.toRadians(lng2)) / 2); return 2 * 6371.0088 * Math.asin(Math.sqrt(a)); }这里有两个细节特别提醒第一门店的经纬度不要直接信任手动录入最好接一次逆地理编码做自动校准否则会出现门店明明在马路对面但算出来距离 5 公里的情况第二Haversine 公式假设地球是标准球形对于同城 10 公里以内的短距离计算误差控制在几十米级别完全够用。3.2 订单状态机一次养护服务全流程的闭环控制同城养护订单跟普通商品订单最大的差别在于状态多且会回跳。比如车主预约了周六下午 3 点的深度养护但技师临时请假订单就需要从已分配回退到待分配同时要给用户发换店或改约通知。如果没有状态机约束这些状态流转就会散落在一堆 Service 方法里排查问题时会非常痛苦。我用一个枚举类来维护所有合法流转public enum OrderStatusEnum { WAIT_PAY(0, 待支付), WAIT_ALLOCATION(10, 待分配技师), ALLOCATED(20, 已分配技师), WORKER_ACCEPTED(25, 技师已接单), IN_SERVICE(30, 服务中), WAIT_MEMBER_CONFIRM(40, 待车主确认), COMPLETED(50, 已完成), COMMENTED(60, 已评价), CANCELLED(90, 已取消); private final int code; private final String desc; private static final MapOrderStatusEnum, SetOrderStatusEnum TRANSITIONS new EnumMap(OrderStatusEnum.class); static { TRANSITIONS.put(WAIT_PAY, EnumSet.of(WAIT_ALLOCATION, CANCELLED)); TRANSITIONS.put(WAIT_ALLOCATION, EnumSet.of(ALLOCATED, CANCELLED)); TRANSITIONS.put(ALLOCATED, EnumSet.of(WORKER_ACCEPTED, WAIT_ALLOCATION, CANCELLED)); TRANSITIONS.put(WORKER_ACCEPTED, EnumSet.of(IN_SERVICE, WAIT_ALLOCATION)); TRANSITIONS.put(IN_SERVICE, EnumSet.of(WAIT_MEMBER_CONFIRM)); TRANSITIONS.put(WAIT_MEMBER_CONFIRM, EnumSet.of(COMPLETED)); TRANSITIONS.put(COMPLETED, EnumSet.of(COMMENTED)); } public boolean canTransferTo(OrderStatusEnum target) { SetOrderStatusEnum allowed TRANSITIONS.get(this); return allowed ! null allowed.contains(target); } }所有订单状态变更统一走OrderStatusTransitionService.changeStatus(orderId, sourceStatus, targetStatus)这个方法内部会先查当前状态再走canTransferTo校验最后 UPDATE 时还会带上WHERE status 源状态避免并发场景下两个请求同时把订单拖到不同终态。这个设计后来在售后工单、退款流程里也直接复用省了大量重复校验。3.3 技师调度忙闲状态的 Redis 缓存与分配策略技师分配是同城养护系统里最有意思的部分。洗车这类短时高频服务适合抢单技师有空就抢但深度养护、贴膜这类长时服务必须走派单要综合考虑技师技能标签、当前任务数、门店工位情况。我的方案是两种都支持由商家后台按服务项目设定。派单模式下系统先查这个技师在目标时间段内是否有重叠任务这个查询如果走 MySQL 会频繁产生慢 SQL。我的处理是给每个技师维护一个 Redis ZSetscore存任务开始时间戳member存订单 ID查询时用ZRANGEBYSCORE快速取出当天所有任务再在内存里做时间段重叠判断。public ListWorker selectAvailableWorker(Long storeId, LocalDateTime planStart, LocalDateTime planEnd, Long skillTagId) { // 1. 获取门店下符合技能标签的技师 ListWorker workers workerMapper.selectList(Wrappers.lambdaQuery(Worker.class) .eq(Worker::getStoreId, storeId) .eq(Worker::getStatus, WorkerStatusEnum.ON_DUTY) .apply(FIND_IN_SET({0}, skill_tags), skillTagId)); // 2. 利用Redis ZSet检查技师是否已有重叠任务 String key RedisKeyBuilder.workerScheduleKey(storeId, LocalDate.now()); return workers.stream() .filter(worker - { SetString orderIds redisTemplate.opsForZSet() .rangeByScore(key, planStart.toEpochSecond(ZoneOffset.ofHours(8)) - 60, planEnd.toEpochSecond(ZoneOffset.ofHours(8)) 60); return orderIds null || orderIds.isEmpty(); }) .sorted(Comparator.comparingInt(worker - worker.getCurrentTaskCount())) .limit(3) .collect(Collectors.toList()); }这里有个很重要的细节判断重叠时间段时首尾各预留 60 秒缓冲。因为技师完成一个服务后需要收拾工位、录入回检单如果前一个单子和后一个单子无缝衔接现场很容易手忙脚乱。这 60 秒缓冲的成本几乎为零但对技师体验的提升非常明显。3.4 订单超时关闭与技师未接单自动重派预约单支付后系统会给技师推送待接单通知。但不是每次都能顺利接单技师可能正在忙、手机没看、或者临时请假。如果不做自动处理这个订单就卡死了。我的处理方式是引入 RabbitMQ 延迟消息支付成功时发一条DelayMessage{orderId, expirationTime}消费端收到后先检查订单状态如果仍是待分配或已分配就触发重新派单逻辑同时给用户推送一条技师调度中的通知。RabbitListener(queues order.auto-repeat-dispatch.queue) public void handleRepeatDispatch(OrderTimeoutMessage message) { Order order orderMapper.selectById(message.getOrderId()); // 只有仍然停留在待分配/已分配状态才执行重派 if (order null || (order.getStatus() ! OrderStatusEnum.WAIT_ALLOCATION.getCode() order.getStatus() ! OrderStatusEnum.ALLOCATED.getCode())) { return; } dispatchService.dispatch(order.getId()); }延迟消息的过期时间我最初设成 10 分钟后来根据门店反馈调到 6 分钟。原因是主动点击立即下单的车主对时效预期很强超过 5 分钟没响应用户流失率明显上升6 分钟既能给技师留出反应窗口又不会让用户等得失去耐心。4. 同城服务的关键支撑LBS 接入、消息触达与对账设计订单链路跑通后系统才到了一个勉强能用的状态。真正让这个项目从演示 Demo变成能运营的系统靠的是另外三块基础能力地图/LBS 的稳定接入、异步消息触达、以及财务结算对账。这三块表面上不如订单状态机那样有技术含量但任何一个环节出问题业务就会立刻崩盘。4.1 地图能力接入POI 检索、逆地理编码与服务范围圈定同城服务的同城两个字注定系统离不开地图相关能力。我这里选了高德开放平台原因很简单服务接入文档清晰、Web API 免费配额够用企业认证后日调用量充足、还有配套的小程序 SDK。实际开发中用到最多的能力是这三个逆地理编码用户授权定位后把经纬度转换为省份/城市/区县/街道文字方便门店后台做区域统计距离计算接口Web 服务骑行/驾车距离比直线距离更接近真实到店成本我在推荐门店排序时会把驾车距离作为二级排序因子POI 检索与周边推荐门店管理后台可以按城市关键词检索并选定门店位置省去手填经纬度的麻烦行政区域围栏 API用于运营后台设定服务开通城市把业务范围约束在已开通区域内。接入地图能力时有一个坑必须提醒高德用的 GCJ-02 坐标系与 GPS 原生坐标存在偏移从小程序端wx.getLocation拿到的经纬度已经是 GCJ-02 加密坐标所以直接传给高德 API 没有问题但如果从 GPS 硬件设备比如门店巡检设备直接取点就必须先做坐标转换否则门店位置会整体偏移几百米。4.2 服务范围如何圈定圆形半径比行政区域更好用最初设计门店服务范围时我采用的是门店所属区县这种行政区域模式原因很简单——运营人员看得懂。但实际跑了一个月后发现行政区域和道路距离并不是一回事。一个门店如果恰好开在区县交界处最核心的 3 公里范围内的用户可能被划到了隔壁区于是系统推荐不到这家店而实际上从这家店开车过去只要 5 分钟。后来改为以门店为圆心、3-5 公里为半径的圆形服务圈同时允许总部按门店级别调整半径洗美店 3 公里、维修保养店 5 公里这个方案准确率高得多。服务范围的真正含义不是画个圈而是在这个圈内我能稳定提供履约能力。初期不要盲目扩大半径宁可在覆盖范围内做深确保每个订单都能在承诺时间内响应。4.3 消息触达的方案WebSocket 短信网关的双通道同城养护业务里消息触达的实时性直接影响履约质量。车主下单后希望尽快知道谁在服务我、技师什么时候到岗/上门技师端则希望新任务来了马上弹出来不要靠 App 里手动刷新。技师端我用了 WebSocket 长连接 RabbitMQ 广播方案。服务端有新的派单任务时订单服务把 TaskAssignedEvent 发到 MQMQ 消费者再通过 WebSocket 推送给指定工号对应的长连接。推送给前端的消息体里只带任务 ID前端收到后自己调详情接口拉取避免把整个 DTO 序列化后塞进 WebSocket 消息导致带宽浪费。C 端车主侧考虑到微信小程序后台运行时长受限我用的是订阅消息 短信兜底。关键节点支付成功、技师已接单、服务完成待评价发小程序订阅消息如果 5 分钟内用户未查看再发一条短信提醒。短信成本大约每条 4-5 分钱对客单价几百元的养护服务来说是完全可以接受的运营成本。4.4 财务结算别等月底才对账每单实时落账汽车养护系统的财务复杂度被很多人低估了。一张 298 元的深度养护订单可能构成是车主付款 298 元其中优惠券抵扣 30 元平台补贴 20 元门店实收 248 元技师提成 80 元平台服务费 40 元剩余 128 元为门店收入。如果这些数字不按订单维度记录清楚月底财务核算时会是一笔糊涂账。我的解决办法是设计一张order_settlement_detail表在订单完成时将整条链路的资金分摊一次性落库字段含义示例order_id订单号202401150001234settle_type结算类型收款/退款/平台补贴amount金额298.00coupon_amount优惠券分摊30.00platform_subsidy平台补贴20.00worker_commission技师提成80.00platform_fee平台服务费40.00store_income门店纯收入128.00这样每天凌晨通过 XXL-JOB 跑一次对账任务汇总当日完成订单的结算明细与支付渠道账单核对总金额不一致时告警并输出差异订单编号。这个机制帮我提前发现了好几类问题比如优惠券重复核销导致的门店收入偏低、技师提成计算时没有排除平台补贴部分等全部在月结之前就处理干净了。5. 我复盘出的几个坑与优化建议这部分原本想写成避坑指南但实际写下来觉得更像是一次真实运营后的源代码级复盘。项目上线前我对自己的设计还挺有信心直到真金白银的订单和真实用户的耐心涌入才发现有些问题上线的瞬间就会暴露出来。5.1 抢单模式的并发超卖不是所有场景都适合 Redis 减库存洗车这类短时高频服务我给技师开放了抢单模式。最初实现是技师点击抢单 -redisTemplate.opsForValue().decrement(store:order:grab:token)- 剩余量大于等于 0 就抢单成功。这个写法在低并发下没问题但实测发现同一个任务被多位技师同时点击时会出现两个技师都抢到同一单的情况。根因在于decrement操作是原子的没错但我后续的抢单成功写库动作并不是原子的两个请求可以同时通过 decrement 判断然后都执行了业务操作。正确做法是用Redis Lua 脚本把扣减 token 检查剩余量 写技师与订单的关系合并成原子操作local tokenKey KEYS[1] local workerOrderKey KEYS[2] local workerId ARGV[1] local orderId ARGV[2] -- 如果该技师已经抢过该订单直接拒绝 if redis.call(SISMEMBER, workerOrderKey, workerId) 1 then return 0 end local remaining redis.call(GET, tokenKey) if tonumber(remaining) and tonumber(remaining) 0 then redis.call(DECR, tokenKey) redis.call(SADD, workerOrderKey, workerId) return 1 end return 0Lua 脚本在 Redis 中是原子执行的从检查到扣减再到记录技师关系不会被其他客户端插入中间状态。实际用下来这个脚本在抢单峰值时延稳定在 3ms 左右没有再出现过超卖。5.2 技师位置追踪别把 App 做成电老虎系统上线第二周门店反馈技师手机掉电极快一天要充两次电。排查后发现原因非常简单我为了做技师到店轨迹和实时位置展示让技师端每隔 5 秒上报一次 GPS 坐标HTTP 请求频率太高屏幕常亮加上网络通信直接吃掉了大量电量。优化方案是双管齐下。前端根据场景动态调整上报频率App 在前台且处于任务进行中时每 15 秒上报一次切到后台但任务仍在进行时每 30 秒上报一次后台且无任务时停止上报。服务端则不再把每一次坐标都写入 MySQL而是先写到 Redis List 里每 5 分钟批量落库一次。这样既保住了轨迹完整度又把电量消耗控制在可接受范围。5.3 营销补贴与技师提成优惠券分摊最容易算错账有一段时间财务反馈部分订单门店实际收入比预期低。排查后发现是优惠券分摊逻辑写错了某张 30 元优惠券本应由平台承担但我写结算逻辑时把优惠券金额直接记成了门店让利导致门店收入被克扣。这个问题的根子在于开发时没有明确补贴资金归属这个业务概念。处理方式是在营销模块统一维护一个优惠活动计费类型字段PLATFORM_SUBSIDY平台补贴还是STORE_GIVE门店让利。在订单生成结算明细时根据这个字段决定优惠成本记到平台还是门店头上而不是简单把订单优惠金额统一从门店收入里减掉。这种口径问题越早规范越好后续接财务总账系统时才不用翻历史订单。5.4 车辆档案是复购利器但也需要运营支持从源码角度看车辆档案表的设计非常容易但让它发挥价值需要运营策略配合。车主第一次完成服务后系统根据车牌号、车型、本次保养项目自动推算建议下一次保养时间机油保养按 5000-8000 公里或半年空调滤芯按 1 万公里或一年。这个保养到期提醒功能在代码实现上不复杂但真正把复购率从 17% 拉到 33% 的是运营在提醒文案和优惠策略上的配合而不是技术本身。具体做法是XXL-JOB 每天凌晨扫描即将到期的车辆档案生成待提醒列表优先给 30 天内到期且在 6 个月内有过到店记录的车主发送提醒短信和小程序订阅消息文案不打“保养提醒”这种阳春白雪的话而是加上门店最近可预约的空闲时段和一张小额优惠券。这套组合拳的转化效果远好于偶然性回访。写在源码之外的一点体会跑完整个项目我最深的感觉是汽车养护同城服务系统的技术难度不在某个单一功能的算法复杂度上而在于业务状态和资金状态的一致性保障。Java 生态里 Spring Boot、Redis、MQ 提供的都是通用能力真正决定这个系统好坏的是我有没有想清楚每一个状态什么时候变、怎么变、变了之后哪些数据需要同步调整。比如订单状态改成已完成的瞬间技师提成有没有结算、优惠券有没有核销、车辆档案有没有更新下次保养日期这四件事必须在一个事务里完成少一件后期对账都会很难受。如果让你在这个项目上做二次扩展我建议优先做技师端 App 的离线缓存能力——洗车车间里网络信号往往不好技师在车库和车间之间移动时任务列表刷新失败率会偏高。这块做好之后整个系统的稳定性会更上一层楼。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。