1. 项目背景与核心价值
疫情常态化管理背景下,社区作为基层防控单元需要高效的信息管理工具。这个基于SpringBoot+Vue的全栈项目,为计算机专业毕业生提供了符合企业级开发标准的实战案例。我在实际开发中发现,这类系统要同时满足三个核心诉求:
- 居民端便捷性:通过Vue构建的响应式前端,确保手机、PC等多设备适配
- 管理端高效性:利用SpringBoot的自动化配置特性快速搭建后台服务
- 数据可视化需求:疫情数据统计需要直观的图表展示
提示:项目采用前后端分离架构,前端Vue CLI 4.x + Element UI,后端SpringBoot 2.7.x + MyBatis-Plus,这种技术组合既能满足毕业设计的技术深度要求,又便于二次开发。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构详解
2.1 前端技术栈选型
Vue 2.x版本的选择基于以下考量:
- 比Vue 3更稳定的周边生态(特别是Element UI组件库)
- 更丰富的中文文档和社区支持
- 对IE11的兼容性更好(部分社区仍在使用老旧设备)
关键配置示例(vue.config.js):
javascript复制module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
},
chainWebpack: config => {
config.plugin('html').tap(args => {
args[0].title = '社区疫情管理系统'
return args
})
}
}
2.2 后端技术栈设计
SpringBoot的starter配置体现了模块化思想:
xml复制<!-- 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 特色功能模块 -->
<dependency> <!-- 疫情数据导出Excel -->
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
<dependency> <!-- 健康码识别 -->
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>5.3.0</version>
</dependency>
3. 数据库设计与优化
3.1 核心表结构
sql复制CREATE TABLE `resident_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(50) NOT NULL COMMENT '居民姓名',
`id_card` varchar(18) NOT NULL COMMENT '身份证号',
`phone` varchar(11) NOT NULL COMMENT '手机号',
`building_num` varchar(10) NOT NULL COMMENT '楼栋号',
`unit_num` varchar(5) NOT NULL COMMENT '单元号',
`room_num` varchar(5) NOT NULL COMMENT '房间号',
`health_code` tinyint(1) DEFAULT 1 COMMENT '健康码状态:1绿码 2黄码 3红码',
`vaccination` tinyint(1) DEFAULT 0 COMMENT '疫苗接种:0未接种 1已接种一针 2已完成接种',
`last_test_time` datetime DEFAULT NULL COMMENT '最近核酸检测时间',
`isolation_status` tinyint(1) DEFAULT 0 COMMENT '隔离状态:0正常 1居家隔离 2集中隔离',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_id_card` (`id_card`),
KEY `idx_location` (`building_num`,`unit_num`,`room_num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='居民基础信息表';
3.2 查询性能优化实践
针对疫情排查场景的高频查询:
sql复制-- 优化前(全表扫描)
SELECT * FROM resident_info WHERE building_num = '5' AND unit_num = '2';
-- 优化后(利用联合索引)
ALTER TABLE resident_info ADD INDEX idx_building_unit (building_num, unit_num);
EXPLAIN SELECT id,name,phone FROM resident_info
WHERE building_num = '5' AND unit_num = '2' AND isolation_status > 0;
4. 典型功能模块实现
4.1 健康码识别对接
采用Tesseract OCR引擎实现健康码截图识别:
java复制@Service
public class HealthCodeServiceImpl implements HealthCodeService {
@Value("${ocr.tessdata.path}")
private String tessDataPath;
public int recognizeHealthCode(MultipartFile imageFile) {
try {
ITesseract instance = new Tesseract();
instance.setDatapath(tessDataPath);
instance.setLanguage("chi_sim+eng"); // 中英文混合识别
BufferedImage image = ImageIO.read(imageFile.getInputStream());
String result = instance.doOCR(image);
if(result.contains("绿色")) return 1;
if(result.contains("黄色")) return 2;
if(result.contains("红色")) return 3;
} catch (Exception e) {
log.error("健康码识别失败", e);
}
return 1; // 默认返回绿码
}
}
4.2 疫情数据可视化
使用ECharts实现的三层嵌套饼图(社区-楼栋-单元感染率分布):
vue复制<template>
<div ref="chart" style="width: 600px; height: 400px;"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
mounted() {
this.initChart();
},
methods: {
async initChart() {
const res = await this.$http.get('/api/epidemic/stats');
const chart = echarts.init(this.$refs.chart);
const option = {
tooltip: { trigger: 'item' },
series: [{
type: 'pie',
radius: ['30%', '60%'],
data: res.data.communityLevel,
label: { formatter: '{b}: {c} ({d}%)' }
},{
type: 'pie',
radius: ['0%', '25%'],
data: res.data.total,
label: { show: false }
}]
};
chart.setOption(option);
window.addEventListener('resize', chart.resize);
}
}
}
</script>
5. 毕业设计专项优化建议
5.1 答辩演示技巧
- 数据准备:预先在数据库插入2000+条模拟数据,包含各种疫情状态组合
- 演示路径:
- 居民端:注册→健康上报→查看通知
- 管理端:数据导入→异常筛查→生成报表
- 亮点展示:
- 同时打开Chrome开发者工具展示API请求
- 演示Excel导出时对比10万条数据的处理速度
5.2 代码规范要点
在application.yml中配置MyBatis-Plus性能分析插件:
yaml复制mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除字段
logic-delete-value: 1
logic-not-delete-value: 0
6. 项目部署与运维
6.1 多环境配置方案
使用Spring Profiles实现开发/生产环境切换:
java复制@Configuration
public class EnvConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/epidemic_dev");
ds.setUsername("dev_user");
ds.setPassword("Dev@1234");
return ds;
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://prod-db:3306/epidemic_prod?useSSL=false");
ds.setUsername("prod_user");
ds.setPassword("Pr0d@2023");
ds.setConnectionTimeout(30000);
return ds;
}
}
6.2 前端性能优化
通过webpack分包策略减小vendor.js体积:
javascript复制// vue.config.js
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
echarts: {
name: 'chunk-echarts',
test: /[\\/]node_modules[\\/]echarts[\\/]/,
priority: 20
},
elementUI: {
name: 'chunk-elementUI',
test: /[\\/]node_modules[\\/]element-ui[\\/]/,
priority: 10
}
}
}
}
}
7. 扩展功能建议
7.1 微信小程序集成
通过uni-app快速构建小程序端:
javascript复制// pages/report/report.vue
export default {
methods: {
submitForm() {
uni.chooseImage({
count: 1,
success: (res) => {
uni.uploadFile({
url: 'https://yourdomain.com/api/health/report',
filePath: res.tempFilePaths[0],
name: 'healthCode',
success: () => uni.showToast({ title: '上报成功' })
});
}
});
}
}
}
7.2 智能预警模块
基于规则引擎Drools实现自动预警:
drl复制rule "HighRiskBuildingAlert"
when
$r : ResidentInfo(isolationStatus == 2,
$building : buildingNum)
$count : Number(intValue > 5) from accumulate(
ResidentInfo(buildingNum == $building,
isolationStatus == 2),
count(1))
then
System.out.println("预警:楼栋"+$building+"存在"+$count+"例集中隔离人员");
insert(new AlertMessage("high_risk_building", $building));
end
8. 常见问题解决方案
8.1 跨域问题深度处理
不仅要在SpringBoot中配置CORS,还需要处理OPTIONS预检请求:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("Authorization")
.maxAge(3600);
}
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
8.2 大文件上传优化
采用分片上传+断点续传方案:
vue复制<template>
<input type="file" @change="handleUpload" />
</template>
<script>
export default {
methods: {
async handleUpload(e) {
const file = e.target.files[0];
const chunkSize = 2 * 1024 * 1024; // 2MB
const chunks = Math.ceil(file.size / chunkSize);
for(let i=0; i<chunks; i++) {
const chunk = file.slice(i*chunkSize, (i+1)*chunkSize);
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', i);
formData.append('totalChunks', chunks);
formData.append('fileId', md5(file.name + file.size));
await this.$http.post('/api/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
}
}
}
}
</script>
9. 安全防护实践
9.1 XSS防御方案
前端使用DOMPurify过滤,后端统一处理:
java复制@ControllerAdvice
public class XssAdvice implements ResponseBodyAdvice<Object> {
private final HtmlFilter htmlFilter;
public XssAdvice() {
htmlFilter = new HtmlFilter();
htmlFilter.setRemoveMsg("不允许的HTML标签");
}
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if(body instanceof String) {
return htmlFilter.filter((String)body);
}
return body;
}
}
9.2 接口幂等性设计
使用Redis实现token防重:
java复制@RestController
@RequestMapping("/api")
public class ReportController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@PostMapping("/report")
public Result reportHealth(@RequestHeader("X-Token") String token,
@RequestBody ReportDTO dto) {
// 检查token是否已使用
Boolean used = redisTemplate.opsForValue()
.setIfAbsent("report:token:"+token, "1", 5, TimeUnit.MINUTES);
if(Boolean.FALSE.equals(used)) {
throw new BusinessException("请勿重复提交");
}
// 正常业务处理
return healthService.processReport(dto);
}
}
10. 性能监控与调优
10.1 SpringBoot Actuator集成
配置关键指标暴露:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
endpoint:
health:
show-details: always
prometheus:
enabled: true
10.2 慢SQL监控方案
使用Druid连接池的监控功能:
java复制@Configuration
public class DruidConfig {
@Bean
public ServletRegistrationBean<StatViewServlet> druidServlet() {
ServletRegistrationBean<StatViewServlet> bean =
new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
bean.addInitParameter("loginUsername", "admin");
bean.addInitParameter("loginPassword", "admin123");
return bean;
}
@Bean
public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
FilterRegistrationBean<WebStatFilter> bean =
new FilterRegistrationBean<>(new WebStatFilter());
bean.addUrlPatterns("/*");
bean.addInitParameter("exclusions", "*.js,*.css,/druid/*");
return bean;
}
}
