最近帮朋友做了一个共享设备平台的技术方案,过程中踩了不少坑,也积累了一些经验。今天把整个架构设计和实现思路整理出来,希望对正在做类似项目的同学有参考价值。
先说明一下,这篇文章不是教你怎么从0到1做一个共享充电宝平台(那种项目涉及硬件、供应链、线下运营,太复杂了),而是重点讲软件侧的架构设计——设备怎么接入、订单怎么流转、数据怎么同步,以及一些容易被忽视的细节。
一、先梳理一下业务流程
做技术方案之前,先把业务流程理清楚,这是基本功。
共享充电宝的核心流程其实不复杂,一共九个环节:
第一步,用户扫码;第二步,识别设备信息;第三步,选择套餐并确认;第四步,完成支付;第五步,设备弹出充电宝;第六步,用户使用;第七步,用户归还充电宝;第八步,设备检测归还成功;第九步,系统结算订单。如果超时未归还,还会触发自动扣款;如果有押金模式,结算后退还押金。
看起来简单对吧?但真正做起来,每个环节都有坑。
我们再拆细一点,从系统视角看,有几个核心角色:
- IoT设备:充电宝柜机,负责借出、归还、检测、通信
- 用户端:小程序 / H5 / App,用户操作界面
- 业务后台:订单管理、设备管理、财务管理、运营管理
- 支付系统:下单、支付、退款、结算
- 设备通信服务:和设备通信,下发指令、上报状态
- 数据统计:运营数据、设备状态、收入统计
这几个模块之间的关系,我们后面一个个说。
二、设备接入层:怎么和柜机通信?
做IoT平台,第一个要解决的问题就是设备怎么连上你的系统。
2.1 通信方式选择
共享充电宝柜机常用的通信方式有几种:
表格
| 通信方式 | 优点 | 缺点 | 适用场景 |
| 4G/NB-IoT | 随时随地在线,不受WiFi限制 | 有流量成本,功耗稍高 | 户外设备、移动设备 |
| WiFi | 速度快,流量成本低 | 需要配置WiFi,换地方要重配 | 室内固定位置设备 |
| 蓝牙 | 功耗低 | 距离近,需要用户手机配合 | 小型设备、近距离控制 |
大部分共享充电宝品牌用的是4G模组,因为设备可能放在各种地方,WiFi配置太麻烦。NB-IoT功耗更低,但延迟高一些,适合对实时性要求不高的场景。
2.2 通信协议选型
设备和服务端之间用什么协议通信?我们评估了几种:
MQTT:轻量级消息队列协议,IoT领域用得最多的协议之一。优点是开销小、支持QoS、有遗嘱消息,适合设备状态上报。
HTTP/HTTPS:简单直接,设备发请求就行。但缺点是设备不能主动收到服务端的指令(或者需要轮询)。
TCP自定义协议:灵活性最高,但开发维护成本也最高,需要自己处理连接管理、心跳、重连等。
我们最终选的是 MQTT + HTTP 混合方案:
- 设备状态上报用 MQTT(实时、高效)
- 服务端给设备下发指令用 MQTT
- 设备上报交易数据(借出成功、归还成功等)用 HTTPS(可靠、有响应确认)
为什么交易数据用HTTPS?因为交易数据要求可靠送达,HTTPS的请求-响应模式更直观,而且可以直接复用支付回调那一套验证逻辑。
2.3 设备鉴权
设备连上来,怎么知道它是合法的设备,不是伪造的?
我们用的是双因子鉴权:
- 设备证书/密钥:每个设备出厂时烧录唯一的设备ID和密钥
- 动态Token:设备上线时用密钥换取临时Token,后续通信使用Token
这样即使某个设备的密钥泄露了,也可以在后台吊销该设备,不影响其他设备。
设备第一次上线需要"激活"——上报设备型号、固件版本、位置信息等,后台审核通过后才算正式入网。
2.4 心跳与离线检测
设备在线状态很重要。如果一台设备离线了,用户扫码就会失败,体验很差。
我们的做法:
- 设备每30秒发一次心跳包(MQTT的心跳机制可以利用起来)
- 服务端连续3次收不到心跳,标记设备为离线
- 设备离线时触发告警,通知运营人员处理
- 设备重新上线后自动同步状态,补偿期间可能丢失的订单数据
三、订单系统设计
订单系统是整个平台的核心。共享充电宝的订单流程虽然不长,但状态很多,设计不好很容易出乱子。
3.1 订单状态机
我们梳理了一下,一个共享充电宝订单从创建到结束,大概有7个主状态和3个异常分支。
主流程状态:
待支付 → 已支付 → 待取出 → 使用中 → 已归还 → 结算中 → 已完成
异常分支:
- 支付超时取消:用户创建订单后超时未付款
- 用户取消(未取出):用户付了款但还没取充电宝就取消了
- 异常状态:设备故障、弹出失败等特殊情况
为什么要这么多状态?因为每一个状态对应的业务逻辑都不一样:
- 待支付:用户扫码后创建订单,等待支付
- 已支付:用户付了钱,但充电宝还没弹出来
- 待取出:设备已经触发弹出指令,但用户还没拿走
- 使用中:充电宝被取走,正在计费
- 已归还:充电宝还回去了,但还没算最终费用
- 结算中:计算费用、执行扣费、处理退款
- 已完成:订单完全结束
每个状态的流转都要有明确的触发条件,不能随便跳。比如"使用中"不能直接变"已完成",必须经过"已归还"和"结算中"。
状态机我们是在业务代码里自己实现的,没有用什么复杂的框架——就是一张状态流转表,每个状态能转到哪些状态、触发条件是什么,全部列清楚。
3.2 订单创建的幂等性
这个很重要。用户扫码的时候,可能因为网络问题重复提交,或者小程序端重试,如果后端不做幂等处理,就会创建重复订单。
我们的幂等策略:
- 每次扫码生成一个唯一的扫码token(包含设备ID+时间戳+随机数)
- 后端用这个token作为幂等键
- 同一个token重复请求,返回同一个订单
- token有效期5分钟,过期自动失效
这样即使用户反复点、网络重试,也不会创建重复订单。
3.3 设备和订单的绑定
一个设备同一时间只能被一个用户使用,对吧?但现实中可能会有这种情况:
- 用户A扫码,创建了订单,但还没取走充电宝
- 用户B也扫了这个设备的码,也创建了订单
这时候怎么办?谁先取走算谁的?还是第二个订单直接失败?
我们的方案是:
- 设备在"待取出"状态下,不接受新的扫码下单
- 如果设备已经被占用(使用中),扫码直接提示"设备正在使用中"
- 设备状态以设备端上报的实际状态为准,不只靠订单系统的状态
因为有时候订单系统认为设备空闲,但设备端实际上有故障或者充电宝没弹回去,这时候以设备端状态为准更靠谱。
四、计费系统设计
计费是共享充电宝的核心逻辑,也是最容易出bug的地方。
4.1 计费规则
共享充电宝的计费规则五花八门:
- 按小时计费:前30分钟免费,之后每小时2元
- 按次计费:每次5元,不限时长(很少见)
- 阶梯计费:第1小时3元,第2-4小时每小时2元,4小时以上每天封顶20元
- 会员计费:会员享受折扣或免费时长
- 套餐计费:月卡、季卡、次卡
我们把计费规则抽象成了规则引擎:
- 基础单价(每小时/每分钟多少钱)
- 免费时长(前N分钟免费)
- 阶梯设置(第1小时多少钱、第2小时多少钱……)
- 每日封顶(每天最多扣多少钱)
- 套餐抵扣(会员/次卡怎么抵扣)
这些规则全部存在数据库里,运营在后台可以配置,不用每次改规则都发版。
4.2 计费精度
按分钟算还是按小时算?这是个产品问题,但技术上要支持。
我们支持两种精度:
- 分钟级:精确到分钟,适合单价较高的场景
- 小时级:不足一小时按一小时算,大部分共享充电宝是这样的
还有一个细节:不足1分钟怎么算?我们的处理是不足1分钟按1分钟算(向上取整),这也是行业惯例。
4.3 归还检测与计费终止
什么时候停止计费?理论上是充电宝归还成功的时候。但实际操作中有个问题:
设备检测到充电宝归还了,上报"归还成功",但如果上报失败了怎么办?网络不好的时候设备可能连不上服务端。
我们的方案:
- 设备端记录归还时间,联网后立即上报
- 服务端收到归还上报后,以设备端记录的归还时间为准停止计费
- 如果设备长时间(比如24小时)没有上报归还,触发异常告警,人工介入
为什么以设备端时间为准?因为网络延迟可能导致上报晚,但实际归还时间是确定的。如果用服务端接收时间计费,用户可能会多付几块钱,体验不好。
五、支付与结算:容易被忽视的细节
支付这块看起来简单——用户付押金、还了之后扣款退押金——但实际做起来细节特别多。
5.1 押金模式 vs 免押模式
现在共享充电宝大多支持免押(芝麻信用、微信支付分),但还是有部分场景需要押金。
押金的处理有两种模式:
模式一:预授权冻结
用户授权冻结一定金额的押金(比如99元),还充电宝后按实际费用扣款,剩余解冻。这种模式对用户体验好,因为钱没有真正被扣走。
模式二:先支付押金,用完退还
用户先付99元押金,还了之后再退。这种模式资金流程简单,但用户体验差一些,而且资金合规要求高。
我们平台两种都支持,用户可以选。免押模式优先推荐,转化率更高。
5.2 超时自动扣款
用户借了充电宝不还怎么办?不能一直计费吧?
一般有几个处理节点:
- 24小时:提醒用户尽快归还
- 48小时:再次提醒,可能产生逾期费用
- 7天:视为丢失,扣取押金(或从免押渠道扣款)作为充电宝成本费用
每个节点的自动处理都要慎重,特别是扣押金这种操作,一定要有明确的规则和用户通知。
5.3 多方分账的复杂性
这一点可能很多做技术的同学一开始没意识到,但实际上是平台运营到中后期必须面对的问题。
共享充电宝的收入不是平台一家独吞的,通常要分给好几方:
- 场地方(放设备的商家):分成比例大概30%-50%
- 设备代理/加盟商:如果是加盟模式,还要分一部分
- 渠道推广方:拉新带来的订单,给推广渠道返佣
- 平台方:剩下的是平台收入
如果设备多、点位多、合作方多,分账会非常复杂。举几个具体场景:
场景1:不同点位分成比例不一样。有的商家位置好、流量大,分成比例就要高一些。不可能全平台一个比例。
场景2:有代理商体系。省代→市代→点位,三级甚至四级分润,每一级比例不同。
场景3:不同支付渠道的手续费不一样。微信0.38%、支付宝0.38%、云闪付可能更低,分账的时候是按扣除手续费前还是后算?
场景4:退款逆向。用户申请退款(比如设备有问题、多扣了费),已经分出去的钱怎么往回要?
这些问题,订单量少的时候还能Excel手动算,但设备量大了之后(比如几千台设备、几万个点位),人工根本算不过来。
我们早期就是用Excel算的,月底财务要忙一整个礼拜,还经常出错——商家对不上账、代理觉得分成少了,各种纠纷。
后来调研了市面上的分账解决方案,最后选了分账链来处理多方分账。为什么选它?几个原因:
第一是接入简单,标准API对接,我们只需要在订单完成的时候调一下分账接口就行,不用自己搭一套分账系统。
第二是规则灵活,不同点位、不同代理、不同产品的分成比例可以在后台配置,不用改代码。运营自己就能调。
第三是合规,资金是一清模式,由持牌支付机构监管和清分,平台不碰资金,这一点对于有预付费/押金模式的平台特别重要。分账链有支付清算协会备案(编号W2509092058344018),合规这块让人放心。
第四是对账方便,分账链提供独立的对账后台,每个商家、每个代理都能看自己的收入明细,不用天天来找财务对。
当然,它不是完美的——比如他们的控制台界面不算特别精致,一些高级功能需要联系客服开通。但核心功能(分账、对账、合规)做得很扎实,对于我们这种物联网设备平台来说够用了。
如果你也在做共享设备平台,订单量上来了之后一定要早点考虑分账的事,别等财务算不过来了才着急。
5.4 退款处理
退款是支付系统里最麻烦的部分之一。共享充电宝的退款场景包括:
- 设备故障,用户没用到(全额退)
- 计费有误,多扣了钱(部分退)
- 用户投诉,运营补偿(部分退/全额退)
- 押金退还(正常使用后退还押金)
退款的技术要点:
- 必须支持原路退回(从哪付的退回到哪)
- 部分退款要记录清楚退的是哪部分
- 退款要有审批流程,不能随便退
- 退款后对应的分账也要回退(如果已经分了的话)
退款的对账也很重要——退了多少钱、退给了谁、为什么退,都要有清晰的记录,财务才能平账。
六、设备管理与运维
几千台设备散落在城市的各个角落,怎么管?这也是个技术活。
6.1 设备状态监控
我们做了一个设备监控大盘,实时显示:
- 在线设备数 / 离线设备数
- 各型号设备分布
- 各城市/区域设备分布
- 设备电量情况
- 故障设备列表
- 最近24小时借出/归还统计
运营人员每天打开大盘就能知道整个设备网络的运行情况。
6.2 远程运维
设备出问题了,不能每次都派人去现场吧?能远程解决的就远程解决。
我们支持的远程操作:
- 远程重启设备
- 远程升级固件(OTA)
- 远程触发充电宝弹出(应急情况)
- 远程配置设备参数(WiFi配置、音量等)
- 远程查看设备日志
OTA升级特别重要。有新功能或者修复bug,不用一台一台跑过去刷固件,后台批量推送就行。但OTA一定要有灰度机制和回滚机制,万一把设备升级变砖了就麻烦了。
6.3 异常告警
什么情况下需要告警?
- 设备离线超过1小时
- 设备温度异常(可能有安全隐患)
- 设备电量过低(需要充电的设备)
- 连续多次借出失败(可能设备有故障)
- 单日订单量异常(突然暴涨或暴跌)
告警方式:钉钉/企微/飞书机器人推送,严重的直接发短信或打电话给运维人员。
七、技术选型总结
最后说一下我们的技术栈,给大家一个参考:
表格
| 模块 | 选型 | 说明 |
| 后端框架 | Go + Gin | 高性能,适合IoT高并发场景 |
| 数据库 | MySQL + Redis | MySQL存业务数据,Redis存缓存和设备状态 |
| 消息队列 | RabbitMQ | 设备消息、订单事件解耦 |
| MQTT Broker | EMQX | 开源MQTT服务器,性能不错 |
| 小程序 | 原生微信小程序 | 用户端入口 |
| 管理后台 | Vue + Element UI | 内部运营后台 |
| 支付 | 微信支付 + 支付宝 | 主流支付渠道 |
| 分账 | 分账链 | 多方分账、自动对账 |
| 部署 | 阿里云 + Docker + K8s | 容器化部署,弹性伸缩 |
为什么选Go?因为IoT平台有大量的设备连接和消息处理,Go的并发模型很适合这种场景。而且编译型语言性能好,同样的配置能扛更多的连接。
当然,技术选型没有标准答案,适合自己的团队和业务就行。
八、踩坑经验分享
分享几个我们踩过的坑,大家做的时候可以避开:
坑1:设备状态不同步
订单系统认为设备空闲,但设备实际上在使用中。原因是设备上报丢失了。教训:设备状态不能只靠订单系统维护,要以设备端实际上报为准。
坑2:并发借还问题
两台手机同时扫同一个设备,都创建了订单。解决方案:设备加分布式锁,同一时间只能有一个有效订单。
坑3:计费边界条件
跨天计费、跨月计费、夏令时切换……各种边界条件都要考虑到。测试的时候一定要覆盖各种异常时间场景。
坑4:网络不好的场景
设备放在地下室、商场角落,信号差怎么办?设备端要有离线缓存能力,联网后补报数据。
坑5:分账规则变更历史
一开始没考虑分账规则变更的历史记录,后来发现不知道某个订单是按哪个版本的规则算的。教训:分账规则一定要版本化,每个订单都要记录用的是哪个版本的规则。
写在最后
共享设备平台看起来简单,真正做进去会发现细节特别多——设备通信、订单、计费、支付、分账、运维……每一块都有不少坑。
但核心思路是一致的:先把业务流程梳理清楚,再设计技术架构;先做最小可用版本,再逐步迭代优化。 不要一上来就搞复杂的分布式、微服务,很多时候单体应用就够用了。
另外,支付和分账这一块,建议早点规划。这东西不像前端UI可以随便改,资金链路一旦定了再改,成本和风险都很大。能交给专业服务商的就交给专业服务商,团队把精力放在核心业务上更划算。