1. 项目背景与核心价值
去年疫情期间,我所在的小区经历了长达28天的封闭管理。作为技术负责人,我亲眼目睹了居民们通过微信群接龙购买生活物资的混乱场景:订单错漏、统计困难、配送延迟等问题层出不穷。这促使我开发了这套前后端分离的小区疫情购物系统,上线后成功服务了600+户居民,日均处理订单量突破200单。
这个系统本质上是一个轻量级的社区电商平台,核心解决了三个痛点:
- 商品信息数字化管理(告别Excel表格和图片轰炸)
- 订单自动化汇总(避免人工统计错误)
- 配送路线智能规划(提升物资分发效率)
技术选型上采用SpringBoot+Vue的经典组合,不仅因为这是目前企业级开发的主流方案,更看重其快速迭代的能力——从立项到上线我们只用了72小时。下面分享从开发到部署的全套实战经验。
2. 系统架构设计解析
2.1 技术栈选型依据
后端技术矩阵:
- SpringBoot 2.7.3:选择LTS版本确保稳定性
- MyBatis-Plus 3.5.1:简化DAO层开发,内置分页插件
- Hutool 5.8.5:处理中国特色的地址解析和短信验证
- Redis 6.2:缓存热点商品数据和抢购锁
前端技术方案:
- Vue 2.6 + ElementUI:考虑社区工作人员的操作习惯
- ECharts 5.3:可视化展示物资需求分布
- WebSocket:实时推送订单状态变更
特别注意:疫情期间很多志愿者使用的是老旧安卓手机,因此放弃Vue3的Composition API写法,全部采用Options API保证兼容性。
2.2 数据库关键设计
用户表t_user特别添加了building_no和room_no字段用于配送定位,采用复合索引INDEX(building_no, room_no)提升查询效率:
CREATE TABLE `t_order` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL COMMENT '关联用户ID', `goods_json` json NOT NULL COMMENT '商品快照', `total_amount` decimal(10,2) NOT NULL COMMENT '实际支付金额', `delivery_building` varchar(20) NOT NULL COMMENT '配送楼栋', `status` tinyint NOT NULL DEFAULT '0' COMMENT '0待支付 1待配送 2已完成', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user` (`user_id`), KEY `idx_building_status` (`delivery_building`,`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;3. 核心功能实现细节
3.1 高并发抢购控制
当发放限量物资(如消毒液)时,采用Redis分布式锁防止超卖:
public boolean seckill(Long goodsId, Integer num) { String lockKey = "lock:goods:" + goodsId; // 尝试获取锁,设置10秒自动释放 Boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS); if (Boolean.TRUE.equals(locked)) { try { Goods goods = goodsMapper.selectById(goodsId); if (goods.getStock() >= num) { goods.setStock(goods.getStock() - num); return goodsMapper.updateById(goods) > 0; } } finally { redisTemplate.delete(lockKey); } } return false; }3.2 智能配送路线生成
基于Dijkstra算法实现楼栋最优路径计算:
# 后端Python服务提供算法支持(实际项目用Java实现) def calculate_route(buildings): graph = { 'gate': {'1栋': 2, '3栋': 4}, '1栋': {'2栋': 3, 'gate': 2}, '2栋': {'1栋': 3, '3栋': 1}, '3栋': {'gate': 4, '2栋': 1} } # 简化的Dijkstra实现 shortest_paths = {building: float('inf') for building in buildings} shortest_paths['gate'] = 0 visited = set() while buildings: current = min(buildings, key=lambda b: shortest_paths[b]) buildings.remove(current) visited.add(current) for neighbor, distance in graph.get(current, {}).items(): if neighbor in visited: continue if shortest_paths[current] + distance < shortest_paths[neighbor]: shortest_paths[neighbor] = shortest_paths[current] + distance return sorted(shortest_paths.items(), key=lambda x: x[1])4. 部署实战与优化
4.1 服务器最低配置
我们在2核4G的腾讯云轻量服务器上平稳运行(日均PV 3000+):
| 服务 | 配置要求 | 优化建议 |
|---|---|---|
| Nginx | 1核1G | 开启gzip压缩静态资源 |
| MySQL | 2核2G | 调整innodb_buffer_pool_size为1.5G |
| Redis | 单进程无需专用资源 | 禁用持久化提升性能 |
| Java服务 | 2核3G | Xmx设置为2G避免OOM |
4.2 前端部署踩坑记录
路由History模式问题:
location / { try_files $uri $uri/ /index.html; }必须添加此配置,否则刷新页面会导致404
跨域解决方案:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST") .allowCredentials(true) .maxAge(3600); } }生产环境建议配置具体域名而非通配符
5. 典型问题排查指南
5.1 订单状态不同步
现象:前端显示"待配送"但后台已标记"完成"
排查步骤:
- 检查WebSocket连接状态(Chrome开发者工具→Network→WS)
- 验证后端事件推送逻辑:
@Transactional public void completeOrder(Long orderId) { orderMapper.updateStatus(orderId, 2); simpMessagingTemplate.convertAndSend( "/topic/order/" + orderId, new OrderEvent(orderId, 2)); } - 查看Nginx是否断开长连接:
proxy_read_timeout 300s; proxy_connect_timeout 75s;
5.2 图片上传失败
错误信息:413 Request Entity Too Large
解决方案:
- Nginx调整上传限制:
client_max_body_size 20M; - SpringBoot添加配置:
spring.servlet.multipart.max-file-size=20MB spring.servlet.multipart.max-request-size=20MB
6. 项目演进建议
- 物资智能推荐:基于历史订单数据实现关联推荐(啤酒+花生米组合)
- 语音操作支持:为老年居民增加语音下单功能
- 离线模式:利用Service Worker实现断网状态下的订单暂存
这套系统在疫情结束后,经过简单改造即可用于物业费缴纳、报修等常规社区服务。所有源码已去除敏感信息后开源,包含完整的Docker Compose部署文件,获取方式见GitHub仓库README。在实际部署时,记得根据小区楼栋分布修改config/map-data.json中的路径权重数据。