
1. 项目概述与背景疫情管理系统作为现代公共卫生应急体系的重要组成部分其技术实现方案一直备受开发者关注。我最近完成了一个基于SpringBootVue的前后端分离架构的疫情管理系统开发这套方案在实际运行中表现出良好的稳定性和扩展性。与传统的单体架构相比前后端分离设计让我们的团队能够并行开发前端工程师可以专注于数据可视化交互而后端团队则全力保障数据服务的可靠性。这个系统最核心的价值在于解决了三个实际问题一是通过自动化数据采集减少了人工录入错误二是利用实时数据看板提升了决策效率三是采用细粒度权限控制确保了数据安全。特别是在数据可视化方面我们使用ECharts实现的疫情热力图和趋势曲线帮助管理人员快速掌握区域风险分布。2. 技术架构设计2.1 整体架构设计我们采用经典的三层架构模式将系统划分为表现层、业务逻辑层和数据访问层。前端使用Vue CLI搭建的SPA应用通过axios与后端通信后端采用SpringBoot提供的RESTful API服务数据库层选用MySQL 8.0利用其窗口函数特性优化了统计分析查询。这种架构的优势在于前后端完全解耦接口定义好后可并行开发前端资源独立部署减轻服务器压力后端服务无状态方便横向扩展清晰的职责划分降低维护成本2.2 技术栈选型分析后端技术栈SpringBoot 2.7.x简化配置内置Tomcat快速启动MyBatis-Plus 3.5.x增强的ORM框架减少SQL编写Hutool 5.8.x工具类库处理日期、加密等常见操作JWT 0.9.x无状态认证方案替代传统Session前端技术栈Vue 3.x组合式API开发体验更好Element PlusUI组件库加速界面开发ECharts 5.3.x专业的数据可视化库Vue Router 4.x前端路由管理Pinia 2.x状态管理替代Vuex技术选型心得MyBatis-Plus相比JPA更适合复杂查询场景其Wrapper条件构造器可以动态拼接SQL。前端放弃Vue2选择Vue3是因为Composition API更利于逻辑复用而且生态已经成熟。3. 数据库设计与实现3.1 核心表结构设计系统主要包含以下三张核心表每张表都遵循第三范式设计疫情动态表epidemic_dynamicCREATE TABLE epidemic_dynamic ( dynamic_id bigint NOT NULL AUTO_INCREMENT COMMENT 动态ID, region_code varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT 地区编码, confirmed int DEFAULT 0 COMMENT 累计确诊, new_cases int DEFAULT 0 COMMENT 新增病例, cured_cases int DEFAULT 0 COMMENT 治愈人数, death_cases int DEFAULT 0 COMMENT 死亡人数, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (dynamic_id), KEY idx_region (region_code), KEY idx_time (update_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin;用户表sys_userCREATE TABLE sys_user ( user_id bigint NOT NULL AUTO_INCREMENT COMMENT 用户ID, username varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT 用户名, password_hash varchar(100) COLLATE utf8mb4_bin NOT NULL COMMENT 加密密码, salt varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT 加密盐值, role_type enum(ADMIN,OPERATOR,VIEWER) COLLATE utf8mb4_bin NOT NULL COMMENT 角色类型, last_login datetime DEFAULT NULL COMMENT 最后登录时间, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (user_id), UNIQUE KEY idx_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin;地区表region_infoCREATE TABLE region_info ( region_code varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT 行政区划码, region_name varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT 地区名称, parent_code varchar(20) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 上级编码, population int DEFAULT NULL COMMENT 人口数量, risk_level enum(HIGH,MEDIUM,LOW) COLLATE utf8mb4_bin DEFAULT LOW COMMENT 风险等级, longitude decimal(10,6) DEFAULT NULL COMMENT 经度, latitude decimal(10,6) DEFAULT NULL COMMENT 纬度, PRIMARY KEY (region_code), KEY idx_parent (parent_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin;3.2 数据库优化实践索引策略在查询频繁的字段上建立组合索引如地区编码时间的联合查询字段类型精确选择字段类型如经度纬度使用DECIMAL(10,6)保证精度分表考虑疫情数据按月份分表通过MyBatis动态表名插件实现缓存层热点数据使用Redis缓存减少数据库压力4. 后端核心实现4.1 SpringBoot应用配置采用多环境配置方案通过application-{profile}.yml区分不同环境# application-dev.yml spring: datasource: url: jdbc:mysql://localhost:3306/epidemic_dev?useSSLfalse username: dev_user password: Dev1234 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 password: database: 1 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境显示SQL关键依赖配置dependencies !-- SpringBoot Starter -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- 数据库相关 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- 工具类 -- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.16/version /dependency /dependencies4.2 MyBatis-Plus高级应用使用LambdaQueryWrapper构建类型安全的查询条件public PageDynamicVO queryDynamic(DynamicQueryDTO dto) { return dynamicMapper.selectPage( new Page(dto.getPageNum(), dto.getPageSize()), new LambdaQueryWrapperEpidemicDynamic() .eq(StringUtils.isNotBlank(dto.getRegionCode()), EpidemicDynamic::getRegionCode, dto.getRegionCode()) .ge(dto.getStartTime() ! null, EpidemicDynamic::getUpdateTime, dto.getStartTime()) .le(dto.getEndTime() ! null, EpidemicDynamic::getUpdateTime, dto.getEndTime()) .orderByDesc(EpidemicDynamic::getUpdateTime) ); }自定义SQL通过Select注解实现复杂统计Select(SELECT ri.region_name, SUM(ed.new_cases) as total_cases FROM epidemic_dynamic ed JOIN region_info ri ON ed.region_code ri.region_code WHERE ed.update_time BETWEEN #{start} AND #{end} GROUP BY ed.region_code ORDER BY total_cases DESC LIMIT 10) ListRegionStatisticVO getTop10Regions(Param(start) Date start, Param(end) Date end);4.3 权限控制实现基于Spring Security JWT的认证方案Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/api/auth/**).permitAll() .antMatchers(/api/admin/**).hasRole(ADMIN) .antMatchers(/api/operator/**).hasAnyRole(ADMIN, OPERATOR) .anyRequest().authenticated() .and() .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } }自定义注解实现方法级权限控制Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) PreAuthorize(hasRole(ADMIN) || (hasRole(OPERATOR) permissionService.checkRegionAccess(authentication, #regionCode))) public interface DynamicAccessControl { }5. 前端核心实现5.1 Vue3组合式API实践疫情数据看板组件采用Composition API编写script setup import { ref, onMounted, computed } from vue import { useDynamicStore } from /stores/dynamic const store useDynamicStore() const loading ref(false) const timeRange ref([new Date(), new Date()]) const fetchData async () { loading.value true try { await store.fetchRegionData({ start: timeRange.value[0], end: timeRange.value[1] }) } finally { loading.value false } } const topRegions computed(() { return store.regionData .slice() .sort((a, b) b.totalCases - a.totalCases) .slice(0, 5) }) onMounted(fetchData) /script5.2 ECharts可视化实现疫情热力图配置示例const initHeatMap () { const chart echarts.init(mapRef.value) const option { tooltip: { formatter: params { return ${params.name}br/新增: ${params.data.value[2]} } }, visualMap: { min: 0, max: 100, calculable: true, inRange: { color: [#50a3ba, #eac736, #d94e5d] } }, series: [{ name: 疫情热力图, type: heatmap, coordinateSystem: geo, data: heatData.value, pointSize: 10, blurSize: 15 }] } chart.setOption(option) return chart }5.3 前端性能优化路由懒加载拆分代码按需加载const routes [ { path: /dashboard, component: () import(/views/Dashboard.vue) } ]API请求缓存使用axios拦截器实现const cache new Map() axios.interceptors.request.use(config { if (config.method get config.cache) { const key generateKey(config) if (cache.has(key)) { return Promise.reject({ cached: true, data: cache.get(key) }) } } return config }) axios.interceptors.response.use(response { if (response.config.cache) { const key generateKey(response.config) cache.set(key, response.data) } return response }, error { if (error.cached) { return Promise.resolve(error) } return Promise.reject(error) })6. 系统部署方案6.1 生产环境部署架构采用Docker Compose编排服务version: 3.8 services: backend: image: epidemic-backend:1.0.0 build: ./backend ports: - 8080:8080 environment: - SPRING_PROFILES_ACTIVEprod depends_on: - redis - mysql frontend: image: nginx:1.21-alpine ports: - 80:80 volumes: - ./frontend/dist:/usr/share/nginx/html - ./nginx.conf:/etc/nginx/conf.d/default.conf mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} MYSQL_DATABASE: epidemic MYSQL_USER: ${DB_USER} MYSQL_PASSWORD: ${DB_PASSWORD} volumes: - mysql_data:/var/lib/mysql ports: - 3306:3306 redis: image: redis:6-alpine ports: - 6379:6379 volumes: - redis_data:/data volumes: mysql_data: redis_data:6.2 Nginx配置优化前端Nginx配置示例server { listen 80; server_name epidemic.example.com; gzip on; gzip_types text/plain text/css application/json application/javascript text/xml; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api { proxy_pass http://backend:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }6.3 CI/CD流程GitLab CI示例配置stages: - build - test - deploy variables: DOCKER_IMAGE: registry.example.com/epidemic backend-build: stage: build script: - cd backend - mvn clean package -DskipTests - docker build -t $DOCKER_API_IMAGE:$CI_COMMIT_SHA . only: - master frontend-build: stage: build script: - cd frontend - npm install - npm run build - docker build -t $DOCKER_FRONTEND_IMAGE:$CI_COMMIT_SHA . only: - master deploy-prod: stage: deploy script: - scp docker-compose.yml deployserver:/opt/epidemic - ssh deployserver cd /opt/epidemic docker-compose pull docker-compose up -d when: manual only: - master7. 常见问题与解决方案7.1 跨域问题处理SpringBoot后端配置CORSConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(https://your-domain.com) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }7.2 数据一致性保障使用Spring事务管理Service RequiredArgsConstructor public class DynamicServiceImpl implements DynamicService { private final DynamicMapper dynamicMapper; private final RegionMapper regionMapper; Transactional(rollbackFor Exception.class) Override public void updateDynamic(DynamicUpdateDTO dto) { // 更新动态数据 dynamicMapper.updateById(convertToEntity(dto)); // 同步更新地区风险等级 regionMapper.updateRiskLevel( dto.getRegionCode(), calculateRiskLevel(dto.getNewCases()) ); } }7.3 性能监控方案集成SpringBoot Actuatormanagement: endpoints: web: exposure: include: health,info,metrics,prometheus metrics: export: prometheus: enabled: true tags: application: epidemic-system配置Grafana监控看板使用Micrometer收集JVM指标Prometheus定时抓取数据Grafana配置自定义监控面板8. 项目扩展方向8.1 微信小程序集成通过uni-app框架复用现有Vue代码// main.js import { createSSRApp } from vue import App from ./App.vue export function createApp() { const app createSSRApp(App) return { app } }8.2 大数据分析扩展使用Flink实时处理疫情数据public class CaseCountJob { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); DataStreamEpidemicEvent events env .addSource(new KafkaSource()) .keyBy(Event::getRegionCode) .window(TumblingEventTimeWindows.of(Time.hours(1))) .process(new CaseCounter()); events.addSink(new AlertSink()); env.execute(Real-time Case Analysis); } }8.3 微服务改造将系统拆分为三个微服务用户服务处理认证授权数据服务提供疫情数据API分析服务负责统计计算使用Spring Cloud Alibaba组件Nacos服务发现与配置中心Sentinel流量控制与熔断降级Seata分布式事务解决方案这个疫情管理系统从技术选型到最终部署每个环节都经过精心设计和反复验证。在实际开发中最大的收获是认识到良好的架构设计能够显著降低后期维护成本。特别是在权限控制和数据一致性方面前期的投入为后续功能扩展打下了坚实基础。