1. 项目背景与需求分析
最近几年,我参与了多个城市智慧社区项目的开发工作,发现垃圾分类管理一直是社区治理中的痛点。传统的垃圾分类主要依靠人工督导和纸质记录,存在数据不透明、分类准确率低、管理效率低下等问题。特别是在早晚高峰时段,垃圾投放点经常出现排队混乱的情况。
这个项目源于某市城管局的实际需求,他们希望建立一套数字化的垃圾分类管理系统,实现以下核心目标:
- 居民可以通过手机快速查询垃圾分类标准
- 自动记录每次垃圾投放的类别、重量等数据
- 社区工作人员能实时监控各投放点情况
- 管理部门可以生成各类统计报表
关键需求点:系统需要支持单日10万+的投放记录处理,响应时间控制在2秒内,同时要保证在高并发情况下的系统稳定性。
2. 技术选型与架构设计
2.1 技术栈选择考量
经过多轮技术评估,我们最终确定了SpringBoot+Vue的前后端分离架构,主要基于以下考虑:
后端技术栈:
- SpringBoot 2.7.x:简化配置,内置Tomcat,快速构建RESTful API
- MyBatis-Plus 3.5.x:增强的ORM框架,减少样板代码
- MySQL 8.0:关系型数据库,支持事务和复杂查询
- Redis 6.x:缓存高频访问数据,如分类规则
前端技术栈:
- Vue 3.x:响应式框架,组件化开发
- Element Plus:UI组件库,快速构建管理后台
- ECharts 5.x:数据可视化,生成统计图表
- Axios:处理HTTP请求,与后端交互
2.2 系统架构设计
系统采用经典的三层架构,具体分层如下:
code复制┌───────────────────────────────────────┐
│ 客户端层 │
│ ┌───────────┐ ┌─────────────┐ │
│ │ Web端 │ │ 移动端H5 │ │
│ └───────────┘ └─────────────┘ │
└───────────────────┬───────────────────┘
│ HTTP/HTTPS
┌───────────────────▼───────────────────┐
│ 表现层 │
│ ┌─────────────────────────────────┐ │
│ │ SpringBoot REST API │ │
│ └─────────────────────────────────┘ │
└───────────────────┬───────────────────┘
│
┌───────────────────▼───────────────────┐
│ 业务逻辑层 │
│ ┌───────────┐ ┌─────────────┐ │
│ │ 服务模块 │ │ 定时任务模块 │ │
│ └───────────┘ └─────────────┘ │
└───────────────────┬───────────────────┘
│
┌───────────────────▼───────────────────┐
│ 数据访问层 │
│ ┌───────────┐ ┌─────────────┐ │
│ │ MyBatis │ │ Redis │ │
│ └───────────┘ └─────────────┘ │
└───────────────────┬───────────────────┘
│
┌───────────────────▼───────────────────┐
│ 数据存储层 │
│ ┌─────────────────────────────────┐ │
│ │ MySQL │ │
│ └─────────────────────────────────┘ │
└───────────────────────────────────────┘
3. 核心功能实现
3.1 用户权限管理模块
系统采用RBAC(基于角色的访问控制)模型,设计了三种角色:
- 管理员:系统配置、用户管理
- 工作人员:投放点管理、异常处理
- 居民:垃圾分类查询、投放记录
关键代码实现:
java复制// 基于Spring Security的权限配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/staff/**").hasAnyRole("STAFF", "ADMIN")
.antMatchers("/user/**").authenticated()
.anyRequest().permitAll()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.csrf().disable();
}
}
权限表设计优化:
在实际使用中发现,简单的角色划分不能满足复杂场景,我们增加了权限组的概念:
sql复制CREATE TABLE `sys_permission` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '权限名称',
`code` varchar(50) NOT NULL COMMENT '权限编码',
`type` tinyint NOT NULL COMMENT '1-菜单 2-按钮 3-API',
`parent_id` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 垃圾分类识别功能
系统提供两种识别方式:
- 文本搜索:基于Elasticsearch构建的垃圾名称搜索引擎
- 图像识别:集成第三方AI服务(实际项目中用的是阿里云视觉智能平台)
文本搜索实现:
java复制@Service
public class GarbageSearchServiceImpl implements GarbageSearchService {
@Autowired
private ElasticsearchRestTemplate elasticsearchTemplate;
public List<GarbageDTO> search(String keyword) {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(keyword, "name", "keywords"))
.build();
SearchHits<GarbageDocument> hits = elasticsearchTemplate.search(query, GarbageDocument.class);
return hits.stream()
.map(hit -> convertToDTO(hit.getContent()))
.collect(Collectors.toList());
}
}
性能优化点:对高频搜索词建立缓存,使用Redis存储最近24小时的热词结果,缓存命中率可达65%以上。
3.3 垃圾投放记录处理
投放记录模块面临的主要挑战是高并发写入问题。我们采用以下解决方案:
- 写入优化:
java复制@Transactional
public void addRecord(GarbageRecord record) {
// 1. 写入主表
recordMapper.insert(record);
// 2. 更新统计信息(异步)
statQueue.add(new StatTask(record.getType(), record.getWeight()));
// 3. 记录操作日志(异步)
logQueue.add(new LogTask("ADD_RECORD", record));
}
- 批量插入优化:
sql复制INSERT INTO garbage_record
(user_id, type, weight, location, create_time)
VALUES
(?,?,?,?,NOW()),
(?,?,?,?,NOW()),
...
- 使用消息队列削峰:
yaml复制# application.yml配置
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: admin
password: 123456
listener:
simple:
prefetch: 50
concurrency: 5
max-concurrency: 20
4. 数据统计与分析
4.1 实时数据看板
使用WebSocket实现实时数据推送:
javascript复制// 前端代码
const socket = new WebSocket('wss://yourdomain.com/ws/stats');
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.updateDashboard(data);
};
后端处理逻辑:
java复制@RestController
@RequestMapping("/ws")
public class WebSocketController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Scheduled(fixedRate = 5000)
public void pushData() {
StatsDTO stats = statsService.getRealtimeStats();
messagingTemplate.convertAndSend("/topic/stats", stats);
}
}
4.2 数据分析报表
我们实现了以下几种分析模型:
- 时段分析:各时间段投放量热力图
- 分类准确率:通过人工复核数据计算
- 趋势预测:基于历史数据的ARIMA模型
python复制# Python数据分析示例(实际项目中使用Java实现)
import pandas as pd
from statsmodels.tsa.arima.model import ARIMA
df = pd.read_csv('garbage_data.csv')
model = ARIMA(df['weight'], order=(7,0,1))
model_fit = model.fit()
forecast = model_fit.forecast(steps=7)
5. 部署与性能优化
5.1 生产环境部署方案
我们采用Docker Compose进行容器化部署:
dockerfile复制version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
MYSQL_DATABASE: garbage
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:6.2
ports:
- "6379:6379"
volumes:
- redis_data:/data
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
5.2 性能优化实践
-
数据库优化:
- 为garbage_record表添加分区(按月份分区)
- 建立复合索引:
INDEX idx_user_time (user_id, create_time) - 优化慢查询:使用pt-query-digest分析SQL
-
缓存策略:
- 一级缓存:MyBatis本地缓存
- 二级缓存:Redis集群
- 缓存失效策略:LFU(最近最不常用)
-
JVM调优:
bash复制# 启动参数
java -jar -Xms2g -Xmx2g -XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:ParallelGCThreads=4 \
-XX:ConcGCThreads=2 \
your-application.jar
6. 踩坑经验分享
6.1 微信小程序兼容性问题
在对接微信小程序时遇到的典型问题:
-
iOS日期格式问题:
javascript复制// 错误写法 new Date('2023-05-01') // 正确写法 new Date('2023/05/01') -
图片上传限制:
- 需要将图片压缩到1MB以内
- 使用canvas进行客户端压缩
6.2 高并发场景下的数据一致性问题
在促销活动期间,出现了积分统计不准确的问题。解决方案:
- 使用分布式锁:
java复制public void addPoints(Long userId, int points) {
String lockKey = "point_lock:" + userId;
try {
boolean locked = redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS);
if (locked) {
// 业务逻辑
}
} finally {
redisLock.unlock(lockKey);
}
}
- 最终一致性方案:
- 先记录操作日志
- 通过定时任务补偿
6.3 缓存穿透解决方案
针对恶意攻击导致的缓存穿透:
- 布隆过滤器实现:
java复制public class BloomFilter {
private final BitSet bitset;
private final int size;
public boolean mightContain(String key) {
int hash = hash(key);
return bitset.get(hash % size);
}
}
- 空值缓存:
java复制public Garbage getById(Long id) {
String cacheKey = "garbage:" + id;
Garbage garbage = cache.get(cacheKey);
if (garbage == NULL_OBJECT) {
return null;
}
if (garbage == null) {
garbage = dao.getById(id);
cache.put(cacheKey, garbage != null ? garbage : NULL_OBJECT);
}
return garbage;
}
7. 项目扩展方向
在实际运行过程中,我们发现系统还可以在以下方面进行扩展:
-
智能硬件对接:
- 通过IoT技术连接智能垃圾桶
- 自动称重和拍照识别
-
信用积分体系:
- 建立居民垃圾分类信用分
- 与社区其他服务挂钩
-
移动端扩展:
- 开发Flutter跨平台应用
- 增加AR垃圾分类指引
-
大数据分析:
- 使用Flink进行实时数据处理
- 建立垃圾产生预测模型
这个项目从设计到上线历时6个月,期间遇到了各种技术挑战,特别是高并发场景下的性能问题。通过这个项目,我深刻体会到系统架构设计的重要性,以及监控和日志对于排查线上问题的关键作用。对于想学习SpringBoot+Vue全栈开发的同学,这个项目涵盖了大部分企业级应用的核心技术点,是非常好的练手项目。