From 61b1802b1b26fc35c07fd09edc4ff2ff8ade6688 Mon Sep 17 00:00:00 2001
From: maven <2163098428@qq.com>
Date: 星期四, 29 一月 2026 14:52:23 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New' into dev_New

---
 src/main/java/com/ruoyi/safe/controller/SafeTrainingDetailsController.java    |   45 ++
 src/main/java/com/ruoyi/safe/service/SafeTrainingFileService.java             |   16 
 src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java           |   81 ++++
 src/main/java/com/ruoyi/safe/mapper/SafeTrainingMapper.java                   |   25 +
 src/main/java/com/ruoyi/safe/service/impl/SafeTrainingServiceImpl.java        |  179 ++++++++
 src/main/java/com/ruoyi/ScheduleTask.java                                     |   58 ++
 src/main/resources/mapper/safe/SafeTrainingFileMapper.xml                     |   19 
 src/main/java/com/ruoyi/safe/dto/SafeTrainingDetailsDto.java                  |   29 +
 src/main/java/com/ruoyi/safe/mapper/SafeTrainingDetailsMapper.java            |   27 +
 src/main/java/com/ruoyi/safe/service/impl/SafeTrainingFileServiceImpl.java    |   20 +
 src/main/java/com/ruoyi/safe/dto/SafeTrainingDto.java                         |   26 +
 src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java |    3 
 src/main/resources/mapper/safe/SafeAccidentMapper.xml                         |    6 
 src/main/java/com/ruoyi/safe/service/impl/SafeTrainingDetailsServiceImpl.java |   81 ++++
 src/main/resources/mapper/safe/SafeTrainingDetailsMapper.xml                  |   37 +
 src/main/java/com/ruoyi/safe/service/SafeTrainingDetailsService.java          |   24 +
 src/main/java/com/ruoyi/safe/controller/SafeTrainingFileController.java       |   72 +++
 src/main/java/com/ruoyi/safe/pojo/SafeTrainingDetails.java                    |   95 ++++
 src/main/java/com/ruoyi/safe/pojo/SafeTrainingFile.java                       |   57 ++
 src/main/java/com/ruoyi/safe/service/SafeTrainingService.java                 |   33 +
 src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java          |    2 
 src/main/resources/static/safe-training.docx                                  |    0 
 src/main/java/com/ruoyi/safe/pojo/SafeTraining.java                           |  123 ++++++
 src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java              |   31 
 src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java           |    2 
 src/main/resources/static/safe-training-details.docx                          |    0 
 src/main/java/com/ruoyi/safe/mapper/SafeTrainingFileMapper.java               |   18 
 src/main/resources/mapper/safe/SafeTrainingMapper.xml                         |   59 ++
 28 files changed, 1,149 insertions(+), 19 deletions(-)

diff --git a/src/main/java/com/ruoyi/ScheduleTask.java b/src/main/java/com/ruoyi/ScheduleTask.java
new file mode 100644
index 0000000..9913292
--- /dev/null
+++ b/src/main/java/com/ruoyi/ScheduleTask.java
@@ -0,0 +1,58 @@
+package com.ruoyi;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.project.system.domain.SysNotice;
+import com.ruoyi.project.system.mapper.SysNoticeMapper;
+import com.ruoyi.safe.mapper.SafeTrainingMapper;
+import com.ruoyi.safe.pojo.SafeTraining;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+@Component
+//瀹氭椂浠诲姟姹囨��
+public class ScheduleTask {
+
+    @Autowired
+    private SafeTrainingMapper safeTrainingMapper;
+
+    @Autowired
+    private SysNoticeMapper noticeMapper;
+
+    //瀹氭椂浠诲姟(15鍒嗛挓鎵ц涓�娆�--鍒ゆ柇鍩硅璁″垝鏁版嵁,鐘舵�佸仛鍙樻洿)
+    @Scheduled(cron = "0 0/15 * * * ?")
+    public void testScheduleTask() {
+        List<SafeTraining> safeTrainings = safeTrainingMapper.selectList(Wrappers.<SafeTraining>lambdaQuery().ne(SafeTraining::getState, 2));
+        if (safeTrainings.size() > 0) {
+            for (SafeTraining safeTraining : safeTrainings) {
+                //鏍规嵁鏃堕棿鍒ゆ柇鍩硅鐘舵��
+                String trainingDate = safeTraining.getTrainingDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                LocalDateTime openingTime = LocalDateTime.parse((trainingDate + safeTraining.getOpeningTime()), DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss"));
+                LocalDateTime endTime = LocalDateTime.parse((trainingDate + safeTraining.getEndTime()), DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss"));
+                if (LocalDateTime.now().isBefore(openingTime)) {
+                    //鏈紑濮�
+                    safeTraining.setState(0);
+                } else if (LocalDateTime.now().isAfter(endTime)) {
+                    //宸茬粨鏉�
+                    safeTraining.setState(2);
+                } else {
+                    //杩涜涓�
+                    safeTraining.setState(1);
+                }
+                safeTrainingMapper.updateById(safeTraining);
+            }
+        }
+    }
+
+    //宸茶鏁版嵁鍋氫竴涓畾鏃朵换鍔�(姣忔湀1鍙�1鐐规竻鐞嗕竴娆′笂涓湀宸茶鏁版嵁)
+    @Scheduled(cron = "0 0 1 1 * ?")
+    public void cleanReadData() {
+        noticeMapper.delete(Wrappers.<SysNotice>lambdaQuery()
+                .eq(SysNotice::getStatus,"1")
+                .lt(SysNotice::getCreateTime, LocalDateTime.now()));
+    }
+}
diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java
index 0b4dae4..942a700 100644
--- a/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java
+++ b/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java
@@ -1,19 +1,22 @@
 package com.ruoyi.project.system.mapper;
 
 import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 import com.ruoyi.project.system.domain.SysDept;
 
 /**
  * 閮ㄩ棬绠$悊 鏁版嵁灞�
- * 
+ *
  * @author ruoyi
  */
+@Mapper
 public interface SysDeptMapper
 {
     /**
      * 鏌ヨ閮ㄩ棬绠$悊鏁版嵁
-     * 
+     *
      * @param dept 閮ㄩ棬淇℃伅
      * @return 閮ㄩ棬淇℃伅闆嗗悎
      */
@@ -21,7 +24,7 @@
 
     /**
      * 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戜俊鎭�
-     * 
+     *
      * @param roleId 瑙掕壊ID
      * @param deptCheckStrictly 閮ㄩ棬鏍戦�夋嫨椤规槸鍚﹀叧鑱旀樉绀�
      * @return 閫変腑閮ㄩ棬鍒楄〃
@@ -30,7 +33,7 @@
 
     /**
      * 鏍规嵁閮ㄩ棬ID鏌ヨ淇℃伅
-     * 
+     *
      * @param deptId 閮ㄩ棬ID
      * @return 閮ㄩ棬淇℃伅
      */
@@ -38,7 +41,7 @@
 
     /**
      * 鏍规嵁ID鏌ヨ鎵�鏈夊瓙閮ㄩ棬
-     * 
+     *
      * @param deptId 閮ㄩ棬ID
      * @return 閮ㄩ棬鍒楄〃
      */
@@ -46,7 +49,7 @@
 
     /**
      * 鏍规嵁ID鏌ヨ鎵�鏈夊瓙閮ㄩ棬锛堟甯哥姸鎬侊級
-     * 
+     *
      * @param deptId 閮ㄩ棬ID
      * @return 瀛愰儴闂ㄦ暟
      */
@@ -54,7 +57,7 @@
 
     /**
      * 鏄惁瀛樺湪瀛愯妭鐐�
-     * 
+     *
      * @param deptId 閮ㄩ棬ID
      * @return 缁撴灉
      */
@@ -62,7 +65,7 @@
 
     /**
      * 鏌ヨ閮ㄩ棬鏄惁瀛樺湪鐢ㄦ埛
-     * 
+     *
      * @param deptId 閮ㄩ棬ID
      * @return 缁撴灉
      */
@@ -70,7 +73,7 @@
 
     /**
      * 鏍¢獙閮ㄩ棬鍚嶇О鏄惁鍞竴
-     * 
+     *
      * @param deptName 閮ㄩ棬鍚嶇О
      * @param parentId 鐖堕儴闂↖D
      * @return 缁撴灉
@@ -79,7 +82,7 @@
 
     /**
      * 鏂板閮ㄩ棬淇℃伅
-     * 
+     *
      * @param dept 閮ㄩ棬淇℃伅
      * @return 缁撴灉
      */
@@ -87,7 +90,7 @@
 
     /**
      * 淇敼閮ㄩ棬淇℃伅
-     * 
+     *
      * @param dept 閮ㄩ棬淇℃伅
      * @return 缁撴灉
      */
@@ -95,14 +98,14 @@
 
     /**
      * 淇敼鎵�鍦ㄩ儴闂ㄦ甯哥姸鎬�
-     * 
+     *
      * @param deptIds 閮ㄩ棬ID缁�
      */
     public void updateDeptStatusNormal(Long[] deptIds);
 
     /**
      * 淇敼瀛愬厓绱犲叧绯�
-     * 
+     *
      * @param depts 瀛愬厓绱�
      * @return 缁撴灉
      */
@@ -110,7 +113,7 @@
 
     /**
      * 鍒犻櫎閮ㄩ棬绠$悊淇℃伅
-     * 
+     *
      * @param deptId 閮ㄩ棬ID
      * @return 缁撴灉
      */
diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
index bf794b5..679514d 100644
--- a/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
+++ b/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
@@ -1,5 +1,6 @@
 package com.ruoyi.project.system.service.impl;
 
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -22,6 +23,7 @@
 import com.ruoyi.project.system.mapper.SysUserMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import com.ruoyi.project.system.domain.SysNotice;
 import com.ruoyi.project.system.mapper.SysNoticeMapper;
@@ -210,4 +212,5 @@
         sysNotice.setTenantId(tenantId);
         return sysNotice;
     }
+
 }
diff --git a/src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java b/src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java
index 615514a..3db1660 100644
--- a/src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java
+++ b/src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java
@@ -48,7 +48,7 @@
 
     @ApiOperation("鍒犻櫎浜嬫晠涓婃姤璁板綍")
     @DeleteMapping("/{ids}")
-    public R delSafeCertification(@RequestBody List<Integer> ids) {
+    public R delSafeAccident(@RequestBody List<Integer> ids) {
         return R.ok(safeAccidentService.removeBatchByIds(ids));
     }
 
diff --git a/src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java b/src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java
new file mode 100644
index 0000000..3ea4689
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java
@@ -0,0 +1,81 @@
+package com.ruoyi.safe.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.safe.dto.SafeTrainingDto;
+import com.ruoyi.safe.pojo.SafeAccident;
+import com.ruoyi.safe.pojo.SafeTraining;
+import com.ruoyi.safe.pojo.SafeTrainingDetails;
+import com.ruoyi.safe.service.SafeAccidentService;
+import com.ruoyi.safe.service.SafeTrainingDetailsService;
+import com.ruoyi.safe.service.SafeTrainingService;
+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;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:06
+ */
+@RestController
+@RequestMapping("/safeTraining")
+@Api(tags = "瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳")
+public class SafeTrainingController {
+
+    @Autowired
+    private SafeTrainingService safeTrainingService;
+
+    @Autowired
+    private SafeTrainingDetailsService safeTrainingDetailsService;
+
+    @GetMapping("/page")
+    @ApiOperation("鍒嗛〉鏌ヨ")
+    public R page(Page page, SafeTrainingDto safeTrainingDto) {
+        return R.ok(safeTrainingService.pageSafeTraining(page, safeTrainingDto));
+    }
+
+    @ApiOperation("鏂板/缂栬緫瀹夊叏鍩硅鑰冩牳")
+    @PostMapping()
+    public R addOrUpdate(@RequestBody SafeTraining safeTraining) {
+        return R.ok(safeTrainingService.addOrUpdate(safeTraining));
+    }
+
+    @ApiOperation("绛惧埌")
+    @PostMapping ("/sign")
+    public R sign(@RequestBody SafeTrainingDetails safeTrainingDetails) {
+        return R.ok(safeTrainingDetailsService.save(safeTrainingDetails));
+    }
+
+    @ApiOperation("缁撴灉鏄庣粏鏌ヨ")
+    @GetMapping ("/getSafeTraining")
+    public R getSafeTraining(Long id) {
+        return R.ok(safeTrainingService.getSafeTraining(id));
+    }
+
+    @ApiOperation("缁撴灉鏄庣粏淇濆瓨")
+    @PostMapping ("/saveSafeTraining")
+    public R saveSafeTraining(@RequestBody SafeTrainingDto safeTrainingDto) {
+        return R.ok(safeTrainingService.saveSafeTraining(safeTrainingDto));
+    }
+
+    @ApiOperation("鍒犻櫎瀹夊叏鍩硅鑰冩牳")
+    @DeleteMapping("/{ids}")
+    public R delSafeTraining(@RequestBody List<Integer> ids) {
+        return R.ok(safeTrainingService.delSafeTraining(ids));
+    }
+
+    @ApiOperation("瀵煎嚭")
+    @PostMapping ("/export")
+    public void export(HttpServletResponse response, @RequestBody SafeTraining safeTraining) {
+        safeTrainingService.export(response,safeTraining.getId());
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/safe/controller/SafeTrainingDetailsController.java b/src/main/java/com/ruoyi/safe/controller/SafeTrainingDetailsController.java
new file mode 100644
index 0000000..75bcfb9
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/controller/SafeTrainingDetailsController.java
@@ -0,0 +1,45 @@
+package com.ruoyi.safe.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.safe.dto.SafeTrainingDto;
+import com.ruoyi.safe.pojo.SafeTraining;
+import com.ruoyi.safe.pojo.SafeTrainingDetails;
+import com.ruoyi.safe.service.SafeTrainingDetailsService;
+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;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--璁板綍璇︽儏 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:15
+ */
+@RestController
+@RequestMapping("/safeTrainingDetails")
+@Api(tags = "瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--璁板綍璇︽儏")
+public class SafeTrainingDetailsController {
+
+
+    @Autowired
+    private SafeTrainingDetailsService safeTrainingDetailsService;
+
+    @GetMapping("/page")
+    @ApiOperation("鍒嗛〉鏌ヨ")
+    public R page(Page page, SafeTrainingDetails safeTrainingDetails) {
+        return R.ok(safeTrainingDetailsService.pageDetails(page, safeTrainingDetails));
+    }
+
+    @ApiOperation("瀵煎嚭")
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, @RequestBody SafeTrainingDetails safeTrainingDetails) {
+        safeTrainingDetailsService.export(response,safeTrainingDetails.getUserId());
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/safe/controller/SafeTrainingFileController.java b/src/main/java/com/ruoyi/safe/controller/SafeTrainingFileController.java
new file mode 100644
index 0000000..3057900
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/controller/SafeTrainingFileController.java
@@ -0,0 +1,72 @@
+package com.ruoyi.safe.controller;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.safe.pojo.SafeHiddenFile;
+import com.ruoyi.safe.pojo.SafeTrainingFile;
+import com.ruoyi.safe.service.SafeTrainingFileService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--闄勪欢 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:23
+ */
+@RestController
+@RequestMapping("/safeTrainingFile")
+@Api(tags = "瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--闄勪欢")
+public class SafeTrainingFileController {
+
+    @Resource
+    private SafeTrainingFileService safeTrainingFileService;
+
+
+    /**
+     * 鏂板
+     * @param safeHiddenFile
+     * @return
+     */
+    @PostMapping("/add")
+    @ApiOperation("鏂板")
+    public R add(@RequestBody SafeTrainingFile safeHiddenFile) {
+        return R.ok(safeTrainingFileService.save(safeHiddenFile));
+    }
+
+    /**
+     * 鍒犻櫎
+     * @param ids
+     * @return
+     */
+    @DeleteMapping("/del")
+    @ApiOperation("鍒犻櫎")
+    public R delSafeHiddenFile(@RequestBody List<Integer> ids) {
+        if(CollectionUtils.isEmpty(ids)){
+            return R.fail("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+        }
+        //鍒犻櫎妫�楠岄檮浠�
+        return R.ok(safeTrainingFileService.removeBatchByIds(ids));
+    }
+
+    /**
+     *鍒嗛〉鏌ヨ
+     * @param page
+     * @param safeTrainingFile
+     * @return
+     */
+    @GetMapping("/listPage")
+    @ApiOperation("鍒嗛〉鏌ヨ")
+    public R listPage(Page page, SafeTrainingFile safeTrainingFile) {
+        return R.ok(safeTrainingFileService.page(page, Wrappers.<SafeTrainingFile>lambdaQuery().eq(SafeTrainingFile::getSafeTrainingId,safeTrainingFile.getSafeTrainingId())));
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/safe/dto/SafeTrainingDetailsDto.java b/src/main/java/com/ruoyi/safe/dto/SafeTrainingDetailsDto.java
new file mode 100644
index 0000000..0eb6da5
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/dto/SafeTrainingDetailsDto.java
@@ -0,0 +1,29 @@
+package com.ruoyi.safe.dto;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.safe.pojo.SafeTrainingDetails;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+public class SafeTrainingDetailsDto extends SafeTrainingDetails {
+
+    @ApiModelProperty("鍩硅浜哄憳缂栧彿")
+    private String userName;
+
+    @ApiModelProperty("鍩硅浜哄憳鍚嶇О")
+    private String nickName;
+
+    @ApiModelProperty("鎵嬫満鍙风爜")
+    private String phonenumber;
+
+
+}
diff --git a/src/main/java/com/ruoyi/safe/dto/SafeTrainingDto.java b/src/main/java/com/ruoyi/safe/dto/SafeTrainingDto.java
new file mode 100644
index 0000000..0b8d7cd
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/dto/SafeTrainingDto.java
@@ -0,0 +1,26 @@
+package com.ruoyi.safe.dto;
+
+import com.ruoyi.safe.pojo.SafeTraining;
+import com.ruoyi.safe.pojo.SafeTrainingDetails;
+import com.ruoyi.safe.pojo.SafeTrainingFile;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class SafeTrainingDto extends SafeTraining {
+
+    @ApiModelProperty("鎶ュ悕浜烘暟")
+    private Integer nums;
+
+    @ApiModelProperty("璇勪环浜�")
+    private String assessmentUserName;
+
+    @ApiModelProperty("闄勪欢闆嗗悎")
+    private List<SafeTrainingFile> safeTrainingFileList;
+
+    @ApiModelProperty("鍩硅璁板綍浜哄憳璇︽儏闆嗗悎")
+    private List<SafeTrainingDetailsDto> safeTrainingDetailsDtoList;
+
+}
diff --git a/src/main/java/com/ruoyi/safe/mapper/SafeTrainingDetailsMapper.java b/src/main/java/com/ruoyi/safe/mapper/SafeTrainingDetailsMapper.java
new file mode 100644
index 0000000..2a05cf8
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/mapper/SafeTrainingDetailsMapper.java
@@ -0,0 +1,27 @@
+package com.ruoyi.safe.mapper;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safe.dto.SafeTrainingDetailsDto;
+import com.ruoyi.safe.pojo.SafeTrainingDetails;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--璁板綍璇︽儏 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:15
+ */
+@Mapper
+public interface SafeTrainingDetailsMapper extends BaseMapper<SafeTrainingDetails> {
+
+    List<SafeTrainingDetailsDto> getSafeTraining(@Param("id") Long id);
+
+    IPage<SafeTrainingDetails> pageDetails(Page page, @Param("c") SafeTrainingDetails safeTrainingDetails);
+}
diff --git a/src/main/java/com/ruoyi/safe/mapper/SafeTrainingFileMapper.java b/src/main/java/com/ruoyi/safe/mapper/SafeTrainingFileMapper.java
new file mode 100644
index 0000000..709df72
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/mapper/SafeTrainingFileMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.safe.mapper;
+
+import com.ruoyi.safe.pojo.SafeTrainingFile;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--闄勪欢 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:23
+ */
+@Mapper
+public interface SafeTrainingFileMapper extends BaseMapper<SafeTrainingFile> {
+
+}
diff --git a/src/main/java/com/ruoyi/safe/mapper/SafeTrainingMapper.java b/src/main/java/com/ruoyi/safe/mapper/SafeTrainingMapper.java
new file mode 100644
index 0000000..a1fe28d
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/mapper/SafeTrainingMapper.java
@@ -0,0 +1,25 @@
+package com.ruoyi.safe.mapper;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safe.dto.SafeTrainingDto;
+import com.ruoyi.safe.pojo.SafeTraining;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:06
+ */
+@Mapper
+public interface SafeTrainingMapper extends BaseMapper<SafeTraining> {
+
+    IPage<SafeTrainingDto> pageSafeTraining(Page page, @Param("c") SafeTrainingDto safeTrainingDto);
+
+    SafeTrainingDto getSafeTraining(@Param("id") Long id);
+}
diff --git a/src/main/java/com/ruoyi/safe/pojo/SafeTraining.java b/src/main/java/com/ruoyi/safe/pojo/SafeTraining.java
new file mode 100644
index 0000000..f9ecc29
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/pojo/SafeTraining.java
@@ -0,0 +1,123 @@
+package com.ruoyi.safe.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:06
+ */
+@Getter
+@Setter
+@TableName("safe_training")
+@ApiModel(value = "SafeTraining瀵硅薄", description = "瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳")
+public class SafeTraining implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("璇剧▼缂栧彿")
+    private String courseCode;
+
+    @ApiModelProperty("鍩硅鐩爣")
+    private String trainingObjectives;
+
+    @ApiModelProperty("鍩硅鍐呭")
+    private String trainingContent;
+
+    @ApiModelProperty("鍩硅鏂瑰紡")
+    private String trainingMode;
+
+    @ApiModelProperty("鐘舵��(0锛氭湭寮�濮�1锛氳繘琛屼腑锛�2锛氬凡缁撴潫)")
+    private Integer state;
+
+    @ApiModelProperty("鍙傚姞瀵硅薄")
+    private String participants;
+
+    @ApiModelProperty("鍩硅鍦扮偣")
+    private String placeTraining;
+
+    @ApiModelProperty("鍩硅璁插笀")
+    private String trainingLecturer;
+
+    @ApiModelProperty("鍩硅鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @NotBlank(message = "鍩硅鏃ユ湡涓嶈兘涓虹┖")
+    private LocalDate trainingDate;
+
+    @ApiModelProperty("寮�濮嬫椂闂�(鏃跺垎绉�)")
+    @NotBlank(message = "寮�濮嬫椂闂翠笉鑳戒负绌�")
+    private String openingTime;
+
+    @ApiModelProperty("缁撴潫鏃堕棿(鏃跺垎绉�)")
+    @NotBlank(message = "缁撴潫鏃堕棿涓嶈兘涓虹┖")
+    private String endTime;
+
+    @ApiModelProperty("璇鹃瀛﹀垎")
+    private String projectCredits;
+
+    @ApiModelProperty("璇炬椂")
+    private Double classHour;
+
+    @ApiModelProperty("鑰冩牳鏂瑰紡")
+    private String assessmentMethod;
+
+    @ApiModelProperty("鏈鍩硅缁煎悎璇勪环")
+    private String comprehensiveAssessment;
+
+    @ApiModelProperty("澶囨敞")
+    private String remarks;
+
+    @ApiModelProperty("璇勪环浜篿d")
+    private Integer assessmentUserId;
+
+    @ApiModelProperty("璇勪环鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate assessmentDate;
+
+    @ApiModelProperty("鍩硅鎽樿")
+    private String trainingAbstract;
+
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+}
diff --git a/src/main/java/com/ruoyi/safe/pojo/SafeTrainingDetails.java b/src/main/java/com/ruoyi/safe/pojo/SafeTrainingDetails.java
new file mode 100644
index 0000000..a53ea3d
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/pojo/SafeTrainingDetails.java
@@ -0,0 +1,95 @@
+package com.ruoyi.safe.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--璁板綍璇︽儏
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:15
+ */
+@Getter
+@Setter
+@TableName("safe_training_details")
+@ApiModel(value = "SafeTrainingDetails瀵硅薄", description = "瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--璁板綍璇︽儏")
+public class SafeTrainingDetails implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("鐢ㄦ埛琛ㄦ牸锛坲ser锛変富閿�")
+    @NotBlank(message = "鐢ㄦ埛id涓嶈兘涓虹┖")
+    private Long userId;
+
+    @ApiModelProperty("鍏宠仈瀹夊叏鍩硅鑰冩牳id")
+    private Integer safeTrainingId;
+
+    @ApiModelProperty("鑰冩牳缁撴灉")
+    private String examinationResults;
+
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+
+
+    @ApiModelProperty("鍩硅鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @TableField(exist = false)
+    private LocalDate trainingDate;
+
+    @ApiModelProperty("璇剧▼缂栧彿")
+    @TableField(exist = false)
+    private String courseCode;
+
+    @ApiModelProperty("鍩硅鍐呭")
+    @TableField(exist = false)
+    private String trainingContent;
+
+    @ApiModelProperty("鍩硅璇炬椂")
+    @TableField(exist = false)
+    private Double classHour;
+
+    @ApiModelProperty("璇鹃瀛﹀垎")
+    @TableField(exist = false)
+    private String projectCredits;
+
+    @ApiModelProperty("澶囨敞")
+    @TableField(exist = false)
+    private String remarks;
+}
diff --git a/src/main/java/com/ruoyi/safe/pojo/SafeTrainingFile.java b/src/main/java/com/ruoyi/safe/pojo/SafeTrainingFile.java
new file mode 100644
index 0000000..9ed72a7
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/pojo/SafeTrainingFile.java
@@ -0,0 +1,57 @@
+package com.ruoyi.safe.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--闄勪欢
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:23
+ */
+@Getter
+@Setter
+@TableName("safe_training_file")
+@ApiModel(value = "SafeTrainingFile瀵硅薄", description = "瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--闄勪欢")
+public class SafeTrainingFile implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("鍏宠仈瀹夊叏鍩硅鑰冩牳id")
+    private Integer safeTrainingId;
+
+    private String name;
+
+    private String url;
+
+    private Object fileSize;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+}
diff --git a/src/main/java/com/ruoyi/safe/service/SafeTrainingDetailsService.java b/src/main/java/com/ruoyi/safe/service/SafeTrainingDetailsService.java
new file mode 100644
index 0000000..8a06f05
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/SafeTrainingDetailsService.java
@@ -0,0 +1,24 @@
+package com.ruoyi.safe.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safe.dto.SafeTrainingDto;
+import com.ruoyi.safe.pojo.SafeTrainingDetails;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--璁板綍璇︽儏 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:15
+ */
+public interface SafeTrainingDetailsService extends IService<SafeTrainingDetails> {
+
+    IPage<SafeTrainingDetails> pageDetails(Page page, SafeTrainingDetails safeTrainingDetails);
+
+    void export(HttpServletResponse response, Long userId);
+}
diff --git a/src/main/java/com/ruoyi/safe/service/SafeTrainingFileService.java b/src/main/java/com/ruoyi/safe/service/SafeTrainingFileService.java
new file mode 100644
index 0000000..7d17c41
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/SafeTrainingFileService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.safe.service;
+
+import com.ruoyi.safe.pojo.SafeTrainingFile;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--闄勪欢 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:23
+ */
+public interface SafeTrainingFileService extends IService<SafeTrainingFile> {
+
+}
diff --git a/src/main/java/com/ruoyi/safe/service/SafeTrainingService.java b/src/main/java/com/ruoyi/safe/service/SafeTrainingService.java
new file mode 100644
index 0000000..56f2e6e
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/SafeTrainingService.java
@@ -0,0 +1,33 @@
+package com.ruoyi.safe.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safe.dto.SafeTrainingDto;
+import com.ruoyi.safe.pojo.SafeTraining;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:06
+ */
+public interface SafeTrainingService extends IService<SafeTraining> {
+
+    IPage<SafeTrainingDto> pageSafeTraining(Page page, SafeTrainingDto safeTrainingDto);
+
+    int addOrUpdate(SafeTraining safeTraining);
+
+    SafeTrainingDto getSafeTraining(Long id);
+
+    int saveSafeTraining(SafeTrainingDto safeTrainingDto);
+
+    int delSafeTraining(List<Integer> ids);
+
+    void export(HttpServletResponse response, Long id);
+}
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java b/src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java
index 9e4aa57..6f928bc 100644
--- a/src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java
@@ -47,7 +47,7 @@
     @Override
     public int add(SafeHidden safeHidden) {
         safeHiddenMapper.insert(safeHidden);
-        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
         String no = "YH" + String.format("%s%03d", datePrefix, safeHidden.getId());
         safeHidden.setHiddenCode(no);
         safeHiddenMapper.updateById(safeHidden);
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingDetailsServiceImpl.java b/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingDetailsServiceImpl.java
new file mode 100644
index 0000000..2804aa3
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingDetailsServiceImpl.java
@@ -0,0 +1,81 @@
+package com.ruoyi.safe.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.deepoove.poi.XWPFTemplate;
+import com.deepoove.poi.config.Configure;
+import com.ruoyi.common.utils.HackLoopTableRenderPolicy;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.safe.dto.SafeTrainingDetailsDto;
+import com.ruoyi.safe.dto.SafeTrainingDto;
+import com.ruoyi.safe.pojo.SafeTrainingDetails;
+import com.ruoyi.safe.mapper.SafeTrainingDetailsMapper;
+import com.ruoyi.safe.service.SafeTrainingDetailsService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--璁板綍璇︽儏 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:15
+ */
+@Service
+public class SafeTrainingDetailsServiceImpl extends ServiceImpl<SafeTrainingDetailsMapper, SafeTrainingDetails> implements SafeTrainingDetailsService {
+
+    @Autowired
+    private SafeTrainingDetailsMapper safeTrainingDetailsMapper;
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Override
+    public IPage<SafeTrainingDetails> pageDetails(Page page, SafeTrainingDetails safeTrainingDetails) {
+        return safeTrainingDetailsMapper.pageDetails(page, safeTrainingDetails);
+    }
+
+    @Override
+    public void export(HttpServletResponse response, Long userId) {
+        SafeTrainingDetails safeTrainingDetails = new SafeTrainingDetails();
+        safeTrainingDetails.setUserId(userId);
+        SysUser sysUser = sysUserMapper.selectUserById(userId);
+        List<SafeTrainingDetails> safeTrainingDetailsList = safeTrainingDetailsMapper.pageDetails(new Page(1, -1), safeTrainingDetails).getRecords();
+        InputStream inputStream = this.getClass().getResourceAsStream("/static/safe-training-details.docx");
+        Configure configure = Configure.builder()
+                .bind("safeTrainingDetailsList", new HackLoopTableRenderPolicy())
+                .build();
+        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
+                new HashMap<String, Object>() {{
+                    put("user", sysUser);
+                    put("safeTrainingDetailsList", safeTrainingDetailsList);
+                }});
+
+        try {
+            response.setContentType("application/msword");
+            String fileName = URLEncoder.encode(
+                    "鍩硅涓庤�冩牳璁板綍", "UTF-8");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-disposition",
+                    "attachment;filename=" + fileName + ".docx");
+            OutputStream os = response.getOutputStream();
+            template.write(os);
+            os.flush();
+            os.close();
+            inputStream.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("瀵煎嚭澶辫触");
+        }
+    }
+}
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingFileServiceImpl.java b/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingFileServiceImpl.java
new file mode 100644
index 0000000..6d5c53b
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingFileServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.safe.service.impl;
+
+import com.ruoyi.safe.pojo.SafeTrainingFile;
+import com.ruoyi.safe.mapper.SafeTrainingFileMapper;
+import com.ruoyi.safe.service.SafeTrainingFileService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳--闄勪欢 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:23
+ */
+@Service
+public class SafeTrainingFileServiceImpl extends ServiceImpl<SafeTrainingFileMapper, SafeTrainingFile> implements SafeTrainingFileService {
+
+}
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingServiceImpl.java b/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingServiceImpl.java
new file mode 100644
index 0000000..0179898
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeTrainingServiceImpl.java
@@ -0,0 +1,179 @@
+package com.ruoyi.safe.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.deepoove.poi.XWPFTemplate;
+import com.deepoove.poi.config.Configure;
+import com.ruoyi.common.utils.HackLoopTableRenderPolicy;
+import com.ruoyi.production.pojo.ProductOrder;
+import com.ruoyi.production.pojo.ProductWorkOrder;
+import com.ruoyi.project.system.domain.SysNotice;
+import com.ruoyi.safe.dto.SafeTrainingDetailsDto;
+import com.ruoyi.safe.dto.SafeTrainingDto;
+import com.ruoyi.safe.mapper.SafeTrainingDetailsMapper;
+import com.ruoyi.safe.mapper.SafeTrainingFileMapper;
+import com.ruoyi.safe.pojo.SafeTraining;
+import com.ruoyi.safe.mapper.SafeTrainingMapper;
+import com.ruoyi.safe.pojo.SafeTrainingDetails;
+import com.ruoyi.safe.pojo.SafeTrainingFile;
+import com.ruoyi.safe.service.SafeTrainingService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--瀹夊叏鍩硅鑰冩牳 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-01-29 10:54:06
+ */
+@Service
+public class SafeTrainingServiceImpl extends ServiceImpl<SafeTrainingMapper, SafeTraining> implements SafeTrainingService {
+
+    @Autowired
+    private SafeTrainingMapper safeTrainingMapper;
+
+    @Autowired
+    private SafeTrainingFileMapper safeTrainingFileMapper;
+
+    @Autowired
+    private SafeTrainingDetailsMapper safeTrainingDetailsMapper;
+
+    @Override
+    public IPage<SafeTrainingDto> pageSafeTraining(Page page, SafeTrainingDto safeTrainingDto) {
+        return safeTrainingMapper.pageSafeTraining(page, safeTrainingDto);
+    }
+
+    @Override
+    public int addOrUpdate(SafeTraining safeTraining) {
+        if (ObjectUtils.isNull(safeTraining.getId())) {
+            String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
+            // 鏌ヨ浠婃棩宸插瓨鍦ㄧ殑鏈�澶ц绋嬬紪鍙�
+            QueryWrapper<SafeTraining> queryWrapper = new QueryWrapper<>();
+            queryWrapper.likeRight("course_code", datePrefix)
+                    .orderByDesc("course_code")
+                    .last("LIMIT 1");
+            SafeTraining lastSafeTraining = safeTrainingMapper.selectOne(queryWrapper);
+            int sequenceNumber = 1; // 榛樿搴忓彿
+            if (lastSafeTraining != null && lastSafeTraining.getCourseCode() != null) {
+                String lastNo = lastSafeTraining.getCourseCode().toString();
+                if (lastNo.startsWith(datePrefix)) {
+                    String seqStr = lastNo.substring(datePrefix.length());
+                    try {
+                        sequenceNumber = Integer.parseInt(seqStr) + 1;
+                    } catch (NumberFormatException e) {
+                        sequenceNumber = 1;
+                    }
+                }
+            }
+            // 鐢熸垚瀹屾暣鐨勮绋嬬紪鍙�
+            String no = "KC-" + String.format("%s%03d", datePrefix, sequenceNumber);
+            safeTraining.setCourseCode(no);
+        }
+        //鏍规嵁鏃堕棿鍒ゆ柇鍩硅鐘舵��
+        String trainingDate = safeTraining.getTrainingDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        LocalDateTime openingTime = LocalDateTime.parse((trainingDate + safeTraining.getOpeningTime()), DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss"));
+        LocalDateTime endTime = LocalDateTime.parse((trainingDate + safeTraining.getEndTime()), DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss"));
+        if (LocalDateTime.now().isBefore(openingTime)) {
+            //鏈紑濮�
+            safeTraining.setState(0);
+        } else if (LocalDateTime.now().isAfter(endTime)) {
+            //宸茬粨鏉�
+            safeTraining.setState(2);
+        } else {
+            //杩涜涓�
+            safeTraining.setState(1);
+        }
+        //鏂板鎴栨洿鏂�
+        saveOrUpdate(safeTraining);
+        return 0;
+    }
+
+    @Override
+    public SafeTrainingDto getSafeTraining(Long id) {
+        //涓昏〃鏁版嵁
+        SafeTrainingDto safeTrainingDto = safeTrainingMapper.getSafeTraining(id);
+        //闄勪欢
+        List<SafeTrainingFile> safeTrainingFiles = safeTrainingFileMapper.selectList(Wrappers.<SafeTrainingFile>lambdaQuery().eq(SafeTrainingFile::getSafeTrainingId, id));
+        safeTrainingDto.setSafeTrainingFileList(safeTrainingFiles);
+        //鍩硅璁板綍璇︽儏
+        List<SafeTrainingDetailsDto> safeTrainingDetailsDto = safeTrainingDetailsMapper.getSafeTraining(id);
+        safeTrainingDto.setSafeTrainingDetailsDtoList(safeTrainingDetailsDto);
+        return safeTrainingDto;
+    }
+
+    @Override
+    public int saveSafeTraining(SafeTrainingDto safeTrainingDto) {
+        //鏇存柊涓昏〃
+        safeTrainingMapper.updateById(safeTrainingDto);
+        //鏇存柊鍩硅璁板綍璇︽儏
+        safeTrainingDto.getSafeTrainingDetailsDtoList().forEach(safeTrainingDetailsDto -> {
+            safeTrainingDetailsMapper.updateById(safeTrainingDetailsDto);
+        });
+        return 0;
+    }
+
+    @Override
+    public int delSafeTraining(List<Integer> ids) {
+        //鍒犻櫎涓昏〃
+        safeTrainingMapper.deleteBatchIds(ids);
+        //鍒犻櫎闄勪欢
+        safeTrainingFileMapper.delete(Wrappers.<SafeTrainingFile>lambdaQuery().in(SafeTrainingFile::getSafeTrainingId, ids));
+        //鍒犻櫎鍩硅璁板綍
+        safeTrainingDetailsMapper.delete(Wrappers.<SafeTrainingDetails>lambdaQuery().in(SafeTrainingDetails::getSafeTrainingId, ids));
+        return 0;
+    }
+
+    @Override
+    public void export(HttpServletResponse response, Long id) {
+        SafeTrainingDto safeTrainingDto = safeTrainingMapper.getSafeTraining(id);
+        List<SafeTrainingDetailsDto> safeTrainingDetailsDtoList = safeTrainingDetailsMapper.getSafeTraining(id);
+        InputStream inputStream = this.getClass().getResourceAsStream("/static/safe-training.docx");
+        Configure configure = Configure.builder()
+                .bind("safeTrainingDetailsDtoList", new HackLoopTableRenderPolicy())
+                .build();
+        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
+                new HashMap<String, Object>() {{
+                    put("safeTrainingDto", safeTrainingDto);
+                    put("safeTrainingDetailsDtoList", safeTrainingDetailsDtoList);
+                }});
+
+        try {
+            response.setContentType("application/msword");
+            String fileName = URLEncoder.encode(
+                    safeTrainingDto.getCourseCode() + "鍩硅涓庤�冩牳璁″垝", "UTF-8");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-disposition",
+                    "attachment;filename=" + fileName + ".docx");
+            OutputStream os = response.getOutputStream();
+            template.write(os);
+            os.flush();
+            os.close();
+            inputStream.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("瀵煎嚭澶辫触");
+        }
+
+    }
+}
diff --git a/src/main/resources/mapper/safe/SafeAccidentMapper.xml b/src/main/resources/mapper/safe/SafeAccidentMapper.xml
index 159e7cd..63e9067 100644
--- a/src/main/resources/mapper/safe/SafeAccidentMapper.xml
+++ b/src/main/resources/mapper/safe/SafeAccidentMapper.xml
@@ -35,13 +35,13 @@
             and sa.accident_code like concat('%', #{c.accidentCode}, '%')
         </if>
         <if test="c.accidentName != null and c.accidentName != ''">
-            and sa.accident_name like concat('%', #{accidentName}, '%')
+            and sa.accident_name like concat('%', #{c.accidentName}, '%')
         </if>
         <if test="c.accidentType != null and c.accidentType != ''">
-            and sa.accident_type like concat('%', #{accidentType}, '%')
+            and sa.accident_type like concat('%', #{c.accidentType}, '%')
         </if>
         <if test="c.accidentGrade != null and c.accidentGrade != ''">
-            and sa.accident_grade like concat('%', #{accidentGrade}, '%')
+            and sa.accident_grade like concat('%', #{c.accidentGrade}, '%')
         </if>
     </select>
 
diff --git a/src/main/resources/mapper/safe/SafeTrainingDetailsMapper.xml b/src/main/resources/mapper/safe/SafeTrainingDetailsMapper.xml
new file mode 100644
index 0000000..c96d711
--- /dev/null
+++ b/src/main/resources/mapper/safe/SafeTrainingDetailsMapper.xml
@@ -0,0 +1,37 @@
+<?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.safe.mapper.SafeTrainingDetailsMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.safe.pojo.SafeTrainingDetails">
+        <id column="id" property="id"/>
+        <result column="user_id" property="userId"/>
+        <result column="safe_training_id" property="safeTrainingId"/>
+        <result column="examination_results" property="examinationResults"/>
+        <result column="create_time" property="createTime"/>
+        <result column="create_user" property="createUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="tenant_id" property="tenantId"/>
+    </resultMap>
+    <select id="getSafeTraining" resultType="com.ruoyi.safe.dto.SafeTrainingDetailsDto">
+        select std.*,
+               su.user_name,
+               su.nick_name,
+               su.phonenumber
+        from safe_training_details std
+                 left join sys_user su on std.user_id = su.user_id
+        where std.safe_training_id = #{id}
+    </select>
+    <select id="pageDetails" resultType="com.ruoyi.safe.pojo.SafeTrainingDetails">
+        select std.*,
+               st.*
+        from safe_training_details std
+                 left join safe_training st on std.safe_training_id = st.id
+        where std.user_id = #{c.userId}
+        <if test="c.trainingDate != null">
+            and st.training_date = #{c.trainingDate}
+        </if>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/safe/SafeTrainingFileMapper.xml b/src/main/resources/mapper/safe/SafeTrainingFileMapper.xml
new file mode 100644
index 0000000..34a42a6
--- /dev/null
+++ b/src/main/resources/mapper/safe/SafeTrainingFileMapper.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.safe.mapper.SafeTrainingFileMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.safe.pojo.SafeTrainingFile">
+        <id column="id" property="id" />
+        <result column="safe_training_id" property="safeTrainingId" />
+        <result column="name" property="name" />
+        <result column="url" property="url" />
+        <result column="file_size" property="fileSize" />
+        <result column="create_time" property="createTime" />
+        <result column="create_user" property="createUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="tenant_id" property="tenantId" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/safe/SafeTrainingMapper.xml b/src/main/resources/mapper/safe/SafeTrainingMapper.xml
new file mode 100644
index 0000000..473b5f2
--- /dev/null
+++ b/src/main/resources/mapper/safe/SafeTrainingMapper.xml
@@ -0,0 +1,59 @@
+<?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.safe.mapper.SafeTrainingMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.safe.pojo.SafeTraining">
+        <id column="id" property="id" />
+        <result column="course_code" property="courseCode" />
+        <result column="training_objectives" property="trainingObjectives" />
+        <result column="training_content" property="trainingContent" />
+        <result column="training_mode" property="trainingMode" />
+        <result column="state" property="state" />
+        <result column="participants" property="participants" />
+        <result column="place_training" property="placeTraining" />
+        <result column="training_lecturer" property="trainingLecturer" />
+        <result column="training_date" property="trainingDate" />
+        <result column="opening_time" property="openingTime" />
+        <result column="end_time" property="endTime" />
+        <result column="project_credits" property="projectCredits" />
+        <result column="class_hour" property="classHour" />
+        <result column="assessment_method" property="assessmentMethod" />
+        <result column="comprehensive_assessment" property="comprehensiveAssessment" />
+        <result column="remarks" property="remarks" />
+        <result column="assessment_user_id" property="assessmentUserId" />
+        <result column="assessment_date" property="assessmentDate" />
+        <result column="training_abstract" property="trainingAbstract" />
+        <result column="create_time" property="createTime" />
+        <result column="create_user" property="createUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="tenant_id" property="tenantId" />
+    </resultMap>
+    <select id="pageSafeTraining" resultType="com.ruoyi.safe.dto.SafeTrainingDto">
+        select st.*,
+               su.nick_name assessmentUserName,
+                count(std.id) nums
+        from safe_training st
+        left join safe_training_details std on std.safe_training_id = st.id
+        left join sys_user su on st.assessment_user_id = su.user_id
+        where 1=1
+        <if test="c.placeTraining != null and c.placeTraining != ''">
+            and st.place_training like concat('%', #{c.placeTraining}, '%')
+        </if>
+        <if test="c.trainingDate != null and c.trainingDate != ''">
+            and st.training_date = #{c.trainingDate}
+        </if>
+        <if test="c.state != null and c.state != ''">
+            and st.state like concat('%', #{c.state}, '%')
+        </if>
+    </select>
+    <select id="getSafeTraining" resultType="com.ruoyi.safe.dto.SafeTrainingDto">
+         select st.*,
+                su.nick_name assessmentUserName
+         from safe_training st
+                  left join sys_user su on st.assessment_user_id = su.user_id
+         where st.id=#{id}
+    </select>
+
+</mapper>
diff --git a/src/main/resources/static/safe-training-details.docx b/src/main/resources/static/safe-training-details.docx
new file mode 100644
index 0000000..d2b502f
--- /dev/null
+++ b/src/main/resources/static/safe-training-details.docx
Binary files differ
diff --git a/src/main/resources/static/safe-training.docx b/src/main/resources/static/safe-training.docx
new file mode 100644
index 0000000..0bc1d64
--- /dev/null
+++ b/src/main/resources/static/safe-training.docx
Binary files differ

--
Gitblit v1.9.3