☰
智慧社区系统从 0 到 1:多端复用架构与抢单池并发控制实战
2026/9/25 12:31:33 网站建设 项目流程

智慧社区系统从 0 到 1:多端复用架构与抢单池并发控制实战

智慧社区系统本质上是一套「后端统一 + 多端复用 + 本地生活服务履约」的业务中台。它把跑腿代取、家政上门、家电清洗维修、上门洗车、衣鞋洗护、本地商城、优惠券导购、小时工预约这些分散的社区服务,收敛到同一套用户、订单、商家和结算模型里,再通过小程序、公众号、H5 和 APP 触达居民。落到技术实现上,比较常见且容易维护的组合是:后端 Spring Boot + MyBatis Plus + MySQL,用户端用 UniApp(Vue 语法)一套代码多端编译,管理后台用 Vue + Element UI。下面按模块建模、架构分层、并发控制、多端鉴权与部署五个部分展开。

一、业务边界与数据建模

先把智慧社区的功能拆成四个域,避免后期表结构反复推翻:

  • 用户域:预约地址簿、我的快递查询、订单分类、邀请好友、会员状态、积分签到、历史浏览。
  • 服务域:跑腿服务、家政服务、上门服务、小时工、家电清洗、家电维修、上门洗车、衣鞋洗护。
  • 交易域:商城、淘客优惠券、本地生活团购、订单支付与退款。
  • 管理域:商家中心、个人资料修改、账号注销、投诉处理、抢单池调度。

其中「抢单池」是跑腿与家政类业务的调度核心,建议单独建表,与业务订单表解耦:

CREATETABLE`grab_pool`(`id`BIGINTNOTNULLAUTO_INCREMENT,`order_id`BIGINTNOTNULLCOMMENT'业务订单ID',`service_type`TINYINTNOTNULLCOMMENT'服务类型:1跑腿 2家政 3维修',`status`TINYINTNOTNULLDEFAULT0COMMENT'0待抢 1已抢 2已取消',`taker_id`BIGINTNULLCOMMENT'接单服务者ID',`grab_time`DATETIMENULL,`version`INTNOTNULLDEFAULT0COMMENT'乐观锁版本',`create_time`DATETIMENOTNULLDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`),UNIQUEKEY`uk_order`(`order_id`),KEY`idx_status_type`(`status`,`service_type`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;

注意uk_order索引,它是防止同一订单重复入池的后一道防线,比在应用层做判断可靠得多。

二、架构分层与多端复用

后端按标准三层组织,MyBatis Plus 负责单表 CRUD,复杂统计走 XML 或手写 SQL:

community-service ├── community-api # 对外接口层,Controller + DTO ├── community-service # 业务逻辑层,事务边界在这一层 ├── community-mapper # MyBatis Plus Mapper ├── community-domain # 实体与枚举 └── community-common # 统一返回、异常、工具类

用户端用 UniApp 的核心收益是一套业务代码编译到四个端。但不同端的差异必须提前隔离,否则会在页面里堆满if。推荐用条件编译:

// utils/pay.jsexportfunctionpay(orderNo,amount){// #ifdef MP-WEIXINreturnPay(orderNo)// 小程序:.requestPayment// #endif// #ifdef H5returnh5Pay(orderNo,amount)// H5:收银台// #endif// #ifdef APP-PLUSreturnappPay(orderNo)// APP:plus.payment// #endif}

同理,登录、分享、订阅消息、定位权限这四类能力都收敛到utils/下的统一适配层,页面只调用统一方法。这样新增一个端时,改动量集中在一个目录,而不是全站搜替换。

三、抢单池的并发控制与订单状态机

抢单是智慧社区里典型的并发场景:一个订单推送后,多个服务者同时点击接单。常见做法有三种,按可靠性递增:

  1. 数据库乐观锁:UPDATE grab_pool SET status=1, taker_id=? WHERE id=? AND status=0,靠affected rows判断是否抢到。实现简单,适合并发量不高的社区场景。
  2. Redis 分布式锁:SET lock:order:{id} {uuid} NX PX 3000,抢到锁后再更新数据库,注意锁的续期与释放要用 Lua 保证原子性。
  3. 队列串行化:把抢单请求投递到 MQ 或 Redis List,单消费者顺序处理,天然避免竞争。

生产环境建议乐观锁兜底 + Redis 锁削峰组合使用:

publicbooleangrab(LongorderId,LongtakerId){Stringkey="lock:grab:"+orderId;Stringtoken=UUID.randomUUID().toString();Booleanlocked=redisTemplate.opsForValue().setIfAbsent(key,token,Duration.ofSeconds(3));if(Boolean.FALSE.equals(locked)){thrownewBizException("当前订单正被其他服务者接单");}try{introws=grabPoolMapper.grab(orderId,takerId);if(rows==0){thrownewBizException("订单已被抢走");}orderService.transferToTaker(orderId,takerId);returntrue;}finally{// Lua 脚本比对 token 后删除,避免误删他人锁redisTemplate.execute(DEL_IF_EQUALS_SCRIPT,Collections.singletonList(key),token);}}

对应 Mapper:

@Update("UPDATE grab_pool SET status=1, taker_id=#{takerId}, grab_time=NOW(), version=version+1 "+"WHERE id=#{orderId} AND status=0")intgrab(@Param("orderId")LongorderId,@Param("takerId")LongtakerId);

订单状态建议用枚举 + 状态机约束,禁止在业务代码里随意setStatus:

publicenumOrderStatus{CREATED,// 待支付PAID,// 已支付,待派单GRABBED,// 已接单SERVING,// 服务中FINISHED,// 已完成CANCELED;// 已取消}

同时在order表加(user_id, status)和(taker_id, status)两个联合索引,因为「订单分类」和「历史浏览」几乎都按这两个维度查询。

四、多端鉴权与消息触达

智慧社区要同时面对小程序、公众号、H5 和 APP,鉴权方案要统一。推荐双 Token 模式:

  • 业务 Token:JWT,承载userId、role(居民 / 服务者 / 商家),有效期较短。
  • 刷新 Token:存 Redis,绑定设备指纹,支持主动踢下线。

第三方平台的openid与站内userId通过user_third表关联,一个用户可绑定多个端:

CREATETABLE`user_third`(`id`BIGINTNOTNULLAUTO_INCREMENT,`user_id`BIGINTNOTNULL,`platform`VARCHAR(20)NOTNULLCOMMENT'MP/MP_OA/H5/APP',`open_id`VARCHAR(64)NOTNULL,PRIMARYKEY(`id`),UNIQUEKEY`uk_platform_open`(`platform`,`open_id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;

消息触达方面,小程序用订阅消息、公众号用模板消息、APP 用厂商推送,同样封装成notifyService.send(userId, templateCode, params),内部按端路由。订单超时未支付、接单后超时未上门这类场景,用延时队列(RocketMQ 延时消息或 Redisson 的RDelayedQueue)比纯定时任务轮询更准。

五、部署与二次开发的注意点

部署上没什么玄学,关键是把配置外置:

spring:datasource:url:jdbc:mysql://${DB_HOST}:3306/community?useUnicode=true&serverTimezone=Asia/Shanghaiusername:${DB_USER}password:${DB_PWD}redis:host:${REDIS_HOST}

前端产物分两套:管理后台npm run build出静态文件交给 Nginx;UniApp 按端分别打包,小程序走开发者工具上传,H5 与后台共用一个 Nginx 实例的不同 location 即可,注意 H5 的 history 路由要配try_files $uri $uri/ /index.html。

如果是在既有智慧社区系统上做二次开发,建议优先关注四点:一是确认订单状态机的流转入口是否;二是检查抢单相关逻辑是否只走数据库条件更新;三是把支付回调的幂等处理补齐;四是理清定时任务在集群下的重复执行问题(用分布式锁或调度平台)。这四点理顺了,后续加同城遛狗、同城搭子社交这类新场景时,基本可以复用现有的用户、订单和消息体系。

FAQ

Q1:智慧社区后端为什么常用 Spring Boot + MyBatis Plus + MySQL?
Spring Boot 的生态能直接对接、支付、短信等 SDK;MyBatis Plus 在单表 CRUD 和分页上开发效率高,复杂 SQL 又能随时下沉到 XML;MySQL 在社区级数据量下足够稳定,配合索引优化可支撑订单与抢单池的高频读写。

Q2:一套 UniApp 代码真的能覆盖小程序、公众号、H5 和 APP 吗?
业务页面和请求层可以完全复用,差异集中在登录、支付、分享、推送、定位这五类平台能力上,用条件编译隔离到独立模块即可。需要注意各端审核与权限要求不同,上线前要分别验证。

Q3:抢单池高并发下怎样避免一单多抢?
三道防线:数据库索引阻止重复入池、WHERE status=0的条件更新保证只有一次成功、Redis 分布式锁做前置削峰。三者叠加后,即使出现短时并发,也不会产生重复接单。

Q4:订单超时未支付或超时未服务怎么处理?
优先用延时消息在到期时触发取消或提醒;若基础设施不具备,可用定时任务扫描加分布式锁防重复执行,同时保证取消操作本身幂等。

Q5:二次开发时容易踩的坑是什么?
状态字段被绕过状态机直接修改,导致后续流转异常;支付回调未做幂等,重复入账;集群环境下定时任务重复执行。建议在改动前先画出完整的状态流转图和任务清单。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询