From 0c23c1e9b9e06ffc570edac28ee45555b772b99c Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 25 五月 2026 11:49:49 +0800
Subject: [PATCH] feat(safety): 新增安全培训测评功能

---
 src/main/java/com/ruoyi/safety/pojo/SafetyTrainingPlan.java                              |   30 
 src/main/java/com/ruoyi/safety/pojo/SafetyWorkArea.java                                  |   19 
 src/main/java/com/ruoyi/safety/mapper/SafetyTrainingPlanMapper.java                      |    9 
 src/main/java/com/ruoyi/safety/service/SafetyEquipmentService.java                       |    6 
 src/main/java/com/ruoyi/safety/service/SafetyBaseService.java                            |   17 
 src/main/java/com/ruoyi/safety/service/SafetyTrainingMaterialService.java                |    6 
 src/main/java/com/ruoyi/safety/service/impl/SafetyRiskServiceImpl.java                   |   31 
 src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentTaskEmployee.java                    |   30 
 src/main/java/com/ruoyi/safety/service/SafetyTrainingRecordService.java                  |    6 
 src/main/java/com/ruoyi/safety/service/SafetyRiskService.java                            |    6 
 src/main/java/com/ruoyi/safety/controller/SafetyRiskController.java                      |   76 +
 src/main/java/com/ruoyi/safety/mapper/SafetyTrainingMaterialMapper.java                  |    9 
 src/main/java/com/ruoyi/safety/controller/SafetyInspectionStatisticsController.java      |   65 +
 src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentReport.java                          |   32 
 src/main/resources/application-dev.yml                                                   |  222 ++++
 src/main/java/com/ruoyi/safety/controller/SafetyTrainingPlanController.java              |   76 +
 src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentReportServiceImpl.java       |   36 
 src/main/java/com/ruoyi/safety/controller/SafetyControllerSupport.java                   |   31 
 src/main/java/com/ruoyi/safety/dto/SafetyInspectionTodayStatistics.java                  |   23 
 src/main/java/com/ruoyi/safety/controller/SafetyTrainingRecordController.java            |   93 +
 src/main/java/com/ruoyi/safety/service/SafetyPersonnelService.java                       |    6 
 src/main/java/com/ruoyi/safety/pojo/SafetyEmergencyResource.java                         |   25 
 src/main/java/com/ruoyi/safety/service/impl/SafetyBaseServiceImpl.java                   |   57 +
 src/main/java/com/ruoyi/safety/pojo/SafetyPersonnel.java                                 |   29 
 src/main/java/com/ruoyi/safety/controller/SafetyPersonnelController.java                 |   76 +
 src/main/java/com/ruoyi/safety/dto/SafetyInspectionTypeStat.java                         |   11 
 src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentTaskEmployeeServiceImpl.java |  141 ++
 src/main/java/com/ruoyi/safety/controller/SafetyInspectionRecordController.java          |   72 +
 src/main/java/com/ruoyi/safety/mapper/SafetyRiskMapper.java                              |    9 
 src/main/java/com/ruoyi/safety/service/impl/SafetyPersonnelServiceImpl.java              |   34 
 src/main/java/com/ruoyi/safety/dto/SafetyInspectionTrendStat.java                        |   21 
 src/main/resources/mapper/safety/SafetyInspectionRecordMapper.xml                        |   68 +
 src/main/java/com/ruoyi/safety/service/impl/SafetyEmergencyResourceServiceImpl.java      |   37 
 src/main/java/com/ruoyi/safety/service/SafetyInspectionRecordService.java                |   29 
 src/main/java/com/ruoyi/safety/pojo/SafetyBaseEntity.java                                |   43 
 src/main/java/com/ruoyi/safety/pojo/SafetyEquipment.java                                 |   21 
 src/main/java/com/ruoyi/safety/service/SafetyAssessmentTaskService.java                  |    6 
 src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentTaskServiceImpl.java         |   33 
 src/main/java/com/ruoyi/safety/service/impl/SafetyEquipmentServiceImpl.java              |   34 
 src/main/java/com/ruoyi/safety/controller/SafetyAssessmentReportController.java          |   46 
 src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentTaskMapper.java                    |    9 
 src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentTask.java                            |   25 
 src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingMaterialServiceImpl.java       |   50 
 src/main/java/com/ruoyi/safety/service/SafetyWorkAreaService.java                        |    6 
 pom.xml                                                                                  |   33 
 src/main/java/com/ruoyi/safety/controller/SafetyWorkAreaController.java                  |   87 +
 src/main/java/com/ruoyi/safety/service/impl/SafetyWorkAreaServiceImpl.java               |   31 
 src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingRecordServiceImpl.java         |   36 
 src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingPlanServiceImpl.java           |   35 
 src/main/java/com/ruoyi/safety/service/SafetyLearningRecordService.java                  |    9 
 src/main/java/com/ruoyi/safety/mapper/SafetyLearningRecordMapper.java                    |   12 
 src/main/java/com/ruoyi/safety/mapper/SafetyInspectionRecordMapper.java                  |   28 
 src/main/java/com/ruoyi/safety/controller/SafetyAssessmentTaskController.java            |   61 +
 src/main/java/com/ruoyi/safety/controller/SafetyEquipmentController.java                 |   76 +
 src/main/java/com/ruoyi/safety/dto/SafetyInspectorStat.java                              |   25 
 src/main/java/com/ruoyi/safety/dto/SafetyLearningStatistics.java                         |   19 
 src/main/java/com/ruoyi/safety/controller/SafetyLearningRecordController.java            |   46 
 src/main/java/com/ruoyi/safety/controller/SafetyTrainingMaterialController.java          |   76 +
 src/main/java/com/ruoyi/safety/controller/SafetyEmergencyResourceController.java         |   76 +
 src/main/java/com/ruoyi/safety/pojo/SafetyTrainingRecord.java                            |   38 
 src/main/java/com/ruoyi/safety/service/SafetyTrainingPlanService.java                    |    6 
 src/main/java/com/ruoyi/safety/mapper/SafetyTrainingRecordMapper.java                    |    9 
 src/main/java/com/ruoyi/safety/mapper/SafetyEmergencyResourceMapper.java                 |    9 
 src/main/java/com/ruoyi/safety/pojo/SafetyRisk.java                                      |   21 
 src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentReportMapper.java                  |    9 
 src/main/java/com/ruoyi/safety/pojo/SafetyTrainingMaterial.java                          |   33 
 doc/safety-front-integration.md                                                          |  274 +++++
 src/main/java/com/ruoyi/safety/service/SafetyAssessmentTaskEmployeeService.java          |   10 
 src/main/java/com/ruoyi/safety/service/impl/SafetyInspectionRecordServiceImpl.java       |  194 +++
 src/main/java/com/ruoyi/safety/pojo/SafetyLearningRecord.java                            |   32 
 src/main/resources/mapper/safety/SafetyLearningRecordMapper.xml                          |   19 
 src/main/java/com/ruoyi/safety/mapper/SafetyEquipmentMapper.java                         |    9 
 src/main/java/com/ruoyi/safety/mapper/SafetyPersonnelMapper.java                         |    9 
 src/main/java/com/ruoyi/safety/pojo/SafetyInspectionRecord.java                          |   35 
 src/main/java/com/ruoyi/safety/service/SafetyAssessmentReportService.java                |    6 
 src/main/java/com/ruoyi/safety/service/SafetyEmergencyResourceService.java               |    6 
 src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentTaskEmployeeMapper.java            |    9 
 src/main/java/com/ruoyi/safety/mapper/SafetyWorkAreaMapper.java                          |    9 
 src/main/resources/application.yml                                                       |    2 
 src/main/java/com/ruoyi/safety/service/impl/SafetyLearningRecordServiceImpl.java         |   42 
 80 files changed, 3,134 insertions(+), 8 deletions(-)

diff --git a/doc/safety-front-integration.md b/doc/safety-front-integration.md
new file mode 100644
index 0000000..a10e9e3
--- /dev/null
+++ b/doc/safety-front-integration.md
@@ -0,0 +1,274 @@
+# safety妯″潡鍓嶇鑱旇皟鏂囨。
+
+鏇存柊鏃堕棿锛�2026-05-25
+
+## 1. 鍏叡绾﹀畾
+
+### 1.1 璁よ瘉
+
+- 璇锋眰澶达細`Authorization: Bearer ${token}`
+- 鐧诲綍鎺ュ彛锛歚POST /login`
+
+### 1.2 缁熶竴杩斿洖
+
+鏅�氭帴鍙g粺涓�杩斿洖 `AjaxResult`锛�
+
+```json
+{
+  "code": 200,
+  "msg": "鎿嶄綔鎴愬姛",
+  "data": {}
+}
+```
+
+### 1.3 鍒嗛〉杩斿洖
+
+safety 妯″潡鍒楄〃鎺ュ彛杩斿洖 MyBatis Plus `Page`锛屾牳蹇冨瓧娈碉細
+
+- `data.records`
+- `data.total`
+- `data.current`
+- `data.size`
+
+鍒嗛〉鍙傛暟寤鸿缁熶竴浣跨敤锛�
+
+- `pageNum`
+- `pageSize`
+
+### 1.4 鏃ユ湡鏍煎紡
+
+- `LocalDate`锛歚yyyy-MM-dd`
+- `LocalDateTime`锛歚yyyy-MM-dd HH:mm:ss`
+
+### 1.5 鍏叡瀛楁
+
+鎵�鏈� safety 瀹炰綋閮藉寘鍚互涓嬪叕鍏卞瓧娈碉紙鏂板鏃舵棤闇�浼狅級锛�
+
+- `id`
+- `createBy`
+- `createTime`
+- `updateBy`
+- `updateTime`
+- `remark`
+- `delFlag`锛堥�昏緫鍒犻櫎锛�
+- `tenantId`
+
+## 2. 鎺ュ彛娓呭崟
+
+### 2.1 鍩虹妗f
+
+- 浜哄憳妗f
+  - `GET /safety/personnel/list`
+  - `POST /safety/personnel/add`
+  - `PUT /safety/personnel/update`
+  - `DELETE /safety/personnel/delete/{id}`
+  - `GET /safety/personnel/detail/{id}`
+- 宀椾綅椋庨櫓
+  - `GET /safety/risk/list`
+  - `POST /safety/risk/add`
+  - `PUT /safety/risk/update`
+  - `DELETE /safety/risk/delete/{id}`
+  - `GET /safety/risk/detail/{id}`
+- 浣滀笟鍖哄煙
+  - `GET /safety/workArea/list`
+  - `GET /safety/workArea/all`
+  - `POST /safety/workArea/add`
+  - `PUT /safety/workArea/update`
+  - `DELETE /safety/workArea/delete/{id}`
+  - `GET /safety/workArea/detail/{id}`
+- 璁惧璁炬柦
+  - `GET /safety/equipment/list`
+  - `POST /safety/equipment/add`
+  - `PUT /safety/equipment/update`
+  - `DELETE /safety/equipment/delete/{id}`
+  - `GET /safety/equipment/detail/{id}`
+- 搴旀�ヨ祫婧�
+  - `GET /safety/emergency/list`
+  - `POST /safety/emergency/add`
+  - `PUT /safety/emergency/update`
+  - `DELETE /safety/emergency/delete/{id}`
+  - `GET /safety/emergency/detail/{id}`
+
+### 2.2 宸℃
+
+- 宸℃璁板綍
+  - `GET /safety/inspection/record/list`
+  - `POST /safety/inspection/record/add`
+  - `PUT /safety/inspection/record/update`
+  - `DELETE /safety/inspection/record/delete/{id}`
+  - `POST /safety/inspection/record/sync`锛堟柊澧烇細妯℃嫙瀛愮郴缁熷悓姝ワ紝鑷姩鍒涘缓18鏉★級
+- 宸℃缁熻
+  - `GET /safety/inspection/statistics/today`
+  - `GET /safety/inspection/statistics/trend`
+  - `GET /safety/inspection/statistics/type`
+  - `GET /safety/inspection/statistics/inspector`
+
+### 2.3 鍩硅瀛︿範
+
+- 鍩硅璧勬枡
+  - `GET /safety/training/material/list`
+  - `POST /safety/training/material/upload`锛堜繚瀛樺厓鏁版嵁锛�
+  - `PUT /safety/training/material/update`
+  - `DELETE /safety/training/material/delete/{id}`
+  - `GET /safety/training/material/detail/{id}`
+- 鍩硅璁″垝
+  - `GET /safety/training/plan/list`
+  - `POST /safety/training/plan/add`
+  - `PUT /safety/training/plan/update`
+  - `DELETE /safety/training/plan/delete/{id}`
+  - `GET /safety/training/plan/detail/{id}`
+- 鍩硅瀹屾垚璁板綍
+  - `GET /safety/training/record/list`
+  - `POST /safety/training/record/add`
+  - `PUT /safety/training/record/update`
+  - `DELETE /safety/training/record/delete/{id}`
+  - `GET /safety/training/record/detail/{id}`
+  - `GET /safety/training/record/export`
+- 鍛樺伐瀛︿範璁板綍
+  - `GET /safety/learning/record/list`
+  - `GET /safety/learning/record/statistics`
+
+### 2.4 娴嬭瘎涓庢姤鍛�
+
+- 娴嬭瘎浠诲姟
+  - `GET /safety/learning/assessment/list`
+  - `POST /safety/learning/assessment/start`
+  - `POST /safety/learning/assessment/submit`
+- 鑰冩牳鎶ュ憡
+  - `GET /safety/learning/report/list`
+  - `GET /safety/learning/report/detail/{id}`
+
+## 3. 宸℃妯″潡鑱旇皟閲嶇偣
+
+### 3.1 宸℃璁板綍鏌ヨ
+
+`GET /safety/inspection/record/list`
+
+鍙敤绛涢�夊弬鏁帮細
+
+- `startDate`銆乣endDate`锛堟寜宸℃鏃堕棿鍖洪棿锛�
+- `inspectorId`銆乣inspector`
+- `areaId`銆乣area`
+- `type`
+- `status`
+- `pageNum`銆乣pageSize`
+
+鍏抽敭瀛楁锛�
+
+- `inspectionTime`
+- `inspectorId`銆乣inspector`
+- `areaId`銆乣area`
+- `type`
+- `status`
+- `abnormalDesc`
+- `isMissed`
+
+### 3.2 宸℃鐘舵�佸彛寰勶紙鍚庣缁熻鍙e緞锛�
+
+- `status=0`锛氬凡瀹屾垚锛堟甯革級
+- `status=1`锛氬凡瀹屾垚锛堝紓甯革級
+- `status=2`锛氬凡瀹屾垚锛堟紡妫�锛�
+- `status=3`锛氭湭鎵ц
+- `isMissed=1`锛氫篃璁″叆婕忔缁熻
+
+### 3.3 鍚屾妯℃嫙瀛愮郴缁熸暟鎹紙鏂板锛�
+
+鎺ュ彛锛歚POST /safety/inspection/record/sync`
+
+鐢ㄩ�旓細鍓嶇鐐瑰嚮鈥滃悓姝ユ暟鎹�濇寜閽椂璋冪敤锛岀郴缁熶細鑷姩鐢熸垚 18 鏉℃ā鎷熷贰妫�鏁版嵁鍐欏叆鏁版嵁搴擄紝骞跺皢杩� 18 鏉¤褰曡繑鍥炵粰鍓嶇銆�
+
+璇锋眰鍙傛暟锛氭棤
+
+璋冪敤闄愬埗锛氭瘡澶╁彧鑳芥垚鍔熻皟鐢� 1 娆★紙Redis 鎸夆�滅鎴�+鏃ユ湡鈥濇帶鍒讹級銆傚悓涓�澶╅噸澶嶈皟鐢ㄤ細杩斿洖澶辫触鎻愮ず锛歚浠婃棩宸插悓姝ワ紝鏄庡ぉ鍐嶈瘯`銆�
+
+杩斿洖绀轰緥锛�
+
+```json
+{
+  "code": 200,
+  "msg": "鎿嶄綔鎴愬姛",
+  "data": {
+    "count": 18,
+    "records": [
+      {
+        "inspectionTime": "2026-05-25 08:20:00",
+        "inspectorId": 101,
+        "inspector": "inspector-a",
+        "areaId": 1,
+        "area": "workshop-a",
+        "type": "daily",
+        "status": 0,
+        "abnormalDesc": null,
+        "isMissed": 0
+      }
+    ]
+  }
+}
+```
+
+鑱旇皟寤鸿璋冪敤椤哄簭锛�
+
+1. 鐐瑰嚮鍚屾鎸夐挳鍚庡厛璋冪敤 `POST /safety/inspection/record/sync`
+2. 鎴愬姛鍚庡埛鏂板垪琛細`GET /safety/inspection/record/list`
+3. 鍐嶅埛鏂扮粺璁″崱鐗囧拰鍥捐〃锛�
+   - `GET /safety/inspection/statistics/today`
+   - `GET /safety/inspection/statistics/trend`
+   - `GET /safety/inspection/statistics/type`
+   - `GET /safety/inspection/statistics/inspector`
+
+### 3.4 宸℃缁熻榛樿鏃堕棿鑼冨洿
+
+`trend/type/inspector` 涓変釜缁熻鎺ュ彛鑻ヤ笉浼� `startDate/endDate`锛屽悗绔粯璁ゆ渶杩� 7 澶╋紙褰撳ぉ寰�鍓嶆帹 6 澶╋級銆�
+
+## 4. 鍩硅璧勬枡涓婁紶璇存槑
+
+`/safety/training/material/upload` 鍙繚瀛樿祫鏂欏厓鏁版嵁锛屼笉鎺ユ敹浜岃繘鍒舵枃浠躲��
+
+鎺ㄨ崘娴佺▼锛�
+
+1. 鍏堣皟鐢� `POST /common/upload` 涓婁紶鏂囦欢
+2. 鎷垮埌 `url/fileName/originalFilename` 鍚庤皟鐢� `POST /safety/training/material/upload` 淇濆瓨鍏冩暟鎹�
+
+## 5. 鍓嶇灏佽绀轰緥锛堝贰妫�鍚屾锛�
+
+```js
+import request from '@/utils/request'
+
+export function syncInspectionData() {
+  return request({
+    url: '/safety/inspection/record/sync',
+    method: 'post'
+  })
+}
+```
+
+## 6. 鍩硅瀹屾垚璁板綍琛ュ厖锛堝鍒犳敼鏌ワ級
+
+鍩虹鍦板潃锛歚/safety/training/record`
+
+宸插紑鏀炬帴鍙o細
+
+1. `GET /list`锛氬垎椤垫煡璇�
+2. `POST /add`锛氭柊澧�
+3. `PUT /update`锛氫慨鏀�
+4. `DELETE /delete/{id}`锛氬垹闄�
+5. `GET /detail/{id}`锛氳鎯�
+6. `GET /export`锛氬鍑�
+
+鏂板/淇敼璇锋眰浣撶ず渚嬶細
+
+```json
+{
+  "planId": 1,
+  "employeeId": 1001,
+  "employeeName": "寮犱笁",
+  "content": "涓夌骇瀹夊叏鏁欒偛鍩硅",
+  "completeTime": "2026-05-25 10:00:00",
+  "duration": 2.0,
+  "method": "绾夸笅鎺堣",
+  "result": "閫氳繃",
+  "score": 92,
+  "status": 1,
+  "remark": "琛ュ綍"
+}
+```
diff --git a/pom.xml b/pom.xml
index e2b697f..b6538a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,9 +41,10 @@
         <okhttp.version>4.9.0</okhttp.version>
         <logback.version>1.2.13</logback.version>
         <spring-security.version>5.7.12</spring-security.version>
-        <spring-framework.version>5.3.39</spring-framework.version>
-        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
-    </properties>
+        <spring-framework.version>5.3.39</spring-framework.version>
+        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
+        <lombok.version>1.18.42</lombok.version>
+    </properties>
 
     <dependencies>
         <dependency>
@@ -304,10 +305,28 @@
 
 	<build>
 	    <finalName>${project.artifactId}</finalName>
-	    <plugins>
-	        <plugin>
-	            <groupId>org.springframework.boot</groupId>
-	            <artifactId>spring-boot-maven-plugin</artifactId>
+	    <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.13.0</version>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                    <proc>full</proc>
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                            <version>${lombok.version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                </configuration>
+            </plugin>
+	        <plugin>
+	            <groupId>org.springframework.boot</groupId>
+	            <artifactId>spring-boot-maven-plugin</artifactId>
 	            <configuration>
 	                <fork>true</fork> <!-- 濡傛灉娌℃湁璇ラ厤缃紝devtools涓嶄細鐢熸晥 -->
 	            </configuration>
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyAssessmentReportController.java b/src/main/java/com/ruoyi/safety/controller/SafetyAssessmentReportController.java
new file mode 100644
index 0000000..e32366b
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyAssessmentReportController.java
@@ -0,0 +1,46 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyAssessmentReport;
+import com.ruoyi.safety.service.SafetyAssessmentReportService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-鑰冩牳鎶ュ憡鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-鑰冩牳鎶ュ憡")
+@RequestMapping("/safety/learning/report")
+public class SafetyAssessmentReportController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyAssessmentReportService reportService;
+
+    /**
+     * 鍒嗛〉鏌ヨ鑰冩牳鎶ュ憡銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鑰冩牳鎶ュ憡-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ鑰冩牳鎶ュ憡")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyAssessmentReport> page, SafetyAssessmentReport query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(reportService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 鏌ヨ鑰冩牳鎶ュ憡璇︽儏銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鑰冩牳鎶ュ憡-璇︽儏", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ鑰冩牳鎶ュ憡璇︽儏")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(reportService.getById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyAssessmentTaskController.java b/src/main/java/com/ruoyi/safety/controller/SafetyAssessmentTaskController.java
new file mode 100644
index 0000000..a26d2c9
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyAssessmentTaskController.java
@@ -0,0 +1,61 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyAssessmentTask;
+import com.ruoyi.safety.pojo.SafetyAssessmentTaskEmployee;
+import com.ruoyi.safety.service.SafetyAssessmentTaskEmployeeService;
+import com.ruoyi.safety.service.SafetyAssessmentTaskService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-娴嬭瘎浠诲姟鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-娴嬭瘎浠诲姟")
+@RequestMapping("/safety/learning/assessment")
+public class SafetyAssessmentTaskController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyAssessmentTaskService taskService;
+
+    @Autowired
+    private SafetyAssessmentTaskEmployeeService taskEmployeeService;
+
+    /**
+     * 鍒嗛〉鏌ヨ娴嬭瘎浠诲姟銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-娴嬭瘎浠诲姟-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ娴嬭瘎浠诲姟")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyAssessmentTask> page, SafetyAssessmentTask query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(taskService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 寮�濮嬫祴璇勶紝鐢熸垚褰撳墠鍛樺伐涓庢祴璇勪换鍔$殑鍏宠仈璁板綍銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-娴嬭瘎浠诲姟-寮�濮嬫祴璇�", businessType = BusinessType.INSERT)
+    @ApiOperation("寮�濮嬫祴璇�")
+    @PostMapping("/start")
+    public AjaxResult start(@RequestBody SafetyAssessmentTaskEmployee request) {
+        return toAjax(taskEmployeeService.start(request));
+    }
+
+    /**
+     * 鎻愪氦娴嬭瘎缁撴灉銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-娴嬭瘎浠诲姟-鎻愪氦娴嬭瘎", businessType = BusinessType.UPDATE)
+    @ApiOperation("鎻愪氦娴嬭瘎")
+    @PostMapping("/submit")
+    public AjaxResult submit(@RequestBody SafetyAssessmentTaskEmployee request) {
+        return toAjax(taskEmployeeService.submit(request));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyControllerSupport.java b/src/main/java/com/ruoyi/safety/controller/SafetyControllerSupport.java
new file mode 100644
index 0000000..4046962
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyControllerSupport.java
@@ -0,0 +1,31 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.AjaxResult;
+
+/**
+ * 瀹夌幆绠$悊 Controller 鍏叡鏂规硶銆�
+ */
+public abstract class SafetyControllerSupport {
+
+    /**
+     * 鍏煎 MyBatis Plus 鐨� current/size 涓庢枃妗d腑鐨� pageNum/pageSize 鍒嗛〉鍙傛暟銆�
+     */
+    protected <T> Page<T> buildPage(Page<T> page, Long pageNum, Long pageSize) {
+        Page<T> result = page == null ? new Page<T>() : page;
+        if (pageNum != null) {
+            result.setCurrent(pageNum);
+        }
+        if (pageSize != null) {
+            result.setSize(pageSize);
+        }
+        return result;
+    }
+
+    /**
+     * 灏嗗竷灏斿鐞嗙粨鏋滆浆鎹负缁熶竴鍝嶅簲銆�
+     */
+    protected AjaxResult toAjax(boolean success) {
+        return success ? AjaxResult.success() : AjaxResult.error();
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyEmergencyResourceController.java b/src/main/java/com/ruoyi/safety/controller/SafetyEmergencyResourceController.java
new file mode 100644
index 0000000..3ade8f9
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyEmergencyResourceController.java
@@ -0,0 +1,76 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyEmergencyResource;
+import com.ruoyi.safety.service.SafetyEmergencyResourceService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-搴旀�ヨ祫婧愭帴鍙c��
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-搴旀�ヨ祫婧�")
+@RequestMapping("/safety/emergency")
+public class SafetyEmergencyResourceController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyEmergencyResourceService emergencyResourceService;
+
+    /**
+     * 鍒嗛〉鏌ヨ搴旀�ヨ祫婧愩��
+     */
+    @Log(title = "瀹夌幆绠$悊-搴旀�ヨ祫婧�-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ搴旀�ヨ祫婧�")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyEmergencyResource> page, SafetyEmergencyResource query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(emergencyResourceService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 鏂板搴旀�ヨ祫婧愩��
+     */
+    @Log(title = "瀹夌幆绠$悊-搴旀�ヨ祫婧�-鏂板", businessType = BusinessType.INSERT)
+    @ApiOperation("鏂板搴旀�ヨ祫婧�")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody SafetyEmergencyResource resource) {
+        return toAjax(emergencyResourceService.saveSafety(resource));
+    }
+
+    /**
+     * 淇敼搴旀�ヨ祫婧愩��
+     */
+    @Log(title = "瀹夌幆绠$悊-搴旀�ヨ祫婧�-淇敼", businessType = BusinessType.UPDATE)
+    @ApiOperation("淇敼搴旀�ヨ祫婧�")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyEmergencyResource resource) {
+        return toAjax(emergencyResourceService.updateSafety(resource));
+    }
+
+    /**
+     * 鍒犻櫎搴旀�ヨ祫婧愩��
+     */
+    @Log(title = "瀹夌幆绠$悊-搴旀�ヨ祫婧�-鍒犻櫎", businessType = BusinessType.DELETE)
+    @ApiOperation("鍒犻櫎搴旀�ヨ祫婧�")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(emergencyResourceService.deleteSafetyById(id));
+    }
+
+    /**
+     * 鏌ヨ搴旀�ヨ祫婧愯鎯呫��
+     */
+    @Log(title = "瀹夌幆绠$悊-搴旀�ヨ祫婧�-璇︽儏", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ搴旀�ヨ祫婧愯鎯�")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(emergencyResourceService.getById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyEquipmentController.java b/src/main/java/com/ruoyi/safety/controller/SafetyEquipmentController.java
new file mode 100644
index 0000000..49354d1
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyEquipmentController.java
@@ -0,0 +1,76 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyEquipment;
+import com.ruoyi.safety.service.SafetyEquipmentService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-璁惧璁炬柦鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-璁惧璁炬柦")
+@RequestMapping("/safety/equipment")
+public class SafetyEquipmentController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyEquipmentService equipmentService;
+
+    /**
+     * 鍒嗛〉鏌ヨ璁惧璁炬柦銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-璁惧璁炬柦-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ璁惧璁炬柦")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyEquipment> page, SafetyEquipment query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(equipmentService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 鏂板璁惧璁炬柦銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-璁惧璁炬柦-鏂板", businessType = BusinessType.INSERT)
+    @ApiOperation("鏂板璁惧璁炬柦")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody SafetyEquipment equipment) {
+        return toAjax(equipmentService.saveSafety(equipment));
+    }
+
+    /**
+     * 淇敼璁惧璁炬柦銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-璁惧璁炬柦-淇敼", businessType = BusinessType.UPDATE)
+    @ApiOperation("淇敼璁惧璁炬柦")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyEquipment equipment) {
+        return toAjax(equipmentService.updateSafety(equipment));
+    }
+
+    /**
+     * 鍒犻櫎璁惧璁炬柦銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-璁惧璁炬柦-鍒犻櫎", businessType = BusinessType.DELETE)
+    @ApiOperation("鍒犻櫎璁惧璁炬柦")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(equipmentService.deleteSafetyById(id));
+    }
+
+    /**
+     * 鏌ヨ璁惧璁炬柦璇︽儏銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-璁惧璁炬柦-璇︽儏", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ璁惧璁炬柦璇︽儏")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(equipmentService.getById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyInspectionRecordController.java b/src/main/java/com/ruoyi/safety/controller/SafetyInspectionRecordController.java
new file mode 100644
index 0000000..30dac35
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyInspectionRecordController.java
@@ -0,0 +1,72 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyInspectionRecord;
+import com.ruoyi.safety.service.SafetyInspectionRecordService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Safety inspection record APIs.
+ */
+@RestController
+@Api(tags = "safety-inspection-record")
+@RequestMapping("/safety/inspection/record")
+public class SafetyInspectionRecordController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyInspectionRecordService recordService;
+
+    @Log(title = "safety-inspection-record-list", businessType = BusinessType.OTHER)
+    @ApiOperation("query safety inspection records by page")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyInspectionRecord> page,
+                           SafetyInspectionRecord query,
+                           @RequestParam(value = "startDate", required = false) String startDate,
+                           @RequestParam(value = "endDate", required = false) String endDate,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(recordService.queryPage(buildPage(page, pageNum, pageSize), query, startDate, endDate));
+    }
+
+    @Log(title = "safety-inspection-record-add", businessType = BusinessType.INSERT)
+    @ApiOperation("add safety inspection record")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody SafetyInspectionRecord record) {
+        return toAjax(recordService.saveSafety(record));
+    }
+
+    @Log(title = "safety-inspection-record-update", businessType = BusinessType.UPDATE)
+    @ApiOperation("update safety inspection record")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyInspectionRecord record) {
+        return toAjax(recordService.updateSafety(record));
+    }
+
+    @Log(title = "safety-inspection-record-delete", businessType = BusinessType.DELETE)
+    @ApiOperation("delete safety inspection record")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(recordService.deleteSafetyById(id));
+    }
+
+    @Log(title = "safety-inspection-record-sync", businessType = BusinessType.IMPORT)
+    @ApiOperation("sync mock subsystem inspection data")
+    @PostMapping("/sync")
+    public AjaxResult sync() {
+        List<SafetyInspectionRecord> records = recordService.syncMockSubsystemData();
+        Map<String, Object> data = new HashMap<String, Object>(2);
+        data.put("count", records.size());
+        data.put("records", records);
+        return AjaxResult.success(data);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyInspectionStatisticsController.java b/src/main/java/com/ruoyi/safety/controller/SafetyInspectionStatisticsController.java
new file mode 100644
index 0000000..10ff0f9
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyInspectionStatisticsController.java
@@ -0,0 +1,65 @@
+package com.ruoyi.safety.controller;
+
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.service.SafetyInspectionRecordService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-宸℃缁熻鎶ヨ〃鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-宸℃缁熻鎶ヨ〃")
+@RequestMapping("/safety/inspection/statistics")
+public class SafetyInspectionStatisticsController {
+
+    @Autowired
+    private SafetyInspectionRecordService recordService;
+
+    /**
+     * 鏌ヨ浠婃棩宸℃缁熻銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宸℃缁熻-浠婃棩缁熻", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ浠婃棩宸℃缁熻")
+    @GetMapping("/today")
+    public AjaxResult todayStatistics() {
+        return AjaxResult.success(recordService.getTodayStatistics());
+    }
+
+    /**
+     * 鏌ヨ宸℃瓒嬪娍缁熻銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宸℃缁熻-瓒嬪娍缁熻", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ宸℃瓒嬪娍缁熻")
+    @GetMapping("/trend")
+    public AjaxResult trendStatistics(@RequestParam(value = "startDate", required = false) String startDate,
+                                      @RequestParam(value = "endDate", required = false) String endDate) {
+        return AjaxResult.success(recordService.getTrendStatistics(startDate, endDate));
+    }
+
+    /**
+     * 鏌ヨ宸℃绫诲瀷鍒嗗竷銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宸℃缁熻-绫诲瀷鍒嗗竷", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ宸℃绫诲瀷鍒嗗竷")
+    @GetMapping("/type")
+    public AjaxResult typeStatistics(@RequestParam(value = "startDate", required = false) String startDate,
+                                     @RequestParam(value = "endDate", required = false) String endDate) {
+        return AjaxResult.success(recordService.getTypeStatistics(startDate, endDate));
+    }
+
+    /**
+     * 鏌ヨ宸℃浜哄憳灞ヨ亴缁熻銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宸℃缁熻-浜哄憳灞ヨ亴", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ宸℃浜哄憳灞ヨ亴缁熻")
+    @GetMapping("/inspector")
+    public AjaxResult inspectorStatistics(@RequestParam(value = "startDate", required = false) String startDate,
+                                          @RequestParam(value = "endDate", required = false) String endDate) {
+        return AjaxResult.success(recordService.getInspectorStatistics(startDate, endDate));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyLearningRecordController.java b/src/main/java/com/ruoyi/safety/controller/SafetyLearningRecordController.java
new file mode 100644
index 0000000..bc1268b
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyLearningRecordController.java
@@ -0,0 +1,46 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyLearningRecord;
+import com.ruoyi.safety.service.SafetyLearningRecordService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-鍛樺伐瀛︿範璁板綍鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-鍛樺伐瀛︿範璁板綍")
+@RequestMapping("/safety/learning/record")
+public class SafetyLearningRecordController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyLearningRecordService learningRecordService;
+
+    /**
+     * 鍒嗛〉鏌ヨ鍛樺伐瀛︿範璁板綍銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍛樺伐瀛︿範璁板綍-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ鍛樺伐瀛︿範璁板綍")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyLearningRecord> page, SafetyLearningRecord query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(learningRecordService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 鏌ヨ鍛樺伐瀛︿範缁熻銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍛樺伐瀛︿範璁板綍-缁熻", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ鍛樺伐瀛︿範缁熻")
+    @GetMapping("/statistics")
+    public AjaxResult statistics() {
+        return AjaxResult.success(learningRecordService.getStatistics());
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyPersonnelController.java b/src/main/java/com/ruoyi/safety/controller/SafetyPersonnelController.java
new file mode 100644
index 0000000..89e0d17
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyPersonnelController.java
@@ -0,0 +1,76 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyPersonnel;
+import com.ruoyi.safety.service.SafetyPersonnelService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-浜哄憳妗f鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-浜哄憳妗f")
+@RequestMapping("/safety/personnel")
+public class SafetyPersonnelController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyPersonnelService personnelService;
+
+    /**
+     * 鍒嗛〉鏌ヨ浜哄憳妗f銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浜哄憳妗f-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ浜哄憳妗f")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyPersonnel> page, SafetyPersonnel query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(personnelService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 鏂板浜哄憳妗f銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浜哄憳妗f-鏂板", businessType = BusinessType.INSERT)
+    @ApiOperation("鏂板浜哄憳妗f")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody SafetyPersonnel personnel) {
+        return toAjax(personnelService.saveSafety(personnel));
+    }
+
+    /**
+     * 淇敼浜哄憳妗f銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浜哄憳妗f-淇敼", businessType = BusinessType.UPDATE)
+    @ApiOperation("淇敼浜哄憳妗f")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyPersonnel personnel) {
+        return toAjax(personnelService.updateSafety(personnel));
+    }
+
+    /**
+     * 鍒犻櫎浜哄憳妗f銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浜哄憳妗f-鍒犻櫎", businessType = BusinessType.DELETE)
+    @ApiOperation("鍒犻櫎浜哄憳妗f")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(personnelService.deleteSafetyById(id));
+    }
+
+    /**
+     * 鏌ヨ浜哄憳妗f璇︽儏銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浜哄憳妗f-璇︽儏", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ浜哄憳妗f璇︽儏")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(personnelService.getById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyRiskController.java b/src/main/java/com/ruoyi/safety/controller/SafetyRiskController.java
new file mode 100644
index 0000000..6fd66da
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyRiskController.java
@@ -0,0 +1,76 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyRisk;
+import com.ruoyi.safety.service.SafetyRiskService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-宀椾綅椋庨櫓鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-宀椾綅椋庨櫓")
+@RequestMapping("/safety/risk")
+public class SafetyRiskController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyRiskService riskService;
+
+    /**
+     * 鍒嗛〉鏌ヨ宀椾綅椋庨櫓銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宀椾綅椋庨櫓-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ宀椾綅椋庨櫓")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyRisk> page, SafetyRisk query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(riskService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 鏂板宀椾綅椋庨櫓銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宀椾綅椋庨櫓-鏂板", businessType = BusinessType.INSERT)
+    @ApiOperation("鏂板宀椾綅椋庨櫓")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody SafetyRisk risk) {
+        return toAjax(riskService.saveSafety(risk));
+    }
+
+    /**
+     * 淇敼宀椾綅椋庨櫓銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宀椾綅椋庨櫓-淇敼", businessType = BusinessType.UPDATE)
+    @ApiOperation("淇敼宀椾綅椋庨櫓")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyRisk risk) {
+        return toAjax(riskService.updateSafety(risk));
+    }
+
+    /**
+     * 鍒犻櫎宀椾綅椋庨櫓銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宀椾綅椋庨櫓-鍒犻櫎", businessType = BusinessType.DELETE)
+    @ApiOperation("鍒犻櫎宀椾綅椋庨櫓")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(riskService.deleteSafetyById(id));
+    }
+
+    /**
+     * 鏌ヨ宀椾綅椋庨櫓璇︽儏銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-宀椾綅椋庨櫓-璇︽儏", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ宀椾綅椋庨櫓璇︽儏")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(riskService.getById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyTrainingMaterialController.java b/src/main/java/com/ruoyi/safety/controller/SafetyTrainingMaterialController.java
new file mode 100644
index 0000000..8db910a
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyTrainingMaterialController.java
@@ -0,0 +1,76 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyTrainingMaterial;
+import com.ruoyi.safety.service.SafetyTrainingMaterialService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-鍩硅璧勬枡鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-鍩硅璧勬枡")
+@RequestMapping("/safety/training/material")
+public class SafetyTrainingMaterialController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyTrainingMaterialService materialService;
+
+    /**
+     * 鍒嗛〉鏌ヨ鍩硅璧勬枡銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璧勬枡-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ鍩硅璧勬枡")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyTrainingMaterial> page, SafetyTrainingMaterial query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(materialService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 涓婁紶鍩硅璧勬枡鍏冩暟鎹��
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璧勬枡-涓婁紶", businessType = BusinessType.INSERT)
+    @ApiOperation("涓婁紶鍩硅璧勬枡")
+    @PostMapping("/upload")
+    public AjaxResult upload(@RequestBody SafetyTrainingMaterial material) {
+        return toAjax(materialService.saveSafety(material));
+    }
+
+    /**
+     * 淇敼鍩硅璧勬枡銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璧勬枡-淇敼", businessType = BusinessType.UPDATE)
+    @ApiOperation("淇敼鍩硅璧勬枡")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyTrainingMaterial material) {
+        return toAjax(materialService.updateSafety(material));
+    }
+
+    /**
+     * 鍒犻櫎鍩硅璧勬枡銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璧勬枡-鍒犻櫎", businessType = BusinessType.DELETE)
+    @ApiOperation("鍒犻櫎鍩硅璧勬枡")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(materialService.deleteSafetyById(id));
+    }
+
+    /**
+     * 鏌ヨ鍩硅璧勬枡璇︽儏銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璧勬枡-璇︽儏", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ鍩硅璧勬枡璇︽儏")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(materialService.getById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyTrainingPlanController.java b/src/main/java/com/ruoyi/safety/controller/SafetyTrainingPlanController.java
new file mode 100644
index 0000000..f049d78
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyTrainingPlanController.java
@@ -0,0 +1,76 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyTrainingPlan;
+import com.ruoyi.safety.service.SafetyTrainingPlanService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-鍩硅璁″垝鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-鍩硅璁″垝")
+@RequestMapping("/safety/training/plan")
+public class SafetyTrainingPlanController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyTrainingPlanService planService;
+
+    /**
+     * 鍒嗛〉鏌ヨ鍩硅璁″垝銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璁″垝-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ鍩硅璁″垝")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyTrainingPlan> page, SafetyTrainingPlan query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(planService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 鏂板鍩硅璁″垝銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璁″垝-鏂板", businessType = BusinessType.INSERT)
+    @ApiOperation("鏂板鍩硅璁″垝")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody SafetyTrainingPlan plan) {
+        return toAjax(planService.saveSafety(plan));
+    }
+
+    /**
+     * 淇敼鍩硅璁″垝銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璁″垝-淇敼", businessType = BusinessType.UPDATE)
+    @ApiOperation("淇敼鍩硅璁″垝")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyTrainingPlan plan) {
+        return toAjax(planService.updateSafety(plan));
+    }
+
+    /**
+     * 鍒犻櫎鍩硅璁″垝銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璁″垝-鍒犻櫎", businessType = BusinessType.DELETE)
+    @ApiOperation("鍒犻櫎鍩硅璁″垝")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(planService.deleteSafetyById(id));
+    }
+
+    /**
+     * 鏌ヨ鍩硅璁″垝璇︽儏銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅璁″垝-璇︽儏", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ鍩硅璁″垝璇︽儏")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(planService.getById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyTrainingRecordController.java b/src/main/java/com/ruoyi/safety/controller/SafetyTrainingRecordController.java
new file mode 100644
index 0000000..9a4a16a
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyTrainingRecordController.java
@@ -0,0 +1,93 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyTrainingRecord;
+import com.ruoyi.safety.service.SafetyTrainingRecordService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 瀹夌幆绠$悊-鍩硅瀹屾垚璁板綍鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-鍩硅瀹屾垚璁板綍")
+@RequestMapping("/safety/training/record")
+public class SafetyTrainingRecordController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyTrainingRecordService recordService;
+
+    /**
+     * 鍒嗛〉鏌ヨ鍩硅瀹屾垚璁板綍銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅瀹屾垚璁板綍-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ鍩硅瀹屾垚璁板綍")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyTrainingRecord> page, SafetyTrainingRecord query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(recordService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * add training completion record.
+     */
+    @Log(title = "safety-training-record-add", businessType = BusinessType.INSERT)
+    @ApiOperation("add safety training completion record")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody SafetyTrainingRecord record) {
+        return toAjax(recordService.saveSafety(record));
+    }
+
+    /**
+     * update training completion record.
+     */
+    @Log(title = "safety-training-record-update", businessType = BusinessType.UPDATE)
+    @ApiOperation("update safety training completion record")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyTrainingRecord record) {
+        return toAjax(recordService.updateSafety(record));
+    }
+
+    /**
+     * delete training completion record.
+     */
+    @Log(title = "safety-training-record-delete", businessType = BusinessType.DELETE)
+    @ApiOperation("delete safety training completion record")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(recordService.deleteSafetyById(id));
+    }
+
+    /**
+     * training completion record detail.
+     */
+    @Log(title = "safety-training-record-detail", businessType = BusinessType.OTHER)
+    @ApiOperation("query safety training completion record detail")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(recordService.getById(id));
+    }
+
+    /**
+     * 瀵煎嚭鍩硅瀹屾垚璁板綍銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-鍩硅瀹屾垚璁板綍-瀵煎嚭", businessType = BusinessType.EXPORT)
+    @ApiOperation("瀵煎嚭鍩硅瀹屾垚璁板綍")
+    @GetMapping("/export")
+    public void export(HttpServletResponse response, SafetyTrainingRecord query) {
+        List<SafetyTrainingRecord> records = recordService.queryPage(new Page<SafetyTrainingRecord>(1, -1), query)
+                .getRecords();
+        ExcelUtil<SafetyTrainingRecord> util = new ExcelUtil<SafetyTrainingRecord>(SafetyTrainingRecord.class);
+        util.exportExcel(response, records, "safety_training_record");
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/controller/SafetyWorkAreaController.java b/src/main/java/com/ruoyi/safety/controller/SafetyWorkAreaController.java
new file mode 100644
index 0000000..824b38f
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/controller/SafetyWorkAreaController.java
@@ -0,0 +1,87 @@
+package com.ruoyi.safety.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.safety.pojo.SafetyWorkArea;
+import com.ruoyi.safety.service.SafetyWorkAreaService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 瀹夌幆绠$悊-浣滀笟鍖哄煙鎺ュ彛銆�
+ */
+@RestController
+@Api(tags = "瀹夌幆绠$悊-浣滀笟鍖哄煙")
+@RequestMapping("/safety/workArea")
+public class SafetyWorkAreaController extends SafetyControllerSupport {
+
+    @Autowired
+    private SafetyWorkAreaService workAreaService;
+
+    /**
+     * 鍒嗛〉鏌ヨ浣滀笟鍖哄煙銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浣滀笟鍖哄煙-鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鍒嗛〉鏌ヨ浣滀笟鍖哄煙")
+    @GetMapping("/list")
+    public AjaxResult list(Page<SafetyWorkArea> page, SafetyWorkArea query,
+                           @RequestParam(value = "pageNum", required = false) Long pageNum,
+                           @RequestParam(value = "pageSize", required = false) Long pageSize) {
+        return AjaxResult.success(workAreaService.queryPage(buildPage(page, pageNum, pageSize), query));
+    }
+
+    /**
+     * 鏂板浣滀笟鍖哄煙銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浣滀笟鍖哄煙-鏂板", businessType = BusinessType.INSERT)
+    @ApiOperation("鏂板浣滀笟鍖哄煙")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody SafetyWorkArea workArea) {
+        return toAjax(workAreaService.saveSafety(workArea));
+    }
+
+    /**
+     * 淇敼浣滀笟鍖哄煙銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浣滀笟鍖哄煙-淇敼", businessType = BusinessType.UPDATE)
+    @ApiOperation("淇敼浣滀笟鍖哄煙")
+    @PutMapping("/update")
+    public AjaxResult update(@RequestBody SafetyWorkArea workArea) {
+        return toAjax(workAreaService.updateSafety(workArea));
+    }
+
+    /**
+     * 鍒犻櫎浣滀笟鍖哄煙銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浣滀笟鍖哄煙-鍒犻櫎", businessType = BusinessType.DELETE)
+    @ApiOperation("鍒犻櫎浣滀笟鍖哄煙")
+    @DeleteMapping("/delete/{id}")
+    public AjaxResult delete(@PathVariable Long id) {
+        return toAjax(workAreaService.deleteSafetyById(id));
+    }
+
+    /**
+     * 鏌ヨ浣滀笟鍖哄煙璇︽儏銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浣滀笟鍖哄煙-璇︽儏", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ浣滀笟鍖哄煙璇︽儏")
+    @GetMapping("/detail/{id}")
+    public AjaxResult detail(@PathVariable Long id) {
+        return AjaxResult.success(workAreaService.getById(id));
+    }
+
+    /**
+     * 鏌ヨ鍏ㄩ儴浣滀笟鍖哄煙锛岀敤浜庝笅鎷夐�夋嫨銆�
+     */
+    @Log(title = "瀹夌幆绠$悊-浣滀笟鍖哄煙-鍏ㄩ儴鏌ヨ", businessType = BusinessType.OTHER)
+    @ApiOperation("鏌ヨ鍏ㄩ儴浣滀笟鍖哄煙")
+    @GetMapping("/all")
+    public AjaxResult all() {
+        return AjaxResult.success(workAreaService.list(new QueryWrapper<SafetyWorkArea>().orderByDesc("create_time")));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTodayStatistics.java b/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTodayStatistics.java
new file mode 100644
index 0000000..cf8293b
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTodayStatistics.java
@@ -0,0 +1,23 @@
+package com.ruoyi.safety.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class SafetyInspectionTodayStatistics {
+
+    private Long totalCount;
+
+    private Long completedCount;
+
+    private Long abnormalCount;
+
+    private Long missedCount;
+
+    private Long unexecutedCount;
+
+    private BigDecimal completionRate;
+
+    private Long inspectorCount;
+}
diff --git a/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTrendStat.java b/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTrendStat.java
new file mode 100644
index 0000000..a07c626
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTrendStat.java
@@ -0,0 +1,21 @@
+package com.ruoyi.safety.dto;
+
+import lombok.Data;
+
+import java.time.LocalDate;
+
+@Data
+public class SafetyInspectionTrendStat {
+
+    private LocalDate statDate;
+
+    private Long totalCount;
+
+    private Long completedCount;
+
+    private Long abnormalCount;
+
+    private Long missedCount;
+
+    private Long unexecutedCount;
+}
diff --git a/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTypeStat.java b/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTypeStat.java
new file mode 100644
index 0000000..15b7e27
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/dto/SafetyInspectionTypeStat.java
@@ -0,0 +1,11 @@
+package com.ruoyi.safety.dto;
+
+import lombok.Data;
+
+@Data
+public class SafetyInspectionTypeStat {
+
+    private String type;
+
+    private Long count;
+}
diff --git a/src/main/java/com/ruoyi/safety/dto/SafetyInspectorStat.java b/src/main/java/com/ruoyi/safety/dto/SafetyInspectorStat.java
new file mode 100644
index 0000000..b1097e7
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/dto/SafetyInspectorStat.java
@@ -0,0 +1,25 @@
+package com.ruoyi.safety.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class SafetyInspectorStat {
+
+    private Long inspectorId;
+
+    private String inspector;
+
+    private Long totalCount;
+
+    private Long completedCount;
+
+    private Long abnormalCount;
+
+    private Long missedCount;
+
+    private Long unexecutedCount;
+
+    private BigDecimal completionRate;
+}
diff --git a/src/main/java/com/ruoyi/safety/dto/SafetyLearningStatistics.java b/src/main/java/com/ruoyi/safety/dto/SafetyLearningStatistics.java
new file mode 100644
index 0000000..515c921
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/dto/SafetyLearningStatistics.java
@@ -0,0 +1,19 @@
+package com.ruoyi.safety.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class SafetyLearningStatistics {
+
+    private Long totalCount;
+
+    private Long completedCount;
+
+    private Long unfinishedCount;
+
+    private BigDecimal totalDuration;
+
+    private BigDecimal completionRate;
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentReportMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentReportMapper.java
new file mode 100644
index 0000000..9f290a5
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentReportMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyAssessmentReport;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyAssessmentReportMapper extends BaseMapper<SafetyAssessmentReport> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentTaskEmployeeMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentTaskEmployeeMapper.java
new file mode 100644
index 0000000..aac8ca7
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentTaskEmployeeMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyAssessmentTaskEmployee;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyAssessmentTaskEmployeeMapper extends BaseMapper<SafetyAssessmentTaskEmployee> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentTaskMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentTaskMapper.java
new file mode 100644
index 0000000..da30607
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyAssessmentTaskMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyAssessmentTask;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyAssessmentTaskMapper extends BaseMapper<SafetyAssessmentTask> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyEmergencyResourceMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyEmergencyResourceMapper.java
new file mode 100644
index 0000000..db9e48f
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyEmergencyResourceMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyEmergencyResource;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyEmergencyResourceMapper extends BaseMapper<SafetyEmergencyResource> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyEquipmentMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyEquipmentMapper.java
new file mode 100644
index 0000000..1d96f6c
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyEquipmentMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyEquipment;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyEquipmentMapper extends BaseMapper<SafetyEquipment> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyInspectionRecordMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyInspectionRecordMapper.java
new file mode 100644
index 0000000..50730d6
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyInspectionRecordMapper.java
@@ -0,0 +1,28 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.dto.SafetyInspectionTodayStatistics;
+import com.ruoyi.safety.dto.SafetyInspectionTrendStat;
+import com.ruoyi.safety.dto.SafetyInspectionTypeStat;
+import com.ruoyi.safety.dto.SafetyInspectorStat;
+import com.ruoyi.safety.pojo.SafetyInspectionRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Mapper
+public interface SafetyInspectionRecordMapper extends BaseMapper<SafetyInspectionRecord> {
+
+    SafetyInspectionTodayStatistics selectTodayStatistics(@Param("statDate") LocalDate statDate);
+
+    List<SafetyInspectionTrendStat> selectTrendStatistics(@Param("startDate") String startDate,
+                                                          @Param("endDate") String endDate);
+
+    List<SafetyInspectionTypeStat> selectTypeStatistics(@Param("startDate") String startDate,
+                                                        @Param("endDate") String endDate);
+
+    List<SafetyInspectorStat> selectInspectorStatistics(@Param("startDate") String startDate,
+                                                        @Param("endDate") String endDate);
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyLearningRecordMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyLearningRecordMapper.java
new file mode 100644
index 0000000..71ef33a
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyLearningRecordMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.dto.SafetyLearningStatistics;
+import com.ruoyi.safety.pojo.SafetyLearningRecord;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyLearningRecordMapper extends BaseMapper<SafetyLearningRecord> {
+
+    SafetyLearningStatistics selectStatistics();
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyPersonnelMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyPersonnelMapper.java
new file mode 100644
index 0000000..37424df
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyPersonnelMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyPersonnel;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyPersonnelMapper extends BaseMapper<SafetyPersonnel> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyRiskMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyRiskMapper.java
new file mode 100644
index 0000000..55b2113
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyRiskMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyRisk;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyRiskMapper extends BaseMapper<SafetyRisk> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingMaterialMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingMaterialMapper.java
new file mode 100644
index 0000000..ca8aeca
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingMaterialMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyTrainingMaterial;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyTrainingMaterialMapper extends BaseMapper<SafetyTrainingMaterial> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingPlanMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingPlanMapper.java
new file mode 100644
index 0000000..47ad722
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingPlanMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyTrainingPlan;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyTrainingPlanMapper extends BaseMapper<SafetyTrainingPlan> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingRecordMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingRecordMapper.java
new file mode 100644
index 0000000..a378703
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyTrainingRecordMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyTrainingRecord;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyTrainingRecordMapper extends BaseMapper<SafetyTrainingRecord> {
+}
diff --git a/src/main/java/com/ruoyi/safety/mapper/SafetyWorkAreaMapper.java b/src/main/java/com/ruoyi/safety/mapper/SafetyWorkAreaMapper.java
new file mode 100644
index 0000000..679d06b
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/mapper/SafetyWorkAreaMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safety.pojo.SafetyWorkArea;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SafetyWorkAreaMapper extends BaseMapper<SafetyWorkArea> {
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentReport.java b/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentReport.java
new file mode 100644
index 0000000..6d210ef
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentReport.java
@@ -0,0 +1,32 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_assessment_report")
+public class SafetyAssessmentReport extends SafetyBaseEntity {
+
+    private Long employeeId;
+
+    private String employeeName;
+
+    private String name;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime assessTime;
+
+    private BigDecimal score;
+
+    private String grade;
+
+    private Integer qualified;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentTask.java b/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentTask.java
new file mode 100644
index 0000000..d8401bc
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentTask.java
@@ -0,0 +1,25 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_assessment_task")
+public class SafetyAssessmentTask extends SafetyBaseEntity {
+
+    private String title;
+
+    private String description;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate deadline;
+
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentTaskEmployee.java b/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentTaskEmployee.java
new file mode 100644
index 0000000..68396f6
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyAssessmentTaskEmployee.java
@@ -0,0 +1,30 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_assessment_task_employee")
+public class SafetyAssessmentTaskEmployee extends SafetyBaseEntity {
+
+    private Long taskId;
+
+    private Long employeeId;
+
+    private String employeeName;
+
+    private Integer status;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime completeTime;
+
+    private BigDecimal score;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyBaseEntity.java b/src/main/java/com/ruoyi/safety/pojo/SafetyBaseEntity.java
new file mode 100644
index 0000000..15114a1
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyBaseEntity.java
@@ -0,0 +1,43 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+public class SafetyBaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("create_by")
+    private String createBy;
+
+    @TableField(condition = "create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @TableField("update_by")
+    private String updateBy;
+
+    @TableField(condition = "update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    private String remark;
+
+    @TableField("del_flag")
+    @TableLogic(value = "0", delval = "1")
+    private Integer delFlag;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyEmergencyResource.java b/src/main/java/com/ruoyi/safety/pojo/SafetyEmergencyResource.java
new file mode 100644
index 0000000..f90e6f3
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyEmergencyResource.java
@@ -0,0 +1,25 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_emergency_resource")
+public class SafetyEmergencyResource extends SafetyBaseEntity {
+
+    private String name;
+
+    private String type;
+
+    private Integer quantity;
+
+    private String location;
+
+    private Long areaId;
+
+    private String manager;
+
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyEquipment.java b/src/main/java/com/ruoyi/safety/pojo/SafetyEquipment.java
new file mode 100644
index 0000000..9a52c86
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyEquipment.java
@@ -0,0 +1,21 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_equipment")
+public class SafetyEquipment extends SafetyBaseEntity {
+
+    private String name;
+
+    private String model;
+
+    private Long areaId;
+
+    private String areaName;
+
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyInspectionRecord.java b/src/main/java/com/ruoyi/safety/pojo/SafetyInspectionRecord.java
new file mode 100644
index 0000000..7ac5e17
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyInspectionRecord.java
@@ -0,0 +1,35 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_inspection_record")
+public class SafetyInspectionRecord extends SafetyBaseEntity {
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime inspectionTime;
+
+    private Long inspectorId;
+
+    private String inspector;
+
+    private Long areaId;
+
+    private String area;
+
+    private String type;
+
+    private Integer status;
+
+    private String abnormalDesc;
+
+    private Integer isMissed;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyLearningRecord.java b/src/main/java/com/ruoyi/safety/pojo/SafetyLearningRecord.java
new file mode 100644
index 0000000..665f1cd
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyLearningRecord.java
@@ -0,0 +1,32 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_learning_record")
+public class SafetyLearningRecord extends SafetyBaseEntity {
+
+    private Long employeeId;
+
+    private String employeeName;
+
+    private String content;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime trainingTime;
+
+    private BigDecimal duration;
+
+    private String method;
+
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyPersonnel.java b/src/main/java/com/ruoyi/safety/pojo/SafetyPersonnel.java
new file mode 100644
index 0000000..ad23de7
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyPersonnel.java
@@ -0,0 +1,29 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_personnel")
+public class SafetyPersonnel extends SafetyBaseEntity {
+
+    private String name;
+
+    private String dept;
+
+    private String post;
+
+    private String phone;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate entryDate;
+
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyRisk.java b/src/main/java/com/ruoyi/safety/pojo/SafetyRisk.java
new file mode 100644
index 0000000..3b34074
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyRisk.java
@@ -0,0 +1,21 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_risk")
+public class SafetyRisk extends SafetyBaseEntity {
+
+    private String type;
+
+    private String level;
+
+    private String description;
+
+    private String post;
+
+    private String controlMeasures;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingMaterial.java b/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingMaterial.java
new file mode 100644
index 0000000..1a4cc94
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingMaterial.java
@@ -0,0 +1,33 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_training_material")
+public class SafetyTrainingMaterial extends SafetyBaseEntity {
+
+    private String name;
+
+    private String type;
+
+    private String fileUrl;
+
+    private String fileSize;
+
+    private String uploader;
+
+    private Long uploaderId;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime uploadTime;
+
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingPlan.java b/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingPlan.java
new file mode 100644
index 0000000..10b29ee
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingPlan.java
@@ -0,0 +1,30 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_training_plan")
+public class SafetyTrainingPlan extends SafetyBaseEntity {
+
+    @TableField("`year`")
+    private String year;
+
+    private String post;
+
+    @TableField("`level`")
+    private String level;
+
+    private String content;
+
+    private BigDecimal hours;
+
+    private String materialIds;
+
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingRecord.java b/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingRecord.java
new file mode 100644
index 0000000..aae7257
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyTrainingRecord.java
@@ -0,0 +1,38 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_training_record")
+public class SafetyTrainingRecord extends SafetyBaseEntity {
+
+    private Long planId;
+
+    private Long employeeId;
+
+    private String employeeName;
+
+    private String content;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime completeTime;
+
+    private BigDecimal duration;
+
+    private String method;
+
+    private String result;
+
+    private BigDecimal score;
+
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/safety/pojo/SafetyWorkArea.java b/src/main/java/com/ruoyi/safety/pojo/SafetyWorkArea.java
new file mode 100644
index 0000000..8657f80
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/pojo/SafetyWorkArea.java
@@ -0,0 +1,19 @@
+package com.ruoyi.safety.pojo;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("safety_work_area")
+public class SafetyWorkArea extends SafetyBaseEntity {
+
+    private String name;
+
+    private String location;
+
+    private String manager;
+
+    private Long managerId;
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyAssessmentReportService.java b/src/main/java/com/ruoyi/safety/service/SafetyAssessmentReportService.java
new file mode 100644
index 0000000..0a8b7ec
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyAssessmentReportService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyAssessmentReport;
+
+public interface SafetyAssessmentReportService extends SafetyBaseService<SafetyAssessmentReport> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyAssessmentTaskEmployeeService.java b/src/main/java/com/ruoyi/safety/service/SafetyAssessmentTaskEmployeeService.java
new file mode 100644
index 0000000..64970c1
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyAssessmentTaskEmployeeService.java
@@ -0,0 +1,10 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyAssessmentTaskEmployee;
+
+public interface SafetyAssessmentTaskEmployeeService extends SafetyBaseService<SafetyAssessmentTaskEmployee> {
+
+    boolean start(SafetyAssessmentTaskEmployee request);
+
+    boolean submit(SafetyAssessmentTaskEmployee request);
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyAssessmentTaskService.java b/src/main/java/com/ruoyi/safety/service/SafetyAssessmentTaskService.java
new file mode 100644
index 0000000..e5f5906
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyAssessmentTaskService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyAssessmentTask;
+
+public interface SafetyAssessmentTaskService extends SafetyBaseService<SafetyAssessmentTask> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyBaseService.java b/src/main/java/com/ruoyi/safety/service/SafetyBaseService.java
new file mode 100644
index 0000000..5337f6c
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyBaseService.java
@@ -0,0 +1,17 @@
+package com.ruoyi.safety.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.safety.pojo.SafetyBaseEntity;
+
+public interface SafetyBaseService<T extends SafetyBaseEntity> extends IService<T> {
+
+    IPage<T> queryPage(Page<T> page, T query);
+
+    boolean saveSafety(T entity);
+
+    boolean updateSafety(T entity);
+
+    boolean deleteSafetyById(Long id);
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyEmergencyResourceService.java b/src/main/java/com/ruoyi/safety/service/SafetyEmergencyResourceService.java
new file mode 100644
index 0000000..9de49d6
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyEmergencyResourceService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyEmergencyResource;
+
+public interface SafetyEmergencyResourceService extends SafetyBaseService<SafetyEmergencyResource> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyEquipmentService.java b/src/main/java/com/ruoyi/safety/service/SafetyEquipmentService.java
new file mode 100644
index 0000000..a90da2b
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyEquipmentService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyEquipment;
+
+public interface SafetyEquipmentService extends SafetyBaseService<SafetyEquipment> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyInspectionRecordService.java b/src/main/java/com/ruoyi/safety/service/SafetyInspectionRecordService.java
new file mode 100644
index 0000000..87b4590
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyInspectionRecordService.java
@@ -0,0 +1,29 @@
+package com.ruoyi.safety.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.dto.SafetyInspectionTodayStatistics;
+import com.ruoyi.safety.dto.SafetyInspectionTrendStat;
+import com.ruoyi.safety.dto.SafetyInspectionTypeStat;
+import com.ruoyi.safety.dto.SafetyInspectorStat;
+import com.ruoyi.safety.pojo.SafetyInspectionRecord;
+
+import java.util.List;
+
+public interface SafetyInspectionRecordService extends SafetyBaseService<SafetyInspectionRecord> {
+
+    IPage<SafetyInspectionRecord> queryPage(Page<SafetyInspectionRecord> page,
+                                            SafetyInspectionRecord query,
+                                            String startDate,
+                                            String endDate);
+
+    SafetyInspectionTodayStatistics getTodayStatistics();
+
+    List<SafetyInspectionTrendStat> getTrendStatistics(String startDate, String endDate);
+
+    List<SafetyInspectionTypeStat> getTypeStatistics(String startDate, String endDate);
+
+    List<SafetyInspectorStat> getInspectorStatistics(String startDate, String endDate);
+
+    List<SafetyInspectionRecord> syncMockSubsystemData();
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyLearningRecordService.java b/src/main/java/com/ruoyi/safety/service/SafetyLearningRecordService.java
new file mode 100644
index 0000000..258650f
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyLearningRecordService.java
@@ -0,0 +1,9 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.dto.SafetyLearningStatistics;
+import com.ruoyi.safety.pojo.SafetyLearningRecord;
+
+public interface SafetyLearningRecordService extends SafetyBaseService<SafetyLearningRecord> {
+
+    SafetyLearningStatistics getStatistics();
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyPersonnelService.java b/src/main/java/com/ruoyi/safety/service/SafetyPersonnelService.java
new file mode 100644
index 0000000..ab96aa0
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyPersonnelService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyPersonnel;
+
+public interface SafetyPersonnelService extends SafetyBaseService<SafetyPersonnel> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyRiskService.java b/src/main/java/com/ruoyi/safety/service/SafetyRiskService.java
new file mode 100644
index 0000000..fcfc8ee
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyRiskService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyRisk;
+
+public interface SafetyRiskService extends SafetyBaseService<SafetyRisk> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyTrainingMaterialService.java b/src/main/java/com/ruoyi/safety/service/SafetyTrainingMaterialService.java
new file mode 100644
index 0000000..7e0a9b6
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyTrainingMaterialService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyTrainingMaterial;
+
+public interface SafetyTrainingMaterialService extends SafetyBaseService<SafetyTrainingMaterial> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyTrainingPlanService.java b/src/main/java/com/ruoyi/safety/service/SafetyTrainingPlanService.java
new file mode 100644
index 0000000..fbfc4a5
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyTrainingPlanService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyTrainingPlan;
+
+public interface SafetyTrainingPlanService extends SafetyBaseService<SafetyTrainingPlan> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyTrainingRecordService.java b/src/main/java/com/ruoyi/safety/service/SafetyTrainingRecordService.java
new file mode 100644
index 0000000..e3d89da
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyTrainingRecordService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyTrainingRecord;
+
+public interface SafetyTrainingRecordService extends SafetyBaseService<SafetyTrainingRecord> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/SafetyWorkAreaService.java b/src/main/java/com/ruoyi/safety/service/SafetyWorkAreaService.java
new file mode 100644
index 0000000..4b1ba77
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/SafetyWorkAreaService.java
@@ -0,0 +1,6 @@
+package com.ruoyi.safety.service;
+
+import com.ruoyi.safety.pojo.SafetyWorkArea;
+
+public interface SafetyWorkAreaService extends SafetyBaseService<SafetyWorkArea> {
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentReportServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentReportServiceImpl.java
new file mode 100644
index 0000000..0e7a412
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentReportServiceImpl.java
@@ -0,0 +1,36 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyAssessmentReportMapper;
+import com.ruoyi.safety.pojo.SafetyAssessmentReport;
+import com.ruoyi.safety.service.SafetyAssessmentReportService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyAssessmentReportServiceImpl
+        extends SafetyBaseServiceImpl<SafetyAssessmentReportMapper, SafetyAssessmentReport>
+        implements SafetyAssessmentReportService {
+
+    @Override
+    public IPage<SafetyAssessmentReport> queryPage(Page<SafetyAssessmentReport> page, SafetyAssessmentReport query) {
+        QueryWrapper<SafetyAssessmentReport> wrapper =
+                new QueryWrapper<SafetyAssessmentReport>().orderByDesc("create_time");
+        if (query != null) {
+            if (query.getEmployeeId() != null) {
+                wrapper.eq("employee_id", query.getEmployeeId());
+            }
+            if (hasText(query.getEmployeeName())) {
+                wrapper.like("employee_name", query.getEmployeeName());
+            }
+            if (hasText(query.getName())) {
+                wrapper.like("name", query.getName());
+            }
+            if (query.getQualified() != null) {
+                wrapper.eq("qualified", query.getQualified());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentTaskEmployeeServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentTaskEmployeeServiceImpl.java
new file mode 100644
index 0000000..cef0b03
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentTaskEmployeeServiceImpl.java
@@ -0,0 +1,141 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyAssessmentTaskEmployeeMapper;
+import com.ruoyi.safety.pojo.SafetyAssessmentTask;
+import com.ruoyi.safety.pojo.SafetyAssessmentTaskEmployee;
+import com.ruoyi.safety.service.SafetyAssessmentTaskEmployeeService;
+import com.ruoyi.safety.service.SafetyAssessmentTaskService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+@Service
+public class SafetyAssessmentTaskEmployeeServiceImpl
+        extends SafetyBaseServiceImpl<SafetyAssessmentTaskEmployeeMapper, SafetyAssessmentTaskEmployee>
+        implements SafetyAssessmentTaskEmployeeService {
+
+    @Autowired
+    private SafetyAssessmentTaskService taskService;
+
+    @Override
+    public IPage<SafetyAssessmentTaskEmployee> queryPage(Page<SafetyAssessmentTaskEmployee> page,
+                                                         SafetyAssessmentTaskEmployee query) {
+        QueryWrapper<SafetyAssessmentTaskEmployee> wrapper =
+                new QueryWrapper<SafetyAssessmentTaskEmployee>().orderByDesc("create_time");
+        if (query != null) {
+            if (query.getTaskId() != null) {
+                wrapper.eq("task_id", query.getTaskId());
+            }
+            if (query.getEmployeeId() != null) {
+                wrapper.eq("employee_id", query.getEmployeeId());
+            }
+            if (hasText(query.getEmployeeName())) {
+                wrapper.like("employee_name", query.getEmployeeName());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+
+    @Override
+    public boolean start(SafetyAssessmentTaskEmployee request) {
+        if (request == null || request.getTaskId() == null) {
+            return false;
+        }
+        fillCurrentEmployeeIfMissing(request);
+        if (request.getEmployeeId() == null) {
+            return false;
+        }
+        SafetyAssessmentTaskEmployee existing = getOne(new QueryWrapper<SafetyAssessmentTaskEmployee>()
+                .eq("task_id", request.getTaskId())
+                .eq("employee_id", request.getEmployeeId())
+                .last("limit 1"));
+        if (existing != null) {
+            return true;
+        }
+        if (request.getStatus() == null) {
+            request.setStatus(0);
+        }
+        updateTaskStatus(request.getTaskId(), 1);
+        return saveSafety(request);
+    }
+
+    @Override
+    public boolean submit(SafetyAssessmentTaskEmployee request) {
+        if (request == null) {
+            return false;
+        }
+        fillCurrentEmployeeIfMissing(request);
+        SafetyAssessmentTaskEmployee target = findSubmitTarget(request);
+        if (target == null) {
+            if (request.getTaskId() == null || request.getEmployeeId() == null) {
+                return false;
+            }
+            request.setStatus(1);
+            if (request.getCompleteTime() == null) {
+                request.setCompleteTime(LocalDateTime.now());
+            }
+            boolean saved = saveSafety(request);
+            finishTaskWhenAllSubmitted(request.getTaskId());
+            return saved;
+        }
+        target.setStatus(1);
+        target.setScore(request.getScore());
+        target.setRemark(request.getRemark());
+        target.setCompleteTime(request.getCompleteTime() == null ? LocalDateTime.now() : request.getCompleteTime());
+        boolean updated = updateSafety(target);
+        finishTaskWhenAllSubmitted(target.getTaskId());
+        return updated;
+    }
+
+    private SafetyAssessmentTaskEmployee findSubmitTarget(SafetyAssessmentTaskEmployee request) {
+        if (request.getId() != null) {
+            SafetyAssessmentTaskEmployee byId = getById(request.getId());
+            if (byId != null) {
+                return byId;
+            }
+        }
+        if (request.getTaskId() != null && request.getEmployeeId() != null) {
+            return getOne(new QueryWrapper<SafetyAssessmentTaskEmployee>()
+                    .eq("task_id", request.getTaskId())
+                    .eq("employee_id", request.getEmployeeId())
+                    .last("limit 1"));
+        }
+        return null;
+    }
+
+    private void fillCurrentEmployeeIfMissing(SafetyAssessmentTaskEmployee request) {
+        if (request.getEmployeeId() == null) {
+            request.setEmployeeId(currentUserId());
+        }
+        if (!hasText(request.getEmployeeName())) {
+            request.setEmployeeName(currentUsername());
+        }
+    }
+
+    private void updateTaskStatus(Long taskId, Integer status) {
+        SafetyAssessmentTask task = taskService.getById(taskId);
+        if (task != null && (task.getStatus() == null || task.getStatus() < status)) {
+            task.setStatus(status);
+            taskService.updateSafety(task);
+        }
+    }
+
+    private void finishTaskWhenAllSubmitted(Long taskId) {
+        if (taskId == null) {
+            return;
+        }
+        long pending = count(new QueryWrapper<SafetyAssessmentTaskEmployee>()
+                .eq("task_id", taskId)
+                .eq("status", 0));
+        if (pending == 0) {
+            updateTaskStatus(taskId, 2);
+        }
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentTaskServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentTaskServiceImpl.java
new file mode 100644
index 0000000..0455817
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyAssessmentTaskServiceImpl.java
@@ -0,0 +1,33 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyAssessmentTaskMapper;
+import com.ruoyi.safety.pojo.SafetyAssessmentTask;
+import com.ruoyi.safety.service.SafetyAssessmentTaskService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyAssessmentTaskServiceImpl
+        extends SafetyBaseServiceImpl<SafetyAssessmentTaskMapper, SafetyAssessmentTask>
+        implements SafetyAssessmentTaskService {
+
+    @Override
+    public IPage<SafetyAssessmentTask> queryPage(Page<SafetyAssessmentTask> page, SafetyAssessmentTask query) {
+        QueryWrapper<SafetyAssessmentTask> wrapper =
+                new QueryWrapper<SafetyAssessmentTask>().orderByDesc("create_time");
+        if (query != null) {
+            if (hasText(query.getTitle())) {
+                wrapper.like("title", query.getTitle());
+            }
+            if (query.getDeadline() != null) {
+                wrapper.eq("deadline", query.getDeadline());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyBaseServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyBaseServiceImpl.java
new file mode 100644
index 0000000..406d477
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyBaseServiceImpl.java
@@ -0,0 +1,57 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.safety.pojo.SafetyBaseEntity;
+import com.ruoyi.safety.service.SafetyBaseService;
+import org.apache.commons.lang3.StringUtils;
+
+import java.time.LocalDateTime;
+
+public abstract class SafetyBaseServiceImpl<M extends BaseMapper<T>, T extends SafetyBaseEntity>
+        extends ServiceImpl<M, T> implements SafetyBaseService<T> {
+
+    @Override
+    public boolean saveSafety(T entity) {
+        entity.setCreateBy(currentUsername());
+        entity.setCreateTime(LocalDateTime.now());
+        if (entity.getDelFlag() == null) {
+            entity.setDelFlag(0);
+        }
+        return save(entity);
+    }
+
+    @Override
+    public boolean updateSafety(T entity) {
+        entity.setUpdateBy(currentUsername());
+        entity.setUpdateTime(LocalDateTime.now());
+        return updateById(entity);
+    }
+
+    @Override
+    public boolean deleteSafetyById(Long id) {
+        return removeById(id);
+    }
+
+    protected boolean hasText(String value) {
+        return StringUtils.isNotBlank(value);
+    }
+
+    protected String currentUsername() {
+        try {
+            return SecurityUtils.getLoginUser().getNickName();
+        } catch (Exception ignored) {
+            return null;
+        }
+    }
+
+    protected Long currentUserId() {
+        try {
+            return SecurityUtils.getUserId();
+        } catch (Exception ignored) {
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyEmergencyResourceServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyEmergencyResourceServiceImpl.java
new file mode 100644
index 0000000..164be2b
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyEmergencyResourceServiceImpl.java
@@ -0,0 +1,37 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyEmergencyResourceMapper;
+import com.ruoyi.safety.pojo.SafetyEmergencyResource;
+import com.ruoyi.safety.service.SafetyEmergencyResourceService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyEmergencyResourceServiceImpl
+        extends SafetyBaseServiceImpl<SafetyEmergencyResourceMapper, SafetyEmergencyResource>
+        implements SafetyEmergencyResourceService {
+
+    @Override
+    public IPage<SafetyEmergencyResource> queryPage(Page<SafetyEmergencyResource> page,
+                                                    SafetyEmergencyResource query) {
+        QueryWrapper<SafetyEmergencyResource> wrapper =
+                new QueryWrapper<SafetyEmergencyResource>().orderByDesc("create_time");
+        if (query != null) {
+            if (hasText(query.getName())) {
+                wrapper.like("name", query.getName());
+            }
+            if (hasText(query.getType())) {
+                wrapper.like("type", query.getType());
+            }
+            if (query.getAreaId() != null) {
+                wrapper.eq("area_id", query.getAreaId());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyEquipmentServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyEquipmentServiceImpl.java
new file mode 100644
index 0000000..40fc090
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyEquipmentServiceImpl.java
@@ -0,0 +1,34 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyEquipmentMapper;
+import com.ruoyi.safety.pojo.SafetyEquipment;
+import com.ruoyi.safety.service.SafetyEquipmentService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyEquipmentServiceImpl extends SafetyBaseServiceImpl<SafetyEquipmentMapper, SafetyEquipment>
+        implements SafetyEquipmentService {
+
+    @Override
+    public IPage<SafetyEquipment> queryPage(Page<SafetyEquipment> page, SafetyEquipment query) {
+        QueryWrapper<SafetyEquipment> wrapper = new QueryWrapper<SafetyEquipment>().orderByDesc("create_time");
+        if (query != null) {
+            if (hasText(query.getName())) {
+                wrapper.like("name", query.getName());
+            }
+            if (hasText(query.getModel())) {
+                wrapper.like("model", query.getModel());
+            }
+            if (query.getAreaId() != null) {
+                wrapper.eq("area_id", query.getAreaId());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyInspectionRecordServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyInspectionRecordServiceImpl.java
new file mode 100644
index 0000000..cedb1fa
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyInspectionRecordServiceImpl.java
@@ -0,0 +1,194 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.framework.redis.RedisCache;
+import com.ruoyi.safety.dto.SafetyInspectionTodayStatistics;
+import com.ruoyi.safety.dto.SafetyInspectionTrendStat;
+import com.ruoyi.safety.dto.SafetyInspectionTypeStat;
+import com.ruoyi.safety.dto.SafetyInspectorStat;
+import com.ruoyi.safety.mapper.SafetyInspectionRecordMapper;
+import com.ruoyi.safety.pojo.SafetyInspectionRecord;
+import com.ruoyi.safety.service.SafetyInspectionRecordService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class SafetyInspectionRecordServiceImpl
+        extends SafetyBaseServiceImpl<SafetyInspectionRecordMapper, SafetyInspectionRecord>
+        implements SafetyInspectionRecordService {
+
+    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    private static final String SYNC_DAILY_KEY_PREFIX = "safety:inspection:sync:daily:";
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Override
+    public IPage<SafetyInspectionRecord> queryPage(Page<SafetyInspectionRecord> page,
+                                                   SafetyInspectionRecord query,
+                                                   String startDate,
+                                                   String endDate) {
+        QueryWrapper<SafetyInspectionRecord> wrapper =
+                new QueryWrapper<SafetyInspectionRecord>().orderByDesc("inspection_time");
+        if (query != null) {
+            if (query.getInspectorId() != null) {
+                wrapper.eq("inspector_id", query.getInspectorId());
+            }
+            if (hasText(query.getInspector())) {
+                wrapper.like("inspector", query.getInspector());
+            }
+            if (query.getAreaId() != null) {
+                wrapper.eq("area_id", query.getAreaId());
+            }
+            if (hasText(query.getArea())) {
+                wrapper.like("area", query.getArea());
+            }
+            if (hasText(query.getType())) {
+                wrapper.eq("type", query.getType());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        if (hasText(startDate)) {
+            wrapper.ge("inspection_time", startDate + " 00:00:00");
+        }
+        if (hasText(endDate)) {
+            wrapper.le("inspection_time", endDate + " 23:59:59");
+        }
+        return page(page, wrapper);
+    }
+
+    @Override
+    public IPage<SafetyInspectionRecord> queryPage(Page<SafetyInspectionRecord> page, SafetyInspectionRecord query) {
+        return queryPage(page, query, null, null);
+    }
+
+    @Override
+    public SafetyInspectionTodayStatistics getTodayStatistics() {
+        return baseMapper.selectTodayStatistics(LocalDate.now());
+    }
+
+    @Override
+    public List<SafetyInspectionTrendStat> getTrendStatistics(String startDate, String endDate) {
+        String[] range = normalizeRange(startDate, endDate);
+        return baseMapper.selectTrendStatistics(range[0], range[1]);
+    }
+
+    @Override
+    public List<SafetyInspectionTypeStat> getTypeStatistics(String startDate, String endDate) {
+        String[] range = normalizeRange(startDate, endDate);
+        return baseMapper.selectTypeStatistics(range[0], range[1]);
+    }
+
+    @Override
+    public List<SafetyInspectorStat> getInspectorStatistics(String startDate, String endDate) {
+        String[] range = normalizeRange(startDate, endDate);
+        return baseMapper.selectInspectorStatistics(range[0], range[1]);
+    }
+
+    @Override
+    public List<SafetyInspectionRecord> syncMockSubsystemData() {
+        String todaySyncKey = buildTodaySyncKey();
+        if (Boolean.TRUE.equals(redisCache.hasKey(todaySyncKey))) {
+            throw new ServiceException("浠婃棩宸插悓姝ワ紝鏄庡ぉ鍐嶈瘯");
+        }
+        List<SafetyInspectionRecord> mockRecords = buildMockRecords();
+        saveBatch(mockRecords);
+        int secondsToEndOfDay = secondsToEndOfDay();
+        redisCache.setCacheObject(todaySyncKey, "1", secondsToEndOfDay, TimeUnit.SECONDS);
+        return mockRecords;
+    }
+
+    private String[] normalizeRange(String startDate, String endDate) {
+        LocalDate end = hasText(endDate) ? LocalDate.parse(endDate, DATE_FORMATTER) : LocalDate.now();
+        LocalDate start = hasText(startDate) ? LocalDate.parse(startDate, DATE_FORMATTER) : end.minusDays(6);
+        return new String[]{start.format(DATE_FORMATTER), end.format(DATE_FORMATTER)};
+    }
+
+    private List<SafetyInspectionRecord> buildMockRecords() {
+        LocalDate today = LocalDate.now();
+        LocalDateTime now = LocalDateTime.now();
+        String operator = currentUsername();
+
+        List<SafetyInspectionRecord> records = new ArrayList<SafetyInspectionRecord>(18);
+        records.add(createMockRecord(today.minusDays(6), LocalTime.of(8, 30), 101L, "鏉庨潤", 1L, "澶у巺", "daily", 0, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(6), LocalTime.of(10, 20), 102L, "鏉庝笘鏉�", 2L, "鍓嶅彴", "fire", 1, 0, "\u706d\u706b\u5668\u538b\u529b\u4f4e", now, operator));
+        records.add(createMockRecord(today.minusDays(5), LocalTime.of(9, 15), 101L, "鐜嬩簹浼�", 3L, "澶у巺", "equipment", 0, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(5), LocalTime.of(14, 0), 103L, "鐜嬩簹浼�", 1L, "澶у巺", "daily", 3, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(4), LocalTime.of(8, 45), 102L, "鏉庝笘鏉�", 2L, "鏈虹數鎴�", "daily", 0, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(4), LocalTime.of(15, 30), 104L, "椤惧叏", 4L, "鏈烘埧", "night", 2, 1, "\u5de1\u68c0\u70b9A3\u6f0f\u68c0", now, operator));
+        records.add(createMockRecord(today.minusDays(3), LocalTime.of(9, 5), 101L, "鏉庨潤", 1L, "閿呯倝鎴�", "daily", 0, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(3), LocalTime.of(16, 20), 103L, "鐜嬩簹浼�", 3L, "鏈虹數鎴�", "fire", 1, 0, "\u5e94\u6025\u901a\u9053\u5806\u653e\u5835\u585e", now, operator));
+        records.add(createMockRecord(today.minusDays(2), LocalTime.of(8, 55), 102L, "鏉庝笘鏉�", 2L, "鏈虹數鎴�", "equipment", 0, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(2), LocalTime.of(13, 40), 104L, "椤惧叏", 4L, "鏈烘埧", "night", 3, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(1), LocalTime.of(8, 40), 101L, "鏉庨潤", 1L, "鏈烘埧", "daily", 0, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(1), LocalTime.of(10, 5), 102L, "鏉庝笘鏉�", 2L, "鍌ㄧ墿闂�", "daily", 0, 0, null, now, operator));
+        records.add(createMockRecord(today.minusDays(1), LocalTime.of(14, 35), 103L, "鐜嬩簹浼�", 3L, "鍌ㄧ墿闂�", "fire", 1, 0, "\u5b89\u5168\u6807\u8bc6\u7834\u635f", now, operator));
+        records.add(createMockRecord(today.minusDays(1), LocalTime.of(17, 10), 104L, "椤惧叏", 4L, "鍌ㄧ墿闂�", "equipment", 2, 1, "\u5de5\u5177\u7bb1\u70b9\u68c0\u6f0f\u68c0", now, operator));
+        records.add(createMockRecord(today, LocalTime.of(8, 20), 101L, "鏉庨潤", 1L, "浜屾ゼ娑堥槻闂�", "daily", 0, 0, null, now, operator));
+        records.add(createMockRecord(today, LocalTime.of(10, 10), 102L, "鏉庝笘鏉�", 2L, "浜屾ゼ娑堥槻闂�", "daily", 0, 0, null, now, operator));
+        records.add(createMockRecord(today, LocalTime.of(14, 25), 103L, "鐜嬩簹浼�", 3L, "浜屾ゼ娑堥槻闂�", "equipment", 0, 0, null, now, operator));
+        records.add(createMockRecord(today, LocalTime.of(16, 50), 104L, "椤惧叏", 4L, "澶у巺", "night", 3, 0, null, now, operator));
+        return records;
+    }
+
+    private SafetyInspectionRecord createMockRecord(LocalDate date,
+                                                    LocalTime time,
+                                                    Long inspectorId,
+                                                    String inspector,
+                                                    Long areaId,
+                                                    String area,
+                                                    String type,
+                                                    Integer status,
+                                                    Integer isMissed,
+                                                    String abnormalDesc,
+                                                    LocalDateTime createTime,
+                                                    String createBy) {
+        SafetyInspectionRecord record = new SafetyInspectionRecord();
+        record.setInspectionTime(LocalDateTime.of(date, time));
+        record.setInspectorId(inspectorId);
+        record.setInspector(inspector);
+        record.setAreaId(areaId);
+        record.setArea(area);
+        record.setType(type);
+        record.setStatus(status);
+        record.setIsMissed(isMissed);
+        record.setAbnormalDesc(abnormalDesc);
+        record.setRemark("mock-subsystem-sync");
+        record.setCreateTime(createTime);
+        record.setCreateBy(createBy);
+        record.setDelFlag(0);
+        return record;
+    }
+
+    private String buildTodaySyncKey() {
+        String tenantPart = "default";
+        try {
+            Long tenantId = com.ruoyi.common.utils.SecurityUtils.getLoginUser().getTenantId();
+            if (tenantId != null) {
+                tenantPart = String.valueOf(tenantId);
+            }
+        } catch (Exception ignored) {
+        }
+        return SYNC_DAILY_KEY_PREFIX + tenantPart + ":" + LocalDate.now().format(DATE_FORMATTER);
+    }
+
+    private int secondsToEndOfDay() {
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime endOfDay = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
+        long seconds = java.time.Duration.between(now, endOfDay).getSeconds();
+        return (int) Math.max(seconds, 1L);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyLearningRecordServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyLearningRecordServiceImpl.java
new file mode 100644
index 0000000..51a468a
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyLearningRecordServiceImpl.java
@@ -0,0 +1,42 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.dto.SafetyLearningStatistics;
+import com.ruoyi.safety.mapper.SafetyLearningRecordMapper;
+import com.ruoyi.safety.pojo.SafetyLearningRecord;
+import com.ruoyi.safety.service.SafetyLearningRecordService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyLearningRecordServiceImpl
+        extends SafetyBaseServiceImpl<SafetyLearningRecordMapper, SafetyLearningRecord>
+        implements SafetyLearningRecordService {
+
+    @Override
+    public IPage<SafetyLearningRecord> queryPage(Page<SafetyLearningRecord> page, SafetyLearningRecord query) {
+        QueryWrapper<SafetyLearningRecord> wrapper =
+                new QueryWrapper<SafetyLearningRecord>().orderByDesc("create_time");
+        if (query != null) {
+            if (query.getEmployeeId() != null) {
+                wrapper.eq("employee_id", query.getEmployeeId());
+            }
+            if (hasText(query.getEmployeeName())) {
+                wrapper.like("employee_name", query.getEmployeeName());
+            }
+            if (hasText(query.getContent())) {
+                wrapper.like("content", query.getContent());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+
+    @Override
+    public SafetyLearningStatistics getStatistics() {
+        return baseMapper.selectStatistics();
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyPersonnelServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyPersonnelServiceImpl.java
new file mode 100644
index 0000000..66fa3dd
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyPersonnelServiceImpl.java
@@ -0,0 +1,34 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyPersonnelMapper;
+import com.ruoyi.safety.pojo.SafetyPersonnel;
+import com.ruoyi.safety.service.SafetyPersonnelService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyPersonnelServiceImpl extends SafetyBaseServiceImpl<SafetyPersonnelMapper, SafetyPersonnel>
+        implements SafetyPersonnelService {
+
+    @Override
+    public IPage<SafetyPersonnel> queryPage(Page<SafetyPersonnel> page, SafetyPersonnel query) {
+        QueryWrapper<SafetyPersonnel> wrapper = new QueryWrapper<SafetyPersonnel>().orderByDesc("create_time");
+        if (query != null) {
+            if (hasText(query.getName())) {
+                wrapper.like("name", query.getName());
+            }
+            if (hasText(query.getDept())) {
+                wrapper.like("dept", query.getDept());
+            }
+            if (hasText(query.getPost())) {
+                wrapper.like("post", query.getPost());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyRiskServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyRiskServiceImpl.java
new file mode 100644
index 0000000..ade4070
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyRiskServiceImpl.java
@@ -0,0 +1,31 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyRiskMapper;
+import com.ruoyi.safety.pojo.SafetyRisk;
+import com.ruoyi.safety.service.SafetyRiskService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyRiskServiceImpl extends SafetyBaseServiceImpl<SafetyRiskMapper, SafetyRisk>
+        implements SafetyRiskService {
+
+    @Override
+    public IPage<SafetyRisk> queryPage(Page<SafetyRisk> page, SafetyRisk query) {
+        QueryWrapper<SafetyRisk> wrapper = new QueryWrapper<SafetyRisk>().orderByDesc("create_time");
+        if (query != null) {
+            if (hasText(query.getType())) {
+                wrapper.like("type", query.getType());
+            }
+            if (hasText(query.getLevel())) {
+                wrapper.eq("level", query.getLevel());
+            }
+            if (hasText(query.getPost())) {
+                wrapper.like("post", query.getPost());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingMaterialServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingMaterialServiceImpl.java
new file mode 100644
index 0000000..2ee4e6c
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingMaterialServiceImpl.java
@@ -0,0 +1,50 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyTrainingMaterialMapper;
+import com.ruoyi.safety.pojo.SafetyTrainingMaterial;
+import com.ruoyi.safety.service.SafetyTrainingMaterialService;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+@Service
+public class SafetyTrainingMaterialServiceImpl
+        extends SafetyBaseServiceImpl<SafetyTrainingMaterialMapper, SafetyTrainingMaterial>
+        implements SafetyTrainingMaterialService {
+
+    @Override
+    public IPage<SafetyTrainingMaterial> queryPage(Page<SafetyTrainingMaterial> page,
+                                                   SafetyTrainingMaterial query) {
+        QueryWrapper<SafetyTrainingMaterial> wrapper =
+                new QueryWrapper<SafetyTrainingMaterial>().orderByDesc("create_time");
+        if (query != null) {
+            if (hasText(query.getName())) {
+                wrapper.like("name", query.getName());
+            }
+            if (hasText(query.getType())) {
+                wrapper.eq("type", query.getType());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+
+    @Override
+    public boolean saveSafety(SafetyTrainingMaterial entity) {
+        if (entity.getUploadTime() == null) {
+            entity.setUploadTime(LocalDateTime.now());
+        }
+        if (!hasText(entity.getUploader())) {
+            entity.setUploader(currentUsername());
+        }
+        if (entity.getUploaderId() == null) {
+            entity.setUploaderId(currentUserId());
+        }
+        return super.saveSafety(entity);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingPlanServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingPlanServiceImpl.java
new file mode 100644
index 0000000..37eb4ac
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingPlanServiceImpl.java
@@ -0,0 +1,35 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyTrainingPlanMapper;
+import com.ruoyi.safety.pojo.SafetyTrainingPlan;
+import com.ruoyi.safety.service.SafetyTrainingPlanService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyTrainingPlanServiceImpl
+        extends SafetyBaseServiceImpl<SafetyTrainingPlanMapper, SafetyTrainingPlan>
+        implements SafetyTrainingPlanService {
+
+    @Override
+    public IPage<SafetyTrainingPlan> queryPage(Page<SafetyTrainingPlan> page, SafetyTrainingPlan query) {
+        QueryWrapper<SafetyTrainingPlan> wrapper = new QueryWrapper<SafetyTrainingPlan>().orderByDesc("create_time");
+        if (query != null) {
+            if (hasText(query.getYear())) {
+                wrapper.eq("`year`", query.getYear());
+            }
+            if (hasText(query.getPost())) {
+                wrapper.like("post", query.getPost());
+            }
+            if (hasText(query.getLevel())) {
+                wrapper.eq("`level`", query.getLevel());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingRecordServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingRecordServiceImpl.java
new file mode 100644
index 0000000..f858cba
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyTrainingRecordServiceImpl.java
@@ -0,0 +1,36 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyTrainingRecordMapper;
+import com.ruoyi.safety.pojo.SafetyTrainingRecord;
+import com.ruoyi.safety.service.SafetyTrainingRecordService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyTrainingRecordServiceImpl
+        extends SafetyBaseServiceImpl<SafetyTrainingRecordMapper, SafetyTrainingRecord>
+        implements SafetyTrainingRecordService {
+
+    @Override
+    public IPage<SafetyTrainingRecord> queryPage(Page<SafetyTrainingRecord> page, SafetyTrainingRecord query) {
+        QueryWrapper<SafetyTrainingRecord> wrapper =
+                new QueryWrapper<SafetyTrainingRecord>().orderByDesc("create_time");
+        if (query != null) {
+            if (query.getPlanId() != null) {
+                wrapper.eq("plan_id", query.getPlanId());
+            }
+            if (query.getEmployeeId() != null) {
+                wrapper.eq("employee_id", query.getEmployeeId());
+            }
+            if (hasText(query.getEmployeeName())) {
+                wrapper.like("employee_name", query.getEmployeeName());
+            }
+            if (query.getStatus() != null) {
+                wrapper.eq("status", query.getStatus());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/java/com/ruoyi/safety/service/impl/SafetyWorkAreaServiceImpl.java b/src/main/java/com/ruoyi/safety/service/impl/SafetyWorkAreaServiceImpl.java
new file mode 100644
index 0000000..21b2510
--- /dev/null
+++ b/src/main/java/com/ruoyi/safety/service/impl/SafetyWorkAreaServiceImpl.java
@@ -0,0 +1,31 @@
+package com.ruoyi.safety.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safety.mapper.SafetyWorkAreaMapper;
+import com.ruoyi.safety.pojo.SafetyWorkArea;
+import com.ruoyi.safety.service.SafetyWorkAreaService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SafetyWorkAreaServiceImpl extends SafetyBaseServiceImpl<SafetyWorkAreaMapper, SafetyWorkArea>
+        implements SafetyWorkAreaService {
+
+    @Override
+    public IPage<SafetyWorkArea> queryPage(Page<SafetyWorkArea> page, SafetyWorkArea query) {
+        QueryWrapper<SafetyWorkArea> wrapper = new QueryWrapper<SafetyWorkArea>().orderByDesc("create_time");
+        if (query != null) {
+            if (hasText(query.getName())) {
+                wrapper.like("name", query.getName());
+            }
+            if (hasText(query.getManager())) {
+                wrapper.like("manager", query.getManager());
+            }
+            if (query.getManagerId() != null) {
+                wrapper.eq("manager_id", query.getManagerId());
+            }
+        }
+        return page(page, wrapper);
+    }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..7d75a06
--- /dev/null
+++ b/src/main/resources/application-dev.yml
@@ -0,0 +1,222 @@
+# 鑺-浠撳偍鐗╂祦绯荤粺椤圭洰鐩稿叧閰嶇疆
+inspur:
+  appId: 1205971611543404544
+  appSecret: VkyDMDYwMTIwMjYxNzQxNDM2OTItrv
+ruoyi:
+  # 鍚嶇О
+  name: RuoYi
+  # 鐗堟湰
+  version: 3.8.9
+  # 鐗堟潈骞翠唤
+  copyrightYear: 2025
+  # 鏂囦欢璺緞 绀轰緥锛� Windows閰嶇疆D:/ruoyi/uploadPath锛孡inux閰嶇疆 /home/ruoyi/uploadPath锛�
+  profile: /center-lims/mis/file
+
+  # 鑾峰彇ip鍦板潃寮�鍏�
+  addressEnabled: false
+  # 楠岃瘉鐮佺被鍨� math 鏁板瓧璁$畻 char 瀛楃楠岃瘉
+  captchaType: math
+
+# 寮�鍙戠幆澧冮厤缃�
+server:
+  # 鏈嶅姟鍣ㄧ殑HTTP绔彛锛岄粯璁や负8080
+  port: 7003
+  servlet:
+    # 搴旂敤鐨勮闂矾寰�
+    context-path: /
+  tomcat:
+    # tomcat鐨刄RI缂栫爜
+    uri-encoding: UTF-8
+    # 杩炴帴鏁版弧鍚庣殑鎺掗槦鏁帮紝榛樿涓�100
+    accept-count: 1000
+    threads:
+      # tomcat鏈�澶х嚎绋嬫暟锛岄粯璁や负200
+      max: 800
+      # Tomcat鍚姩鍒濆鍖栫殑绾跨▼鏁帮紝榛樿鍊�10
+      min-spare: 100
+
+# 鏃ュ織閰嶇疆
+logging:
+  level:
+    com.ruoyi: warn
+    org.springframework: warn
+
+minio:
+  endpoint: http://114.132.189.42/
+  port: 7019
+  secure: false
+  accessKey: admin
+  secretKey: 12345678
+  preview-expiry: 24 # 棰勮鍦板潃榛樿24灏忔椂
+  default-bucket: uploadPath
+# 鐢ㄦ埛閰嶇疆
+user:
+  password:
+    # 瀵嗙爜鏈�澶ч敊璇鏁�
+    maxRetryCount: 5
+    # 瀵嗙爜閿佸畾鏃堕棿锛堥粯璁�10鍒嗛挓锛�
+    lockTime: 10
+
+# Spring閰嶇疆
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driverClassName: com.mysql.cj.jdbc.Driver
+    druid:
+      # 涓诲簱鏁版嵁婧�
+      master:
+        url: jdbc:mysql://localhost:3306/mis-ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        username: root
+        password: 123456
+      # 浠庡簱鏁版嵁婧�
+      slave:
+        # 浠庢暟鎹簮寮�鍏�/榛樿鍏抽棴
+        enabled: false
+        url:
+        username:
+        password:
+      # 鍒濆杩炴帴鏁�
+      initialSize: 5
+      # 鏈�灏忚繛鎺ユ睜鏁伴噺
+      minIdle: 10
+      # 鏈�澶ц繛鎺ユ睜鏁伴噺
+      maxActive: 20
+      # 閰嶇疆鑾峰彇杩炴帴绛夊緟瓒呮椂鐨勬椂闂�
+      maxWait: 60000
+      # 閰嶇疆杩炴帴瓒呮椂鏃堕棿
+      connectTimeout: 30000
+      # 閰嶇疆缃戠粶瓒呮椂鏃堕棿
+      socketTimeout: 60000
+      # 閰嶇疆闂撮殧澶氫箙鎵嶈繘琛屼竴娆℃娴嬶紝妫�娴嬮渶瑕佸叧闂殑绌洪棽杩炴帴锛屽崟浣嶆槸姣
+      timeBetweenEvictionRunsMillis: 60000
+      # 閰嶇疆涓�涓繛鎺ュ湪姹犱腑鏈�灏忕敓瀛樼殑鏃堕棿锛屽崟浣嶆槸姣
+      minEvictableIdleTimeMillis: 300000
+      # 閰嶇疆涓�涓繛鎺ュ湪姹犱腑鏈�澶х敓瀛樼殑鏃堕棿锛屽崟浣嶆槸姣
+      maxEvictableIdleTimeMillis: 900000
+      # 閰嶇疆妫�娴嬭繛鎺ユ槸鍚︽湁鏁�
+      validationQuery: SELECT 1 FROM DUAL
+      testWhileIdle: true
+      testOnBorrow: false
+      testOnReturn: false
+      webStatFilter:
+        enabled: true
+      statViewServlet:
+        enabled: true
+        # 璁剧疆鐧藉悕鍗曪紝涓嶅~鍒欏厑璁告墍鏈夎闂�
+        allow:
+        url-pattern: /druid/*
+        # 鎺у埗鍙扮鐞嗙敤鎴峰悕鍜屽瘑鐮�
+        login-username: ruoyi
+        login-password: 123456
+      filter:
+        stat:
+          enabled: true
+          # 鎱QL璁板綍
+          log-slow-sql: true
+          slow-sql-millis: 1000
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+  # 璧勬簮淇℃伅
+  messages:
+    # 鍥介檯鍖栬祫婧愭枃浠惰矾寰�
+    basename: i18n/messages
+  # 鏂囦欢涓婁紶
+  servlet:
+    multipart:
+      # 鍗曚釜鏂囦欢澶у皬
+      max-file-size: 1GB
+      # 璁剧疆鎬讳笂浼犵殑鏂囦欢澶у皬
+      max-request-size: 2GB
+  # 鏈嶅姟妯″潡
+  devtools:
+    restart:
+      # 鐑儴缃插紑鍏�
+      enabled: false
+  # redis 閰嶇疆
+  redis:
+    # 鍦板潃
+    host: 127.0.0.1
+#    host: 172.17.0.1
+    # 绔彛锛岄粯璁や负6379
+    port: 6379
+    # 鏁版嵁搴撶储寮�
+    database: 0
+    # 瀵嗙爜
+    password:
+#    password: 123456
+
+    # 杩炴帴瓒呮椂鏃堕棿
+    timeout: 10s
+    lettuce:
+      pool:
+        # 杩炴帴姹犱腑鐨勬渶灏忕┖闂茶繛鎺�
+        min-idle: 0
+        # 杩炴帴姹犱腑鐨勬渶澶х┖闂茶繛鎺�
+        max-idle: 8
+        # 杩炴帴姹犵殑鏈�澶ф暟鎹簱杩炴帴鏁�
+        max-active: 8
+        # #杩炴帴姹犳渶澶ч樆濉炵瓑寰呮椂闂达紙浣跨敤璐熷�艰〃绀烘病鏈夐檺鍒讹級
+        max-wait: -1ms
+
+# token閰嶇疆
+token:
+  # 浠ょ墝鑷畾涔夋爣璇�
+  header: Authorization
+  # 浠ょ墝瀵嗛挜
+  secret: abcdefghijklmnopqrstuvwxyz
+  # 浠ょ墝鏈夋晥鏈燂紙榛樿30鍒嗛挓锛�
+  expireTime: 450
+  
+# MyBatis Plus閰嶇疆
+mybatis-plus:
+  # 鎼滅储鎸囧畾鍖呭埆鍚�   鏍规嵁鑷繁鐨勯」鐩潵
+  typeAliasesPackage: com.ruoyi.**.pojo
+  # 閰嶇疆mapper鐨勬壂鎻忥紝鎵惧埌鎵�鏈夌殑mapper.xml鏄犲皠鏂囦欢
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
+  # 鍔犺浇鍏ㄥ眬鐨勯厤缃枃浠�
+  configLocation: classpath:mybatis/mybatis-config.xml
+  global-config:
+    enable-sql-runner: true
+    db-config:
+      id-type: auto
+  
+# PageHelper鍒嗛〉鎻掍欢
+pagehelper:
+  helperDialect: mysql
+  supportMethodsArguments: true
+  params: count=countSql
+
+# Swagger閰嶇疆
+swagger:
+  # 鏄惁寮�鍚痵wagger
+  enabled: false
+  # 璇锋眰鍓嶇紑
+  pathMapping: /dev-api
+
+# 闃叉XSS鏀诲嚮
+xss:
+  # 杩囨护寮�鍏�
+  enabled: true
+  # 鎺掗櫎閾炬帴锛堝涓敤閫楀彿鍒嗛殧锛�
+  excludes: /system/notice
+  # 鍖归厤閾炬帴
+  urlPatterns: /system/*,/monitor/*,/tool/*
+  
+# 浠g爜鐢熸垚
+gen:
+  # 浣滆��
+  author: ruoyi
+  # 榛樿鐢熸垚鍖呰矾寰� system 闇�鏀规垚鑷繁鐨勬ā鍧楀悕绉� 濡� system monitor tool
+  packageName: com.ruoyi.project.system
+  # 鑷姩鍘婚櫎琛ㄥ墠缂�锛岄粯璁ゆ槸true
+  autoRemovePre: false
+  # 琛ㄥ墠缂�锛堢敓鎴愮被鍚嶄笉浼氬寘鍚〃鍓嶇紑锛屽涓敤閫楀彿鍒嗛殧锛�
+  tablePrefix: sys_
+  # 鏄惁鍏佽鐢熸垚鏂囦欢瑕嗙洊鍒版湰鍦帮紙鑷畾涔夎矾寰勶級锛岄粯璁や笉鍏佽
+  allowOverwrite: false
+
+file:
+  temp-dir: /center-lims/mis/file/temp/uploads
+  upload-dir: /center-lims/mis/file/prod/uploads
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 2b5d0d9..5fb3cfe 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,7 +1,7 @@
 # Spring閰嶇疆
 spring:
   profiles:
-    active: yxglxt
+    active: dev
 #10.136.58.65	璐㈠姟绠$悊绯荤粺    cwglxt
 #10.136.58.66	璁惧绠$悊绯荤粺    sbglxt
 #10.136.58.67	鐢熶骇绠℃帶绯荤粺    scgkxt
diff --git a/src/main/resources/mapper/safety/SafetyInspectionRecordMapper.xml b/src/main/resources/mapper/safety/SafetyInspectionRecordMapper.xml
new file mode 100644
index 0000000..ea5ff49
--- /dev/null
+++ b/src/main/resources/mapper/safety/SafetyInspectionRecordMapper.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.safety.mapper.SafetyInspectionRecordMapper">
+
+    <select id="selectTodayStatistics" resultType="com.ruoyi.safety.dto.SafetyInspectionTodayStatistics">
+        SELECT
+            COUNT(1) AS totalCount,
+            IFNULL(SUM(CASE WHEN status &lt;&gt; 3 THEN 1 ELSE 0 END), 0) AS completedCount,
+            IFNULL(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) AS abnormalCount,
+            IFNULL(SUM(CASE WHEN status = 2 OR is_missed = 1 THEN 1 ELSE 0 END), 0) AS missedCount,
+            IFNULL(SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END), 0) AS unexecutedCount,
+            CASE
+                WHEN COUNT(1) = 0 THEN 0
+                ELSE ROUND(IFNULL(SUM(CASE WHEN status &lt;&gt; 3 THEN 1 ELSE 0 END), 0) / COUNT(1) * 100, 2)
+            END AS completionRate,
+            COUNT(DISTINCT inspector_id) AS inspectorCount
+        FROM safety_inspection_record
+        WHERE del_flag = 0
+          AND DATE(inspection_time) = #{statDate}
+    </select>
+
+    <select id="selectTrendStatistics" resultType="com.ruoyi.safety.dto.SafetyInspectionTrendStat">
+        SELECT
+            DATE(inspection_time) AS statDate,
+            COUNT(1) AS totalCount,
+            IFNULL(SUM(CASE WHEN status &lt;&gt; 3 THEN 1 ELSE 0 END), 0) AS completedCount,
+            IFNULL(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) AS abnormalCount,
+            IFNULL(SUM(CASE WHEN status = 2 OR is_missed = 1 THEN 1 ELSE 0 END), 0) AS missedCount,
+            IFNULL(SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END), 0) AS unexecutedCount
+        FROM safety_inspection_record
+        WHERE del_flag = 0
+          AND DATE(inspection_time) BETWEEN #{startDate} AND #{endDate}
+        GROUP BY DATE(inspection_time)
+        ORDER BY statDate ASC
+    </select>
+
+    <select id="selectTypeStatistics" resultType="com.ruoyi.safety.dto.SafetyInspectionTypeStat">
+        SELECT
+            IFNULL(type, 'unknown') AS type,
+            COUNT(1) AS count
+        FROM safety_inspection_record
+        WHERE del_flag = 0
+          AND DATE(inspection_time) BETWEEN #{startDate} AND #{endDate}
+        GROUP BY IFNULL(type, 'unknown')
+        ORDER BY count DESC
+    </select>
+
+    <select id="selectInspectorStatistics" resultType="com.ruoyi.safety.dto.SafetyInspectorStat">
+        SELECT
+            inspector_id AS inspectorId,
+            inspector AS inspector,
+            COUNT(1) AS totalCount,
+            IFNULL(SUM(CASE WHEN status &lt;&gt; 3 THEN 1 ELSE 0 END), 0) AS completedCount,
+            IFNULL(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) AS abnormalCount,
+            IFNULL(SUM(CASE WHEN status = 2 OR is_missed = 1 THEN 1 ELSE 0 END), 0) AS missedCount,
+            IFNULL(SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END), 0) AS unexecutedCount,
+            CASE
+                WHEN COUNT(1) = 0 THEN 0
+                ELSE ROUND(IFNULL(SUM(CASE WHEN status &lt;&gt; 3 THEN 1 ELSE 0 END), 0) / COUNT(1) * 100, 2)
+            END AS completionRate
+        FROM safety_inspection_record
+        WHERE del_flag = 0
+          AND DATE(inspection_time) BETWEEN #{startDate} AND #{endDate}
+        GROUP BY inspector_id, inspector
+        ORDER BY totalCount DESC
+    </select>
+</mapper>
diff --git a/src/main/resources/mapper/safety/SafetyLearningRecordMapper.xml b/src/main/resources/mapper/safety/SafetyLearningRecordMapper.xml
new file mode 100644
index 0000000..0e500a6
--- /dev/null
+++ b/src/main/resources/mapper/safety/SafetyLearningRecordMapper.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.safety.mapper.SafetyLearningRecordMapper">
+
+    <select id="selectStatistics" resultType="com.ruoyi.safety.dto.SafetyLearningStatistics">
+        SELECT
+            COUNT(1) AS totalCount,
+            IFNULL(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) AS completedCount,
+            IFNULL(SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END), 0) AS unfinishedCount,
+            IFNULL(SUM(duration), 0) AS totalDuration,
+            CASE
+                WHEN COUNT(1) = 0 THEN 0
+                ELSE ROUND(IFNULL(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) / COUNT(1) * 100, 2)
+            END AS completionRate
+        FROM safety_learning_record
+        WHERE del_flag = 0
+    </select>
+</mapper>

--
Gitblit v1.9.3