简介:这是一套面向企业安全生产管理人员的危险化学品双重预防机制数字化管理系统源码,基于Java和Shell实现,聚焦危化品库存、监测、隐患排查与风险实时管控,适用于企业安全平台搭建及JavaWeb项目二次开发。资源共504个文件,压缩包约2.19MB,核心为416个Java源文件,覆盖业务逻辑、数据模型与接口设计;另含44个XML配置、26个VM模板、4个YML配置、4个BAT批处理及SQL脚本等,兼顾后端开发、环境配置、数据库部署与自动化运维。系统提供若依环境使用手册、项目说明文档,目录结构清晰,便于快速部署和阅读真实源码。当前已有338人学习,对于希望掌握双重预防机制数字化流程、研究企业级项目管理或扩展危化品安全功能的开发者,具备直接借鉴价值。
1. 一套藏在源码里的双重预防机制,到底解决了什么
接触过危化品企业安全管理的朋友都知道,最头疼的不是买设备、装监控,而是风险分级管控和隐患排查治理这两套体系经常“两张皮”:风险清单在Excel里,隐患台账在纸质本上,巡检记录拍几张照片就算完事。这套基于Java和Shell的企业危险化学品双重预防机制数字化管理系统,把416个Java源文件、44个XML配置、4个BAT批处理脚本和2个SQL脚本打包成一个完整闭环——从危险化学品库存台账、重大危险源监测数据接入,到隐患排查任务的生成、流转、销号,全部落在同一套数据模型里。如果你正打算做安全生产信息化建设,或者想从若依框架二次开发出自己的巡检系统,这个源码包的拆解价值不在于功能多炫,而在于它展示了一种标准化的落地方式:风险点如何编码、隐患如何分级、监测数据如何和预警规则挂钩。适合Java后端开发、安全环保工程师和对双重预防机制数字化有需求的团队参考。
2. 从文件清单反推系统架构:Java、XML、VM、YML各司其职
打开源码包,第一眼是满屏的.java和环境配置文件。很多人会问:为什么一个管理系统需要这么多种文件类型?其实每种文件都对应一个明确的架构职责。416个Java文件承载的是业务逻辑、数据模型和接口定义;XML配置文件主要用在MyBatis的Mapper映射、Spring的Bean装配以及若依框架的权限配置;26个VM文件是Velocity模板,负责导出Word、Excel报表时的数据渲染;4个YML文件则是Spring Boot的核心配置,定义了数据源、Redis、日志级别等运行参数。
2.1 从包结构识别领域边界
我习惯先看com.xxx.project下的包结构,大致就能猜到系统的模块划分。一个典型的危化品双重预防系统,至少要包含以下几个子域:
com.xxx.project ├── risk // 风险分级管控 │ ├── controller // 风险点、风险分级接口 │ ├── domain // 风险点实体、风险清单 │ ├── mapper // 风险数据MyBatis映射 │ └── service // 风险评价算法、分级逻辑 ├── hazard // 隐患排查治理 │ ├── controller // 隐患上报、整改、复查接口 │ ├── domain // 隐患台账、整改记录 │ └── service // 隐患闭环流程状态机 ├── monitor // 重大危险源监测 │ ├── controller // 监测数据接收、预警接口 │ └── service // 阈值判断、报警推送 ├── inventory // 危化品库存管理 │ ├── domain // 化学品台账、出入库记录 │ └── service // 库存数量、库存预警 └── common // 通用工具类这种分包方式的好处是:风险、隐患、监测、库存四个核心域之间通过数据库表和REST接口交互,互不渗透。比如风险评价模块只负责输出风险等级,不关心隐患整改流程;隐患模块读取风险点ID作为外键,完成“风险点—隐患”的关联查询。如果你能拿到源码,建议先用IDEA的Structure视图逐个Controller扫一遍,看每个模块对外暴露了哪些REST端点,这样对整个系统的业务边界十分钟就能建立起来。
2.2 数据模型设计的三个关键点
打开2个SQL脚本,你会发现数据库设计不是简单的CRUD,而是围绕双重预防机制的两个核心概念展开。风险点是第一层,隐患是第二层,两者通过risk_id关联。我一般重点关注三张表:
| 表名 | 核心字段 | 作用 |
|---|---|---|
risk_point | id, point_code, risk_level, hazard_type, department_id | 存储风险点基础信息,risk_code 是企业内唯一的风险编码,如GHS-HF-001 |
hazard_record | id, risk_id, hazard_desc, level, status, find_time, rect_deadline | 隐患台账,status 字段驱动流程:0待整改 1整改中 2待复查 3已闭环 |
monitor_realtime | id, point_id, monitor_type, value, threshold, collect_time | 重大危险源实时监测数据,用于阈值预警 |
这三张表的设计有一个容易被忽略的点:风险等级和隐患等级都是单独字段,而不是通过计算得出。原因是双重预防机制要求风险分级是“固有风险”,隐患排查是“动态管控”,两者的评价维度不同,不能混用。实际做二次开发时,我建议保留这种冗余设计,否则后面接大屏可视化时,统计SQL会变得非常痛苦。
2.3 YML配置里的运行环境玄机
4个YML文件通常是application.yml、application-dev.yml、application-prod.yml和application-druid.yml。前者定义公共配置,后者通过Maven Profile切换环境。一个典型的application-druid.yml片段:
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: master: url: jdbc:mysql://localhost:3306/risk_db?useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 driverClassName: com.mysql.cj.jdbc.Driver initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 stat-view-servlet: enabled: true url-pattern: /druid/* allow: 127.0.0.1这里的参数含义:initialSize是连接池启动时创建的物理连接数,maxActive是最大连接数,maxWait是获取连接的最大等待毫秒数。生产环境我一般会调大maxActive到50,并且把stat-view-servlet的allow限定为内网IP,避免Druid监控页面暴露。注意密码不要直接写在YML里,用jasypt加密或通过环境变量注入,这是很多二次开发项目最容易踩的坑。
3. 环境搭建与批处理/Shell脚本实战:从Windows到Linux的自动化部署
这套源码的特别之处在于同时提供了BAT和Shell两套脚本。若依框架原本依赖IDE启动,但企业实际运维中,服务器多半是CentOS 7/8,没有图形界面,只能靠命令行。所以源码里的ry.bat、run.bat、package.bat、clean.bat是给Windows开发机用的,而真正上线时你需要自己写Shell脚本——这也是我拆这个包时觉得最有借鉴价值的地方。
3.1 读懂BAT脚本的执行顺序
打开run.bat,常见写法是:
@echo off set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_202 set PATH=%JAVA_HOME%\bin;%PATH% cd /d D:\workspace\risk-system mvn clean package -DskipTests java -jar -Xms512m -Xmx1024m target/risk-system.jar pause第一行@echo off是关闭命令行回显,让输出干净一点。第3行和第4行手动设置JAVA_HOME和PATH,这是为了防止开发机装了多个JDK版本导致Java环境变量配置混乱——很多新手直接双击run.bat报错“不是内部或外部命令”,大概率是这里没配好。第6行进入项目根目录,第7行执行Maven打包,-DskipTests跳过单元测试加快速度。第8行启动Spring Boot Jar包,-Xms512m指定JVM堆初始内存512MB,-Xmx1024m最大堆内存1GB,这两个参数要根据服务器物理内存调整,我一般给到物理内存的一半左右。
package.bat和clean.bat更简单,本质是对Maven命令的封装:
mvn clean package -DskipTests mvn clean区别不大,但把它抽成单独文件能让运维同事不接触Maven命令也能完成构建发布,这是一种低门槛的工程化思维。如果你接手的是这套源码,建议随手加上一个restart.bat,内容是先调用clean.bat再run.bat,省去手动关进程的麻烦。
3.2 用Shell脚本完成Linux服务器自动化部署
Linux环境下不能跑BAT,需要写Shell脚本。我一般会在项目根目录放一个deploy.sh,内容如下:
#!/bin/bash APP_NAME=risk-system JAR_NAME=$APP_NAME.jar PORT=8080 # 根据端口杀旧进程 PID=$(netstat -tlnp | grep :$PORT | awk '{print $7}' | awk -F'/' '{print $1}') if [ -n "$PID" ]; then echo "stop old process: $PID" kill -9 $PID fi # 进入项目目录并打包 cd /opt/risk-system mvn clean package -DskipTests # 启动新进程 nohup java -jar -Dserver.port=$PORT target/$JAR_NAME --spring.profiles.active=prod > /dev/null 2>&1 & echo "deploy success, pid: $!"脚本里要注意的点:netstat -tlnp查看端口占用,grep :$PORT精确匹配端口,awk '{print $7}'提取PID/程序名,再用-F'/'分割拿纯PID。kill -9是强制杀死进程,实际生产环境建议先kill发送SIGTERM让Spring优雅停机,等5秒再kill -9兜底。--spring.profiles.active=prod指定生产配置,这样启动时会加载2.3节里的application-prod.yml。
很多人在Shell脚本里踩过nohup的坑:不加> /dev/null 2>&1,退出SSH后进程就挂了。这里nohup让进程忽略挂断信号,>重定向标准输出到空设备,2>&1把标准错误也并入标准输出,保证日志不会撑爆终端。想排错时,改成> /opt/risk-system/logs/app.log 2>&1 &,然后tail -f日志即可。
3.3 Shell常见坑:换行符、权限和Java环境
我猜很多读者是从“Shell脚本入门”搜到这个页面的。围绕这套源码,运维时最容易遇到三个坑。第一是编码问题,在Windows上写好的.sh文件传到Linux后执行报\r: command not found,因为BAT文件是CRLF换行,Shell只认LF。用sed -i 's/\r$//' deploy.sh批量去掉。第二是执行权限,Shell脚本需要chmod +x deploy.sh才能直接运行,否则只能bash deploy.sh。第三是Java环境变量,在/etc/profile里配置JAVA_HOME时,必须用export导出,否则脚本里识别不到java命令。检查方式:
source /etc/profile && echo $JAVA_HOME每次修改profile后要source重新加载,Shell -l登录模式会自动加载,但非登录Shell不会。如果java -version在SSH窗口能用,但脚本里报错,多半就是环境变量没全局导出。
4. 核心业务模块的Java实现:从库存查询到隐患闭环
模块代码是这套源码最厚的部分,我拆出三个有代表性的点来讲:工具类取舍、库存管理的Excel导入导出、隐患整改流程的状态控制。这些场景在面试题里常被拿出来当“Java八股文”考,但真正落地时坑远不止背概念那么简单。
4.1 ExcelUtil与Convert:看似不起眼,却决定运维效率
看到ExcelUtil.java和Convert.java,很多人会以为是若依自带的工具类。前者封装了EasyExcel/POI的读写逻辑,后者提供了类型转换工具。在危化品场景里,最常用的操作是把库存台账导出成Excel,或者把安监部门的Excel导入更新库存。一个简化版的库存导入方法:
public void importInventory(MultipartFile file) { ExcelUtil<Inventory> util = new ExcelUtil<>(Inventory.class); List<Inventory> list = util.importExcel(file.getInputStream()); for (Inventory inv : list) { // 按CAS号或物料代码判断是否存在 Inventory exist = inventoryMapper.selectByCasNo(inv.getCasNo()); if (exist != null) { exist.setStockQuantity(inv.getStockQuantity()); inventoryMapper.updateById(exist); } else { // CAS号不存在则新增 inventoryMapper.insert(inv); } } }逻辑说明:第3行通过反射解析Excel表头映射到Inventory实体,第5行开始逐行判断库存记录是否存在。这里用CAS号作为唯一键是个安全处理——危化品的全球唯一标识是CAS登记号,用名称会因“盐酸”和“氯化氢水溶液”这类别名导致重复台账。setStockQuantity更新数量时,必须注意并发问题,生产环境我会在更新的SQL里加stock_quantity = stock_quantity - #{outQuantity}而不是先查后算,防止多人同时操作时数据错乱。
4.2 隐患闭环流程:状态机的正确打开方式
隐患从发现、评估、整改到复查,本质上是一个有限状态机。如果直接用if else写在Service里,后续加“延期申请”或“转办”会非常痛苦。这套源码典型做法是在HazardRecord实体里加status字段,用整型枚举控制流转:
public class HazardStatus { public static final int WAITING = 0; // 待整改 public static final int RECTIFYING = 1; // 整改中 public static final int WAIT_REVIEW = 2; // 待复查 public static final int CLOSED = 3; // 已闭环 public static final int POSTPONED = 4; // 已延期 } public void rectify(Long hazardId, String rectifyResult) { HazardRecord record = hazardMapper.selectById(hazardId); // 校验当前状态必须为待整改 if (record.getStatus() != HazardStatus.WAITING) { throw new BusinessException("当前状态不允许整改"); } record.setRectifyResult(rectifyResult); record.setStatus(HazardStatus.RECTIFYING); record.setRectifyTime(new Date()); hazardMapper.updateById(record); // 发送通知给安全员 notifyService.sendTodo(record.getSupervisorId(), "待复查"); }参数说明:第7行hazardId是隐患主键,第13行设置整改结果,第14行把状态从0改成1。注意这里必须做状态校验,否则一个已被复查关闭的隐患可以被反复“整改”,整个闭环就失去审计意义。状态字段建议用tinyint存数据库,注释写清楚0-4分别代表什么,别用含义不明的数字裸奔。如果你想加更复杂的流程,可以把状态机抽成策略模式,用Map维护状态到处理器的映射,但小项目用上面的方式已经足够清晰。
4.3 风险评价与阈值预警的数据处理
风险分级通常是“LEC法”或“LS法”。LEC法公式是D = L × E × C,L是事故发生可能性,E是暴露频率,C是后果严重性,D值决定风险等级。在Java里实现可以写成:
public RiskLevel evaluate(int L, int E, int C) { int D = L * E * C; if (D >= 320) return RiskLevel.重大; if (D >= 160) return RiskLevel.较大; if (D >= 70) return RiskLevel.一般; return RiskLevel.低; }这段代码的逻辑很直接,但要注意两点:一是L/E/C的取值必须在枚举里约束,否则用户可以填C=500直接导致D溢出;二是评价结果要拿risk_point_id做外键存到风险清单表,方便后续隐患关联。监测阈值预警也是类似思路,在接收实时数据的Controller里,先插入monitor_realtime表,再判断是否超过阈值,超过则调用消息服务推送报警。
5. 最后的坑:安全过滤器、SQL脚本和排查技巧
这套源码还有两个容易被忽略的部分,一个是HTMLFilter.java,一个是两个SQL脚本的初始化数据。前者是XSS防护过滤器,后者决定系统一启动就有基础字典和菜单数据。最后一章聊点实际经验。
5.1 HTMLFilter与系统安全加固
HTMLFilter的核心是把用户提交的富文本内容里的<script>、<iframe>等危险标签过滤掉。若依框架自带这个类,但很多人二次开发时会关掉它,原因是“有的业务需要粘贴HTML表格”。我的建议是不要整体关闭,而是按接口维度放行。比如隐患详情描述可以压缩危险标签,只允许<table>、<td>、<br>;而风险名称字段则完全禁止任何HTML。一个常见做法是自定义注解,在Controller方法的参数上加校验:
@PostMapping("/hazard") public AjaxResult add(@RequestBody @SafeHtml(whitelist = {WHITELIST.TABLE, WHITELIST.TR, WHITELIST.TD}) HazardRecord hazard) { return hazardService.insert(hazard); }这样既保留了表格段落样式的提交能力,又防住了脚本注入。另一个安全重点是Druid监控页面权限,2.3节已经提过要限制IP。另外,SQL脚本里的初始管理员密码默认是admin/admin123,上线前必须强制修改,否则攻击者可以用默认口令直接登录后台查看危化品库存——这属于重大安全事故前的“人祸”。
5.2 SQL脚本的初始化数据策略
两个SQL文件通常是ry_2024.sql和quartz.sql。前者包含若依框架的菜单权限表、部门表、字典表,后者是Quartz定时任务的建表语句。你在自己的MySQL里执行时,建议先创建独立数据库,再用命令行导入:
mysql -u root -p risk_db < /opt/risk-system/sql/ry_2024.sql导入后不要急着登录系统。打开sys_menu表,把针对危化品模块的菜单项检查一遍,尤其是按钮权限的perms字段。如果菜单ID和外键关联错了,页面会显示不出来或按钮全部不可见。排查时看浏览器Network请求,如果/getRouters返回空数组,多半是菜单表里status字段为1被禁用。
5.3 送一个排查技巧:通过Shell做日志分析
系统上线后日志每天增长很快,手动tail -f不现实。我一般配合Shell脚本做关键字统计,快速定位异常。以下脚本统计当天“Exception”出现次数最多的前10个类:
#!/bin/bash LOG_FILE=/opt/risk-system/logs/app.log TODAY=$(date +%Y-%m-%d) grep "$TODAY.*Exception" $LOG_FILE | \ awk '{for(i=1;i<=NF;i++) if($i ~ /Exception$/) print $i}' | \ sort | uniq -c | sort -rn | head -10逐段解释:grep 先捞当天异常行,awk 按空格切分字段,找出以Exception结尾的单词,sort 排序后uniq -c统计次数,再用sort -rn按次数倒排,head 取前10。这套组合命令在排查“java.lang.NullPointerException”或“Redis连接超时”时非常管用。要注意日志格式,如果时间戳不在行首或包含括号,需要调整grep的正则,否则过滤不到数据。
本文还有配套的精品资源,点击获取