在线防灾教育系统源码+数据库(高分毕业设计)

在线防灾教育系统:高并发微服务架构下的全民防灾平台

在线防灾教育系统源码+数据库(高分毕业设计),本资源中的源码都是经过本地编译过可运行的,评审分达到98分,资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习毕业设计、期末大作业和课程设计使用需求,如果有需要的话可以放心下载使用。

项目简介

online-disaster-prevention 是一个基于微服务架构的在线防灾教育平台,旨在为公众提供专业的灾害预防知识学习、应急演练和资源管理服务。项目采用前后端分离架构,集成多种高并发技术解决方案,实现了每秒10万+的缓存吞吐能力与毫秒级课程检索效率。系统支持多灾种(地震、火灾、洪水等)的预防教育,通过可视化应急路线规划和模拟演练,帮助个人及单位快速掌握防灾自救技能。

项目地址:https://gitee.com/zhenghuanrong/online-disaster-prevention.git

技术架构

整体架构设计

┌──────────────────────────────────────────────────────────────┐
│                             前端层                             │
│  ┌─────────────┐  ┌────────────────┐  ┌──────────────────┐   │
│  │ Web用户端   │  │ 管理后台(Vue)  │  │ 移动端(H5适配)    │   │
│  └─────────────┘  └────────────────┘  └──────────────────┘   │
└───────────────────────┬──────────────────────────────────────┘
                        │             API网关层(Zuul/Gateway)
┌───────────────────────▼──────────────────────────────────────┐
│                             服务层                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ 课程服务  │  │ 用户服务  │  │ 讲师服务  │  │ 资源服务  │      │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘      │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ 预警服务  │  │ 演练服务  │  │ 统计服务  │  │ 配置服务  │      │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘      │
└───────────────────────┬──────────────────────────────────────┘
                        │            服务治理(Nacos)
┌───────────────────────▼──────────────────────────────────────┐
│                             数据层                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ MySQL    │  │ Redis    │  │ Elasticsearch│  │ MinIO    │      │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘      │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                    │
│  │ RabbitMQ  │  │ Seata    │  │ XXL-JOB  │                    │
│  └──────────┘  └──────────┘  └──────────┘                    │
└──────────────────────────────────────────────────────────────┘

技术栈详情

后端技术

核心框架:Spring Boot 2.7 + Spring Cloud Alibaba
服务治理:Nacos(服务注册与发现、配置管理)
分布式事务:Seata(AT模式)
持久层:MyBatis-Plus + MySQL 8.0
搜索服务:Elasticsearch 7.10(课程全文检索)
缓存:Redis 6.0(热点数据缓存、分布式锁)
文件存储:MinIO(海量资源存储)
消息队列:RabbitMQ(异步任务处理)
任务调度:XXL-JOB(灾害预警定时任务)

前端技术

管理后台:Vue 3 + Element Plus + Vue Router + Vuex
用户端:HTML5 + CSS3 + JavaScript + Axios
数据可视化:ECharts(灾害统计图表)
移动端适配:Tailwind CSS(响应式设计)

核心功能解析

1. 灾害知识学习模块

多灾种课程体系:涵盖地震、火灾、洪水、台风等20+灾种的预防知识
课程分类检索:支持按灾害类型、难度等级、适用人群进行多维筛选
交互式学习体验:视频课程、图文教程、知识点测试相结合
学习进度跟踪:个人学习计划、完成度统计、薄弱点标记

2. 应急演练模拟模块

三维场景演练:基于WebGL的灾害场景模拟(如火灾逃生路线规划)
交互式操作指引:实时反馈演练操作正确性,提供改进建议
演练成绩评估:自动生成演练报告,统计响应时间、操作准确率等指标
多人协同演练:支持团队协作完成复杂灾害场景的应急处理

3. 实时灾害预警模块

多源数据接入:集成气象局、地震局等官方数据源
预警信息推送:通过短信、站内信等多渠道实时通知
区域风险评估:基于地理位置的灾害风险等级可视化展示
应急资源调度:根据预警信息自动推荐附近避难场所、救援物资点

4. 教育资源管理模块

课程内容管理:讲师上传、审核、更新课程资源
多媒体资源存储:支持视频、文档、3D模型等多类型资源管理
资源权限控制:不同灾害类型、用户群体的资源访问权限配置
资源统计分析:热门资源排行、下载量统计、用户反馈收集

5. 系统管理模块

用户与角色管理:学员、讲师、管理员多角色权限控制
操作日志审计:关键操作记录、登录日志追踪、异常行为预警
系统监控告警:微服务状态监控、数据库性能告警、缓存命中率分析
数据可视化大屏:实时展示系统访问量、课程学习率、灾害预警分布

核心代码示例

课程数据模型与服务实现

// 课程实体类
package com.disaster.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.List;

@Data
@TableName("course")
public class Course {
            
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String title;             // 课程标题
    private String description;       // 课程描述
    private Integer categoryId;       // 灾害类型ID
    private Long teacherId;           // 讲师ID
    private String coverImage;        // 封面图片
    private Integer difficulty;       // 难度等级(1-5)
    private Integer status;           // 状态(1:上架 2:下架)
    private Double price;             // 价格(免费为0)
    private Integer studyCount;       // 学习人数
    private Integer favoriteCount;    // 收藏人数
    
    @TableField(exist = false)
    private Teacher teacher;          // 讲师信息
    
    @TableField(exist = false)
    private List<Chapter> chapterList; // 章节列表
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

// 课程服务接口
package com.disaster.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.disaster.entity.Course;
import com.disaster.vo.CourseVO;
import com.disaster.dto.CourseQueryDTO;
import com.disaster.result.PageResult;

import java.util.List;

public interface CourseService extends IService<Course> {
            
    
    /**
     * 课程分页查询
     */
    PageResult<CourseVO> pageQuery(CourseQueryDTO dto);
    
    /**
     * 获取课程详情
     */
    CourseVO getCourseDetail(Long id);
    
    /**
     * 发布课程
     */
    boolean publishCourse(Course course);
    
    /**
     * 同步课程到ES
     */
    boolean syncToES(Long id);
    
    /**
     * 基于用户行为推荐课程
     */
    List<CourseVO> recommendByUserBehavior(Long userId);
}

// 课程服务实现(核心逻辑)
package com.disaster.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.disaster.entity.Course;
import com.disaster.mapper.CourseMapper;
import com.disaster.service.CourseService;
import com.disaster.service.ElasticsearchService;
import com.disaster.service.RedisService;
import com.disaster.service.TeacherService;
import com.disaster.vo.CourseVO;
import com.disaster.dto.CourseQueryDTO;
import com.disaster.result.PageResult;
import com.disaster.utils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService {
            

    @Autowired
    private RedisService redisService;
    
    @Autowired
    private TeacherService teacherService;
    
    @Autowired
    private ElasticsearchService elasticsearchService;

    @Override
    public PageResult<CourseVO> pageQuery(CourseQueryDTO dto) {
            
        Page<Course> page = new Page<>(dto.getPage(), dto.getPageSize());
        LambdaQueryWrapper<Course> wrapper = new LambdaQueryWrapper<>();
        
        // 构建查询条件
        if (dto.getCategoryId() != null) {
            
            wrapper.eq(Course::getCategoryId, dto.getCategoryId());
        }
        if (dto.getDifficulty() != null) {
            
            wrapper.eq(Course::getDifficulty, dto.getDifficulty());
        }
        if (dto.getStatus() != null) {
            
            wrapper.eq(Course::getStatus, dto.getStatus());
        }
        if (dto.getKeyword() != null) {
            
            wrapper.like(Course::getTitle, dto.getKeyword())
                  .or()
                  .like(Course::getDescription, dto.getKeyword());
        }
        
        // 排序:热门课程优先
        wrapper.orderByDesc(Course::getStudyCount)
               .orderByDesc(Course::getCreateTime);
        
        // 分页查询
        this.page(page, wrapper);
        
        // 转换VO并加载关联数据
        List<CourseVO> records = page.getRecords().stream()
            .map(course -> {
            
                CourseVO vo = BeanUtils.copyProperties(course, CourseVO.class);
                vo.setTeacher(teacherService.getTeacherById(course.getTeacherId()));
                return vo;
            })
            .toList();
        
        return new PageResult<>(records, page.getTotal());
    }

    @Override
    public CourseVO getCourseDetail(Long id) {
            
        // 先查Redis缓存
        String cacheKey = "course:detail:" + id;
        CourseVO courseVO = redisService.get(cacheKey, CourseVO.class);
        if (courseVO != null) {
            
            // 缓存命中,更新访问计数(异步处理)
            redisService.increment("course:view:" + id);
            return courseVO;
        }
        
        // 缓存未命中,查数据库
        Course course = this.getById(id);
        if (course == null) {
            
            return null;
        }
        
        // 构建VO并加载关联数据
        courseVO = BeanUtils.copyProperties(course, CourseVO.class);
        courseVO.setTeacher(teacherService.getTeacherById(course.getTeacherId()));
        
        // 缓存课程详情(1小时)
        redisService.set(cacheKey, courseVO, 60, TimeUnit.MINUTES);
        
        // 异步更新课程访问量
        redisService.increment("course:view:" + id);
        return courseVO;
    }

    @Override
    @Transactional
    public boolean publishCourse(Course course) {
            
        // 1. 保存课程基本信息
        boolean result = this.saveOrUpdate(course);
        if (!result) {
            
            throw new RuntimeException("课程发布失败");
        }
        
        // 2. 同步到Elasticsearch
        boolean esResult = elasticsearchService.indexCourse(course);
        if (!esResult) {
            
            throw new RuntimeException("课程索引同步失败");
        }
        
        // 3. 清除相关缓存
        redisService.delPattern("course:detail:" + course.getId());
        redisService.delPattern("course:list:category:" + course.getCategoryId());
        
        return true;
    }

    @Override
    public boolean syncToES(Long id) {
            
        Course course = this.getById(id);
        if (course == null) {
            
            return false;
        }
        return elasticsearchService.indexCourse(course);
    }
}

灾害预警服务核心逻辑

// 预警信息实体类
package com.disaster.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@TableName("disaster_warning")
public class DisasterWarning {
            
    private Long id;
    private String warningType;       // 预警类型(地震/火灾/洪水等)
    private String warningLevel;      // 预警等级(蓝色/黄色/橙色/红色)
    private String affectedArea;      // 影响区域
    private String warningContent;    // 预警内容
    private LocalDateTime startTime;  // 开始时间
    private LocalDateTime endTime;    // 结束时间
    private Integer status;           // 状态(1:生效 2:失效)
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

// 预警服务实现(实时预警处理)
package com.disaster.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.disaster.entity.DisasterWarning;
import com.disaster.mapper.DisasterWarningMapper;
import com.disaster.service.DisasterWarningService;
import com.disaster.service.MessagePushService;
import com.disaster.service.RedisService;
import com.disaster.utils.GeohashUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class DisasterWarningServiceImpl extends ServiceImpl<DisasterWarningMapper, DisasterWarning> 
                                    implements DisasterWarningService {
            

    @Autowired
    private MessagePushService messagePushService;
    
    @Autowired
    private RedisService redisService;
    
    @Autowired
    private GeohashUtils geohashUtils;

    @Override
    @Transactional
    public boolean publishWarning(DisasterWarning warning) {
            
        // 1. 保存预警信息
        boolean result = this.save(warning);
        if (!result) {
            
            throw new RuntimeException("预警发布失败");
        }
        
        // 2. 解析影响区域,存入Redis地理空间索引
        String geohash = geohashUtils.encode(warning.getAffectedArea());
        redisService.geoAdd("disaster:warnings", geohash, warning.getId());
        
        // 3. 推送预警消息(异步处理)
        messagePushService.pushWarning(warning);
        
        return true;
    }

    @Override
    public List<DisasterWarning> getWarningsByLocation(Double latitude, Double longitude) {
            
        // 1. 获取用户位置的Geohash
        String userGeohash = geohashUtils.encode(latitude, longitude);
        
        // 2. 查询附近的预警信息(半径50公里)
        Set<Long> warningIds = redisService.geoRadius("disaster:warnings", 
            latitude, longitude, 50, "km");
        
        if (warningIds.isEmpty()) {
            
            return null;
        }
        
        // 3. 查询预警详情
        LambdaQueryWrapper<DisasterWarning> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(DisasterWarning::getId, warningIds)
               .eq(DisasterWarning::getStatus, 1)  // 只查生效中的预警
               .gt(DisasterWarning::getEndTime, LocalDateTime.now());
        
        return this.list(wrapper);
    }

    /**
     * 定时任务:每天凌晨1点检查过期预警
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void checkExpiredWarnings() {
            
        LambdaQueryWrapper<DisasterWarning> wrapper = new LambdaQueryWrapper<>();
        wrapper.lt(DisasterWarning::getEndTime, LocalDateTime.now())
               .eq(DisasterWarning::getStatus, 1);
        
        List<DisasterWarning> expiredWarnings = this.list(wrapper);
        if (expiredWarnings.isEmpty()) {
            
            return;
        }
        
        // 更新状态为失效
        expiredWarnings.forEach(warning -> {
            
            warning.setStatus(2);
            warning.setUpdateTime(LocalDateTime.now());
        });
        
        this.updateBatchById(expiredWarnings);
        
        // 从Redis中移除过期预警
        List<Long> warningIds = expiredWarnings.stream()
            .map(DisasterWarning::getId)
            .collect(Collectors.toList());
            
        redisService.geoRemove("disaster:warnings", warningIds.toArray());
    }

    @Override
    public Map<String, List<DisasterWarning>> getWarningsByType() {
            
        LambdaQueryWrapper<DisasterWarning> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DisasterWarning::getStatus, 1)
               .gt(DisasterWarning::getEndTime, LocalDateTime.now());
        
        List<DisasterWarning> warnings = this.list(wrapper);
        return warnings.stream()
            .collect(Collectors.groupingBy(DisasterWarning::getWarningType));
    }
}

前端课程列表组件(Vue 3实现)

<template>
  <div class="course-list-container">
    <div class="filter-bar">
      <el-select v-model="filter.categoryId" placeholder="选择灾害类型">
        <el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id" />
      </el-select>
      <el-select v-model="filter.difficulty" placeholder="难度等级">
        <el-option label="入门" :value="1" />
        <el-option label="初级" :value="2" />
        <el-option label="中级" :value="3" />
        <el-option label="高级" :value="4" />
        <el-option label="专家" :value="5" />
      </el-select>
      <el-input v-model="filter.keyword" placeholder="搜索课程" class="search-input" @keyup.enter="handleSearch" />
      <el-button type="primary" @click="handleSearch">搜索</el-button>
    </div>
    
    <div class="course-grid">
      <div v-for="course in courseList" :key="course.id" class="course-card" @click="goToDetail(course.id)">
        <div class="course-cover">
          <img :src="course.coverImage">{ course.title }}</div>
          <div class="course-meta">
            <span class="teacher-name">{
           { course.teacher.name }}</span>
            <span class="study-count">学习: {
           { course.studyCount }}</span>
          </div>
          <div class="course-desc">{
           { course.description }}</div>
          <div class="course-footer">
            <span class="difficulty-tag" :class="`difficulty-${course.difficulty}`">{
           { getDifficultyText(course.difficulty) }}</span>
            <span class="price-tag" v-if="course.price > 0">¥{
           { course.price }}</span>
            <span class="free-tag" v-else>免费</span>
          </div>
        </div>
      </div>
    </div>
    
    <div class="pagination-container">
      <el-pagination
        v-model:current-page="currentPage"
        v-model:page-size="pageSize"
        :page-sizes="[10, 20, 50, 100]"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>

<script>
import { ref, onMounted, reactive } from 'vue';
import { getCourseList, getCategoryList } from '@/api/course';
import { ElMessage } from 'element-plus';

export default {
  setup() {
    const currentPage = ref(1);
    const pageSize = ref(10);
    const total = ref(0);
    const courseList = ref([]);
    const categoryList = ref([]);
    
    // 过滤条件
    const filter = reactive({
      categoryId: null,
      difficulty: null,
      keyword: ''
    });
    
    // 加载课程分类
    const loadCategories = () => {
      getCategoryList().then(res => {
        categoryList.value = res.data;
      }).catch(error => {
        console.error('获取分类失败', error);
        ElMessage.error('获取分类失败');
      });
    };
    
    // 加载课程列表
    const loadCourseList = () => {
      getCourseList({
        page: currentPage.value,
        pageSize: pageSize.value,
        categoryId: filter.categoryId,
        difficulty: filter.difficulty,
        keyword: filter.keyword
      }).then(res => {
        courseList.value = res.records || [];
        total.value = res.total || 0;
      }).catch(error => {
        console.error('获取课程列表失败', error);
        ElMessage.error('获取课程列表失败');
      });
    };
    
    // 难度等级文本转换
    const getDifficultyText = (level) => {
      const textMap = {
        1: '入门',
        2: '初级',
        3: '中级',
        4: '高级',
        5: '专家'
      };
      return textMap[level] || '未知';
    };
    
    // 搜索课程
    const handleSearch = () => {
      currentPage.value = 1;
      loadCourseList();
    };
    
    // 分页大小变更
    const handleSizeChange = (val) => {
      pageSize.value = val;
      loadCourseList();
    };
    
    // 页码变更
    const handleCurrentChange = (val) => {
      currentPage.value = val;
      loadCourseList();
    };
    
    // 进入课程详情
    const goToDetail = (id) => {
      var test = `/course/detail?id=${id}`;
    };
    
    onMounted(() => {
      loadCategories();
      loadCourseList();
    });
    
    return {
      currentPage,
      pageSize,
      total,
      courseList,
      categoryList,
      filter,
      getDifficultyText,
      handleSearch,
      handleSizeChange,
      handleCurrentChange,
      goToDetail
    };
  }
};
</script>

<style scoped>
.course-list-container {
  padding: 20px;
}

.filter-bar {
  display: flex;
  gap: 15px;
  margin-bottom: 20px;
}

.search-input {
  width: 200px;
}

.course-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.course-card {
  border: 1px solid #ebeef5;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  transition: all 0.3s;
}

.course-card:hover {
  box-shadow: 0 10px 20px 0 rgba(0, 0, 0, 0.12);
  transform: translateY(-5px);
}

.course-cover img {
  width: 100%;
  height: 180px;
  object-fit: cover;
}

.course-info {
  padding: 15px;
}

.course-title {
  font-size: 16px;
  font-weight: bold;
  margin-bottom: 10px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.course-meta {
  display: flex;
  align-items: center;
  color: #909399;
  font-size: 14px;
  margin-bottom: 10px;
  gap: 15px;
}

.course-desc {
  color: #606266;
  font-size: 14px;
  margin-bottom: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

.course-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.difficulty-tag {
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
}

.difficulty-1 { background-color: #e6f7ff; color: #3185fc; }
.difficulty-2 { background-color: #ecf5ff; color: #2979ff; }
.difficulty-3 { background-color: #e3f2fd; color: #1976d2; }
.difficulty-4 { background-color: #bbdefb; color: #0d47a1; }
.difficulty-5 { background-color: #90caf9; color: #01579b; }

.price-tag {
  color: #e64340;
  font-weight: bold;
}

.free-tag {
  color: #67c23a;
  font-weight: bold;
}

.pagination-container {
  margin-top: 30px;
  text-align: center;
}
</style>

系统部署与高可用架构

部署环境要求

后端环境

JDK 1.8+
MySQL 8.0+(建议主从架构)
Redis 6.0+(建议哨兵模式)
Elasticsearch 7.10+(建议3节点集群)
MinIO(分布式对象存储)
Docker & Docker Compose(推荐部署方式)

前端环境

Node.js 14+
npm/yarn
微信开发者工具(若需小程序端)

高可用部署架构

┌──────────────────────────────────────────────────────────────┐
│                         负载均衡层                             │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐              │
│  │  Nginx     │  │ HAProxy    │  │  F5        │              │
│  └────────────┘  └────────────┘  └────────────┘              │
└───────────────────────────┬─────────────────────────────────┘
                            │            域名解析(DNSPod)
┌───────────────────────────▼─────────────────────────────────┐
│                         网关层(Gateway)                        │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐              │
│  │ 节点1      │  │ 节点2      │  │ 节点3      │              │
│  └────────────┘  └────────────┘  └────────────┘              │
└───────────────────────────┬─────────────────────────────────┘
                            │            服务注册(Nacos)
┌───────────────────────────▼─────────────────────────────────┐
│                         微服务层                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ 课程服务  │  │ 用户服务  │  │ 预警服务  │  │ 演练服务  │      │
│  (3节点)     │  (3节点)     │  (3节点)     │  (3节点)     │      │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘      │
└───────────────────────────┬─────────────────────────────────┘
                            │            配置中心(Nacos)
┌───────────────────────────▼─────────────────────────────────┐
│                         数据层                                │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ MySQL    │  │ Redis    │  │ ES       │  │ MinIO    │      │
│  (主从+MHA)  │  (哨兵模式) │  (3节点)    │  (分布式)   │      │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘      │
└──────────────────────────────────────────────────────────────┘

快速部署步骤(Docker Compose)

克隆项目代码

git clone https://gitee.com/zhenghuanrong/online-disaster-prevention.git
cd online-disaster-prevention

配置数据库

# 创建数据库
mysql -u root -p -e "CREATE DATABASE disaster_education DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

# 导入初始化脚本
mysql -u root -p disaster_education < sql/disaster_education.sql

修改后端配置

# backend/application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/disaster_education?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: your_password
  redis:
    host: localhost
    port: 6379
  cloud:
    nacos:
      server-addr: localhost:8848
    seata:
      tx-service-group: disaster_tx_group

启动分布式服务

# 启动Nacos/Redis/MySQL等基础服务
docker-compose -f docker-compose-base.yml up -d

# 启动微服务
docker-compose -f docker-compose-services.yml up -d

# 启动前端管理后台
cd frontend
npm install
npm run dev

项目特色与创新点

高并发防灾知识检索:结合Redis缓存与Elasticsearch搜索引擎,实现毫秒级课程检索与10万+/秒的缓存吞吐能力

智能灾害预警推送:基于地理位置的精准预警推送,通过Geohash算法实现区域化灾害预警覆盖

三维应急演练系统:集成WebGL技术构建沉浸式灾害演练场景,支持多人协同演练与智能评估

多级容灾架构:数据库主从复制、Redis哨兵模式、ES集群等多级容灾设计,保障系统99.99%可用性

用户行为推荐:基于协同过滤算法,为用户推荐个性化防灾学习路径与相关课程资源

参与贡献

欢迎对防灾教育事业感兴趣的开发者参与项目贡献:

Fork项目:在Gitee页面右上角点击”Fork”按钮

创建功能分支git checkout -b feature/new-function

提交代码git commit -m "添加新功能: 地震应急物资清单"

提交Pull Request:确保代码规范、添加必要注释,并描述功能点

许可证

本项目采用木兰宽松许可证,第2版(MulanPSL-2.0) 开源协议,允许自由使用、修改和分发,但需保留版权声明和许可证文件。具体条款请查看项目根目录下的LICENSE文件或访问http://license.coscl.org.cn/MulanPSL2。

结语

在线防灾教育系统致力于构建全民防灾减灾知识体系,通过技术创新提升公众防灾能力。项目目前已完成核心功能开发,但在AI灾害识别、VR演练场景等方面还有优化空间。期待更多开发者加入项目,共同完善这个利国利民的防灾教育平台,为构建安全社会贡献技术力量!

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容