浏览代码

ly 培训优化

ly 1 月之前
父节点
当前提交
abc8bd7934

+ 105 - 0
master/src/main/java/com/ruoyi/common/utils/file/OfficeConvertUtils.java

@@ -0,0 +1,105 @@
+package com.ruoyi.common.utils.file;
+
+import com.ruoyi.framework.config.RuoYiConfig;
+import org.jodconverter.DocumentConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.File;
+
+/**
+ * Office文件转换工具类
+ * 提供PPT转PDF等文件转换功能
+ * 
+ * @author ruoyi
+ */
+@Component
+public class OfficeConvertUtils {
+    
+    private static final Logger logger = LoggerFactory.getLogger(OfficeConvertUtils.class);
+    
+    @Resource
+    private DocumentConverter documentConverter;
+    
+    /**
+     * 将PPT文件转换为PDF
+     * 
+     * @param pptFilePath PPT文件路径(相对路径,如 /profile/upload/xxx.pptx)
+     * @return PDF文件路径,转换失败返回null
+     */
+    public String convertPptToPdf(String pptFilePath) {
+        try {
+            // 移除路径前缀 /profile
+            String relativePath = pptFilePath.replace("/profile", "");
+            
+            // 获取完整的文件路径
+            File pptFile = new File(RuoYiConfig.getProfile() + relativePath);
+            
+            if (!pptFile.exists()) {
+                logger.error("PPT文件不存在: {}", pptFile.getAbsolutePath());
+                return null;
+            }
+            
+            // 获取文件名(不含扩展名)
+            String fileName = pptFile.getName();
+            int lastDotIndex = fileName.lastIndexOf(".");
+            String fileNameWithoutExt = lastDotIndex > 0 ? fileName.substring(0, lastDotIndex) : fileName;
+            
+            // 生成PDF文件名和路径
+            String pdfFileName = fileNameWithoutExt + ".pdf";
+            File pdfFile = new File(pptFile.getParent(), pdfFileName);
+            
+            logger.info("开始转换PPT为PDF - 源文件: {}, 目标文件: {}", pptFile.getAbsolutePath(), pdfFile.getAbsolutePath());
+            
+            // 使用JODConverter进行转换
+            documentConverter.convert(pptFile).to(pdfFile).execute();
+            
+            // 构建返回的URL路径
+            String pdfRelativePath = relativePath.substring(0, relativePath.lastIndexOf("/") + 1) + pdfFileName;
+            String pdfUrl = "/profile" + pdfRelativePath;
+            
+            logger.info("PPT转PDF完成,PDF路径: {}", pdfUrl);
+            return pdfUrl;
+            
+        } catch (Exception e) {
+            logger.error("PPT转PDF失败: {}", e.getMessage());
+            e.printStackTrace();
+            return null;
+        }
+    }
+    
+    /**
+     * 判断文件是否为PPT格式
+     * 
+     * @param fileName 文件名
+     * @return 是否为PPT格式
+     */
+    public static boolean isPptFile(String fileName) {
+        if (fileName == null || fileName.trim().isEmpty()) {
+            return false;
+        }
+        String lowerCaseFileName = fileName.toLowerCase();
+        return lowerCaseFileName.endsWith(".ppt") || lowerCaseFileName.endsWith(".pptx");
+    }
+    
+    /**
+     * 将文件名后缀改为.pdf
+     * 
+     * @param fileName 原文件名
+     * @return PDF文件名
+     */
+    public static String changeFileNameToPdf(String fileName) {
+        if (fileName == null || fileName.trim().isEmpty()) {
+            return fileName;
+        }
+        int lastDotIndex = fileName.lastIndexOf(".");
+        if (lastDotIndex > 0) {
+            return fileName.substring(0, lastDotIndex) + ".pdf";
+        } else {
+            return fileName + ".pdf";
+        }
+    }
+}
+

+ 29 - 5
master/src/main/java/com/ruoyi/framework/task/trainingbcc/TrainingMatrixTask.java

@@ -22,6 +22,7 @@ import com.ruoyi.project.document.service.ITPlantproglistService;
 import com.ruoyi.project.common.domain.TCommonfile;
 import com.ruoyi.project.common.service.ITCommonfileService;
 import com.ruoyi.project.plant.mapper.TStaffmgrMapper;
+import com.ruoyi.common.utils.file.OfficeConvertUtils;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -67,6 +68,8 @@ public class TrainingMatrixTask extends BaseController
     private TTrainingbccRegularMapper tTrainingbccRegularMapper;
     @Resource
     private TStaffmgrMapper tStaffmgrMapper;
+    @Autowired
+    private OfficeConvertUtils officeConvertUtils;
 
     /**
      * 每年矩阵新增培训
@@ -234,12 +237,33 @@ public class TrainingMatrixTask extends BaseController
 
                 // 设置培训人和文件信息
                 tTraining.setTrainer(trainer);
-                tTraining.setFileUrl(fileUrl);
-                tTraining.setFileName(fileName);
+                
+                // 检查文件是否为PPT格式,如果是则转换为PDF
+                String finalFileUrl = fileUrl;
+                String finalFileName = fileName;
+                if (OfficeConvertUtils.isPptFile(fileName)) {
+                    try {
+                        logger.info("检测到PPT文件,开始转换为PDF: {}", fileName);
+                        String pdfUrl = officeConvertUtils.convertPptToPdf(fileUrl);
+                        if (pdfUrl != null && !pdfUrl.isEmpty()) {
+                            finalFileUrl = pdfUrl;
+                            finalFileName = OfficeConvertUtils.changeFileNameToPdf(fileName);
+                            logger.info("PPT文件转换成功,PDF文件路径: {}", pdfUrl);
+                        } else {
+                            logger.warn("PPT转PDF失败,使用原始文件");
+                        }
+                    } catch (Exception e) {
+                        logger.error("PPT转PDF过程中发生错误,使用原始文件: {}", e.getMessage());
+                        e.printStackTrace();
+                    }
+                }
+                
+                tTraining.setFileUrl(finalFileUrl);
+                tTraining.setFileName(finalFileName);
                 // 培训内容:去掉文件后缀
-                String content = fileName;
-                if (StringUtils.isNotEmpty(fileName) && fileName.contains(".")) {
-                    content = fileName.substring(0, fileName.lastIndexOf("."));
+                String content = finalFileName;
+                if (StringUtils.isNotEmpty(finalFileName) && finalFileName.contains(".")) {
+                    content = finalFileName.substring(0, finalFileName.lastIndexOf("."));
                 }
                 tTraining.setContent(content);
                 //添加学习时间

+ 55 - 10
master/src/main/java/com/ruoyi/project/document/controller/TPlantproglistController.java

@@ -33,6 +33,7 @@ import com.ruoyi.project.training.service.ITTrainingbccDeviceService;
 import com.ruoyi.project.training.service.ITTrainingbccService;
 import org.apache.commons.lang.StringUtils;
 import org.apache.poi.ss.usermodel.Cell;
+import com.ruoyi.common.utils.file.OfficeConvertUtils;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
@@ -79,6 +80,8 @@ public class TPlantproglistController extends BaseController
     private TStaffmgrMapper tStaffmgrMapper;
     @Autowired
     private ITCommonfileService tCommonfileService;
+    @Autowired
+    private OfficeConvertUtils officeConvertUtils;
     /**
      * 查询装置程序清单列表
      */
@@ -243,12 +246,33 @@ public class TPlantproglistController extends BaseController
             tTraining.setDeptId(tTrainingRegular.getDeptId());
             tTraining.setRegularId(tTrainingRegular.getId());
             tTraining.setPeriod(matrix.getFrequency());
-            tTraining.setFileUrl(fileUrl);
-            tTraining.setFileName(fileName);
+            
+            // 检查文件是否为PPT格式,如果是则转换为PDF
+            String finalFileUrl = fileUrl;
+            String finalFileName = fileName;
+            if (OfficeConvertUtils.isPptFile(fileName)) {
+                try {
+                    logger.info("检测到PPT文件,开始转换为PDF: {}", fileName);
+                    String pdfUrl = officeConvertUtils.convertPptToPdf(fileUrl);
+                    if (pdfUrl != null && !pdfUrl.isEmpty()) {
+                        finalFileUrl = pdfUrl;
+                        finalFileName = OfficeConvertUtils.changeFileNameToPdf(fileName);
+                        logger.info("PPT文件转换成功,PDF文件路径: {}", pdfUrl);
+                    } else {
+                        logger.warn("PPT转PDF失败,使用原始文件");
+                    }
+                } catch (Exception e) {
+                    logger.error("PPT转PDF过程中发生错误,使用原始文件: {}", e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+            
+            tTraining.setFileUrl(finalFileUrl);
+            tTraining.setFileName(finalFileName);
             // 培训内容:去掉文件后缀
-            String content = fileName;
-            if (StringUtils.isNotEmpty(fileName) && fileName.contains(".")) {
-                content = fileName.substring(0, fileName.lastIndexOf("."));
+            String content = finalFileName;
+            if (StringUtils.isNotEmpty(finalFileName) && finalFileName.contains(".")) {
+                content = finalFileName.substring(0, finalFileName.lastIndexOf("."));
             }
             tTraining.setContent(content);
             // 通过人名查找staffmgr,获取员工编号来赋值trainer
@@ -416,13 +440,34 @@ public class TPlantproglistController extends BaseController
         tTraining.setDeptId(103L); // BCC专用
         tTraining.setRegularId(null);
         tTraining.setPeriod(null); // 未指定频率
-        tTraining.setFileUrl(fileUrl);
-        tTraining.setFileName(fileName);
+        
+        // 检查文件是否为PPT格式,如果是则转换为PDF
+        String finalFileUrl = fileUrl;
+        String finalFileName = fileName;
+        if (OfficeConvertUtils.isPptFile(fileName)) {
+            try {
+                logger.info("检测到PPT文件,开始转换为PDF: {}", fileName);
+                String pdfUrl = officeConvertUtils.convertPptToPdf(fileUrl);
+                if (pdfUrl != null && !pdfUrl.isEmpty()) {
+                    finalFileUrl = pdfUrl;
+                    finalFileName = OfficeConvertUtils.changeFileNameToPdf(fileName);
+                    logger.info("PPT文件转换成功,PDF文件路径: {}", pdfUrl);
+                } else {
+                    logger.warn("PPT转PDF失败,使用原始文件");
+                }
+            } catch (Exception e) {
+                logger.error("PPT转PDF过程中发生错误,使用原始文件: {}", e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        
+        tTraining.setFileUrl(finalFileUrl);
+        tTraining.setFileName(finalFileName);
         
         // 培训内容:去掉文件后缀
-        String content = fileName;
-        if (StringUtils.isNotEmpty(fileName) && fileName.contains(".")) {
-            content = fileName.substring(0, fileName.lastIndexOf("."));
+        String content = finalFileName;
+        if (StringUtils.isNotEmpty(finalFileName) && finalFileName.contains(".")) {
+            content = finalFileName.substring(0, finalFileName.lastIndexOf("."));
         }
         tTraining.setContent(content);
         

+ 215 - 35
master/src/main/java/com/ruoyi/project/plant/controller/TStaffmgrController.java

@@ -5,6 +5,7 @@ import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.file.ExcelUtils;
 import com.ruoyi.common.utils.file.FileUploadUtils;
+import com.ruoyi.common.utils.file.OfficeConvertUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.aspectj.lang.annotation.Log;
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
@@ -115,6 +116,8 @@ public class TStaffmgrController extends BaseController {
     private ITPlantproglistService tPlantproglistService;
     @Autowired
     private ITCommonfileService tCommonfileService;
+    @Autowired
+    private OfficeConvertUtils officeConvertUtils;
     /**
      * 获取SAI整改负责人列表
      */
@@ -288,14 +291,8 @@ public class TStaffmgrController extends BaseController {
                 Iterator<TStaffmgr> iterator = list.iterator();
                 while (iterator.hasNext()) {
                     TStaffmgr t = iterator.next();
-                    if (t.getLeftDate() != null && t.getDelFlag() == 9) {
-                        Calendar cal = Calendar.getInstance();
-                        cal.setTime(t.getLeftDate());
-                        Integer year = cal.get(Calendar.YEAR);//获取年
-                        if (year < Integer.parseInt(tStaffmgr.getLeftYear())) {
-                            logger.debug(t.getName() + "离职年份小于数据");
-                            iterator.remove();
-                        }
+                    if (t.getDelFlag() == 9) {
+                        iterator.remove();
                     }
                 }
             }
@@ -579,7 +576,7 @@ public class TStaffmgrController extends BaseController {
             int currentYear = LocalDate.now().getYear();
             for (TTrainingMatrix matrix: list
             ) {
-                //矩阵
+                //计划
                 TTrainingbccRegular tTrainingRegular = new TTrainingbccRegular();
                 tTrainingRegular.setDeptId(matrix.getDeptId());
                 tTrainingRegular.setActualpostId(matrix.getActualpostId());
@@ -666,12 +663,33 @@ public class TStaffmgrController extends BaseController {
 
                 // 设置培训人和文件信息
                 tTraining.setTrainer(trainer);
-                tTraining.setFileUrl(fileUrl);
-                tTraining.setFileName(fileName);
+                
+                // 检查文件是否为PPT格式,如果是则转换为PDF
+                String finalFileUrl = fileUrl;
+                String finalFileName = fileName;
+                if (OfficeConvertUtils.isPptFile(fileName)) {
+                    try {
+                        logger.info("检测到PPT文件,开始转换为PDF: {}", fileName);
+                        String pdfUrl = officeConvertUtils.convertPptToPdf(fileUrl);
+                        if (pdfUrl != null && !pdfUrl.isEmpty()) {
+                            finalFileUrl = pdfUrl;
+                            finalFileName = OfficeConvertUtils.changeFileNameToPdf(fileName);
+                            logger.info("PPT文件转换成功,PDF文件路径: {}", pdfUrl);
+                        } else {
+                            logger.warn("PPT转PDF失败,使用原始文件");
+                        }
+                    } catch (Exception e) {
+                        logger.error("PPT转PDF过程中发生错误,使用原始文件: {}", e.getMessage());
+                        e.printStackTrace();
+                    }
+                }
+                
+                tTraining.setFileUrl(finalFileUrl);
+                tTraining.setFileName(finalFileName);
                 // 培训内容:去掉文件后缀
-                String content = fileName;
-                if (StringUtils.isNotEmpty(fileName) && fileName.contains(".")) {
-                    content = fileName.substring(0, fileName.lastIndexOf("."));
+                String content = finalFileName;
+                if (StringUtils.isNotEmpty(finalFileName) && finalFileName.contains(".")) {
+                    content = finalFileName.substring(0, finalFileName.lastIndexOf("."));
                 }
                 tTraining.setContent(content);
                 //添加学习时间
@@ -819,7 +837,7 @@ public class TStaffmgrController extends BaseController {
     }
 
     /**
-     * 修改人员管理
+     * 人员管理转岗
      */
     @PreAuthorize("@ss.hasPermi('plant:staffmgr:edit')")
     @Log(title = "人员管理转岗", businessType = BusinessType.UPDATE)
@@ -875,6 +893,50 @@ public class TStaffmgrController extends BaseController {
             }
         }
 
+        // 删除老岗位需要但新岗位不需要的未开始培训
+        TTrainingbccDevice queryDevice = new TTrainingbccDevice();
+        queryDevice.setStaffId(old.getStaffid());
+        List<TTrainingbccDevice> allDevices = trainingbccDeviceService.selectTTrainingbccDeviceList(queryDevice);
+        List<Long> toDeleteDeviceIds = new ArrayList<>();
+
+        for (TTrainingbccDevice device : allDevices) {
+            // 只处理未完成的培训
+            if (device.getExamState() != 1) {
+                // 通过regularId查找对应的TTrainingbcc
+                TTrainingbcc trainingBcc = trainingbccService.selectTTrainingbccById(device.getRegularId());
+                if (trainingBcc != null && trainingBcc.getRegularId() != null) {
+                    // 通过TTrainingbcc的regularId查找TTrainingbccRegular
+                    TTrainingbccRegular trainingRegular = tTrainingbccRegularService.selectTTrainingbccRegularById(trainingBcc.getRegularId());
+                    if (trainingRegular != null && trainingRegular.getActualpostId() != null) {
+                        String[] requiredPosts = trainingRegular.getActualpostId().split(",");
+                        boolean oldPostRequired = false;
+                        boolean newPostRequired = false;
+
+                        // 判断老岗位和新岗位是否需要此培训
+                        for (String post : requiredPosts) {
+                            if (post.trim().equals(old.getActualpost())) {
+                                oldPostRequired = true;
+                            }
+                            if (post.trim().equals(tStaffmgr.getActualpost())) {
+                                newPostRequired = true;
+                            }
+                        }
+
+                        // 如果老岗位需要但新岗位不需要,则标记删除
+                        if (oldPostRequired && !newPostRequired) {
+                            toDeleteDeviceIds.add(device.getId());
+                        }
+                    }
+                }
+            }
+        }
+
+        // 批量删除不需要的培训
+        if (toDeleteDeviceIds.size() > 0) {
+            Long[] deleteIds = toDeleteDeviceIds.toArray(new Long[0]);
+            trainingbccDeviceService.deleteTTrainingbccDeviceByIds(deleteIds);
+        }
+
         return toAjax(tStaffmgrService.updateTStaffmgr(tStaffmgr));
     }
 
@@ -923,17 +985,76 @@ public class TStaffmgrController extends BaseController {
     @Log(title = "人员管理", businessType = BusinessType.DELETE)
     @GetMapping("/left/{id}")
     public AjaxResult removeLeft(@PathVariable Long id) {
-        TTrainingrecords tTrainingrecords = tTrainingrecordsService.selectTTrainingrecordsBystaffId(id);
-        if (tTrainingrecords != null) {
-            tTrainingrecords.setDelFlag(9l);
-            tTrainingrecordsService.updateTTrainingrecords(tTrainingrecords);
+        logger.info("======== 开始执行删除离职人员操作 ========");
+        logger.info("传入参数 - 人员ID: {}", id);
+
+        try {
+            // 处理培训记录
+            logger.info("步骤1: 查询培训记录");
+            TTrainingrecords tTrainingrecords = tTrainingrecordsService.selectTTrainingrecordsBystaffId(id);
+            if (tTrainingrecords != null) {
+                logger.info("找到培训记录,ID: {},准备更新删除标志为9", tTrainingrecords.getId());
+                tTrainingrecords.setDelFlag(9l);
+                tTrainingrecordsService.updateTTrainingrecords(tTrainingrecords);
+                logger.info("培训记录删除标志更新成功");
+            } else {
+                logger.info("未找到培训记录,跳过此步骤");
+            }
+
+            // 获取人员信息
+            logger.info("步骤2: 查询人员信息");
+            TStaffmgr staffmgr = tStaffmgrService.selectTStaffmgrById(id);
+            if (staffmgr == null) {
+                logger.error("未找到ID为{}的人员信息", id);
+                return AjaxResult.error("未找到人员信息");
+            }
+            logger.info("查询到人员信息 - 员工号: {}, 姓名: {}, 部门ID: {}",
+                staffmgr.getStaffid(), staffmgr.getName(), staffmgr.getDeptId());
+
+            // 删除该人员所有的Trainingbccdevice培训
+            logger.info("步骤3: 删除BCC培训设备记录(使用简单查询)");
+            List<TTrainingbccDevice> bccDevices = tTrainingbccDeviceMapper.selectTTrainingbccDeviceListByStaffId(staffmgr.getStaffid());
+            logger.info("查询到BCC培训设备记录数量: {}", bccDevices.size());
+
+            if (bccDevices.size() != 0) {
+                Long[] bccDeviceIds = new Long[bccDevices.size()];
+                for (int m = 0; m < bccDevices.size(); m++) {
+                    bccDeviceIds[m] = bccDevices.get(m).getId();
+                }
+                logger.info("准备删除BCC培训设备记录(设置del_flag=9),ID列表: {}", Arrays.toString(bccDeviceIds));
+                int deletedCount = tTrainingbccDeviceMapper.deleteTTrainingbccDeviceByIdsForStaffLeft(bccDeviceIds);
+                logger.info("BCC培训设备记录删除成功,受影响行数: {}", deletedCount);
+            } else {
+                logger.info("该人员没有BCC培训设备记录,跳过删除");
+            }
+
+            // 王子文 2022年6月27日 添加
+            // 离职时修改继任者清单状态、培养计划状态
+            logger.info("步骤4: 更新继任者清单状态和培养计划状态");
+            Long staffId = Long.parseLong(staffmgr.getStaffid());
+            logger.info("员工号转换为Long: {}", staffId);
+
+            logger.info("更新继任者清单状态...");
+            stSuccessorService.updateStateByStaffId(staffId);
+            logger.info("继任者清单状态更新完成");
+
+            logger.info("更新培养计划状态...");
+            tStPlanService.updateStudyStateByStaffId(staffId);
+            logger.info("培养计划状态更新完成");
+
+            // 最终删除人员
+            logger.info("步骤5: 执行人员离职删除操作");
+            int result = tStaffmgrService.deleteLeftTStaffmgrByIds(id);
+            logger.info("人员离职删除操作完成,受影响行数: {}", result);
+
+            logger.info("======== 删除离职人员操作成功完成 ========");
+            return toAjax(result);
+
+        } catch (Exception e) {
+            logger.error("======== 删除离职人员操作失败 ========");
+            logger.error("异常信息: {}", e.getMessage(), e);
+            return AjaxResult.error("删除离职人员失败: " + e.getMessage());
         }
-        // 王子文 2022年6月27日 添加
-        // 离职时修改继任者清单状态、培养计划状态
-        Long staffId = Long.parseLong(tStaffmgrService.selectTStaffmgrById(id).getStaffid());
-        stSuccessorService.updateStateByStaffId(staffId);
-        tStPlanService.updateStudyStateByStaffId(staffId);
-        return toAjax(tStaffmgrService.deleteLeftTStaffmgrByIds(id));
     }
 
     /**
@@ -943,17 +1064,76 @@ public class TStaffmgrController extends BaseController {
     @Log(title = "人员管理", businessType = BusinessType.DELETE)
     @GetMapping("/retire/{id}")
     public AjaxResult removeRetire(@PathVariable Long id) {
-        TTrainingrecords tTrainingrecords = tTrainingrecordsService.selectTTrainingrecordsBystaffId(id);
-        if (tTrainingrecords != null) {
-            tTrainingrecords.setDelFlag(9l);
-            tTrainingrecordsService.updateTTrainingrecords(tTrainingrecords);
+        logger.info("======== 开始执行删除退休人员操作 ========");
+        logger.info("传入参数 - 人员ID: {}", id);
+
+        try {
+            // 处理培训记录
+            logger.info("步骤1: 查询培训记录");
+            TTrainingrecords tTrainingrecords = tTrainingrecordsService.selectTTrainingrecordsBystaffId(id);
+            if (tTrainingrecords != null) {
+                logger.info("找到培训记录,ID: {},准备更新删除标志为9", tTrainingrecords.getId());
+                tTrainingrecords.setDelFlag(9l);
+                tTrainingrecordsService.updateTTrainingrecords(tTrainingrecords);
+                logger.info("培训记录删除标志更新成功");
+            } else {
+                logger.info("未找到培训记录,跳过此步骤");
+            }
+
+            // 获取人员信息
+            logger.info("步骤2: 查询人员信息");
+            TStaffmgr staffmgr = tStaffmgrService.selectTStaffmgrById(id);
+            if (staffmgr == null) {
+                logger.error("未找到ID为{}的人员信息", id);
+                return AjaxResult.error("未找到人员信息");
+            }
+            logger.info("查询到人员信息 - 员工号: {}, 姓名: {}, 部门ID: {}",
+                staffmgr.getStaffid(), staffmgr.getName(), staffmgr.getDeptId());
+
+            // 删除该人员所有的Trainingbccdevice培训
+            logger.info("步骤3: 删除BCC培训设备记录(使用简单查询)");
+            List<TTrainingbccDevice> bccDevices = tTrainingbccDeviceMapper.selectTTrainingbccDeviceListByStaffId(staffmgr.getStaffid());
+            logger.info("查询到BCC培训设备记录数量: {}", bccDevices.size());
+
+            if (bccDevices.size() != 0) {
+                Long[] bccDeviceIds = new Long[bccDevices.size()];
+                for (int m = 0; m < bccDevices.size(); m++) {
+                    bccDeviceIds[m] = bccDevices.get(m).getId();
+                }
+                logger.info("准备删除BCC培训设备记录(设置del_flag=9),ID列表: {}", Arrays.toString(bccDeviceIds));
+                int deletedCount = tTrainingbccDeviceMapper.deleteTTrainingbccDeviceByIdsForStaffLeft(bccDeviceIds);
+                logger.info("BCC培训设备记录删除成功,受影响行数: {}", deletedCount);
+            } else {
+                logger.info("该人员没有BCC培训设备记录,跳过删除");
+            }
+
+            // 王子文 2022年6月27日 添加
+            // 离职时修改继任者清单状态、培养计划状态
+            logger.info("步骤4: 更新继任者清单状态和培养计划状态");
+            Long staffId = Long.parseLong(staffmgr.getStaffid());
+            logger.info("员工号转换为Long: {}", staffId);
+
+            logger.info("更新继任者清单状态...");
+            stSuccessorService.updateStateByStaffId(staffId);
+            logger.info("继任者清单状态更新完成");
+
+            logger.info("更新培养计划状态...");
+            tStPlanService.updateStudyStateByStaffId(staffId);
+            logger.info("培养计划状态更新完成");
+
+            // 最终删除人员
+            logger.info("步骤5: 执行人员退休删除操作");
+            int result = tStaffmgrService.deleteRetireTStaffmgrByIds(id);
+            logger.info("人员退休删除操作完成,受影响行数: {}", result);
+
+            logger.info("======== 删除退休人员操作成功完成 ========");
+            return toAjax(result);
+
+        } catch (Exception e) {
+            logger.error("======== 删除退休人员操作失败 ========");
+            logger.error("异常信息: {}", e.getMessage(), e);
+            return AjaxResult.error("删除退休人员失败: " + e.getMessage());
         }
-        // 王子文 2022年6月27日 添加
-        // 离职时修改继任者清单状态、培养计划状态
-        Long staffId = Long.parseLong(tStaffmgrService.selectTStaffmgrById(id).getStaffid());
-        stSuccessorService.updateStateByStaffId(staffId);
-        tStPlanService.updateStudyStateByStaffId(staffId);
-        return toAjax(tStaffmgrService.deleteRetireTStaffmgrByIds(id));
     }
 
     /**

+ 29 - 5
master/src/main/java/com/ruoyi/project/training/controller/TTrainingMatrixController.java

@@ -3,6 +3,7 @@ package com.ruoyi.project.training.controller;
 import com.alibaba.fastjson.JSON;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.file.ExcelUtils;
+import com.ruoyi.common.utils.file.OfficeConvertUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.aspectj.lang.annotation.Log;
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
@@ -78,6 +79,8 @@ public class TTrainingMatrixController extends BaseController {
     private ITCommonfileService tCommonfileService;
     @Autowired
     private TStaffmgrMapper tStaffmgrMapper;
+    @Autowired
+    private OfficeConvertUtils officeConvertUtils;
     /**
      * 查询培训矩阵列表
      */
@@ -366,12 +369,33 @@ public class TTrainingMatrixController extends BaseController {
 
             // 设置培训人和文件信息
             tTraining.setTrainer(trainer);
-            tTraining.setFileUrl(fileUrl);
-            tTraining.setFileName(fileName);
+            
+            // 检查文件是否为PPT格式,如果是则转换为PDF
+            String finalFileUrl = fileUrl;
+            String finalFileName = fileName;
+            if (OfficeConvertUtils.isPptFile(fileName)) {
+                try {
+                    logger.info("检测到PPT文件,开始转换为PDF: {}", fileName);
+                    String pdfUrl = officeConvertUtils.convertPptToPdf(fileUrl);
+                    if (pdfUrl != null && !pdfUrl.isEmpty()) {
+                        finalFileUrl = pdfUrl;
+                        finalFileName = OfficeConvertUtils.changeFileNameToPdf(fileName);
+                        logger.info("PPT文件转换成功,PDF文件路径: {}", pdfUrl);
+                    } else {
+                        logger.warn("PPT转PDF失败,使用原始文件");
+                    }
+                } catch (Exception e) {
+                    logger.error("PPT转PDF过程中发生错误,使用原始文件: {}", e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+            
+            tTraining.setFileUrl(finalFileUrl);
+            tTraining.setFileName(finalFileName);
             // 培训内容:去掉文件后缀
-            String content = fileName;
-            if (StringUtils.isNotEmpty(fileName) && fileName.contains(".")) {
-                content = fileName.substring(0, fileName.lastIndexOf("."));
+            String content = finalFileName;
+            if (StringUtils.isNotEmpty(finalFileName) && finalFileName.contains(".")) {
+                content = finalFileName.substring(0, finalFileName.lastIndexOf("."));
             }
             tTraining.setContent(content);
             trainingbccService.insertTTrainingbcc(tTraining);

+ 16 - 0
master/src/main/java/com/ruoyi/project/training/mapper/TTrainingbccDeviceMapper.java

@@ -69,4 +69,20 @@ public interface TTrainingbccDeviceMapper
     int countTraining(TTrainingbccDevice device);
 
     void deleteTTrainingbccDeviceByTrainingbcc(Long[] ids);
+
+    /**
+     * 根据员工编号查询BCC培训设备记录列表(简单查询,不包含复杂业务逻辑)
+     * 
+     * @param staffId 员工编号
+     * @return BCC培训设备记录集合
+     */
+    List<TTrainingbccDevice> selectTTrainingbccDeviceListByStaffId(String staffId);
+
+    /**
+     * 批量删除BCC培训设备记录(用于离职/退休,设置del_flag=9)
+     * 
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    int deleteTTrainingbccDeviceByIdsForStaffLeft(Long[] ids);
 }

+ 34 - 0
master/src/main/resources/mybatis/training/TTrainingbccDeviceMapper.xml

@@ -217,5 +217,39 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     </select>
 
+    <!-- 根据员工编号查询BCC培训设备记录列表 -->
+    <select id="selectTTrainingbccDeviceListByStaffId" parameterType="String" resultMap="TTrainingbccDeviceResult">
+        select
+            d.id,
+            d.staff_id,
+            d.regular_id,
+            d.start_date,
+            d.remarks,
+            d.del_flag,
+            d.creater_code,
+            d.createdate,
+            d.updater_code,
+            d.updatedate,
+            d.supplementary,
+            d.learn_state,
+            d.exam_state,
+            d.exam_id,
+            d.exam_num,
+            d.learn_time,
+            d.finish_date
+        from t_trainingbcc_device d
+        where d.staff_id = #{staffId}
+          and d.del_flag = 0
+        order by d.id
+    </select>
+
+    <!-- 批量删除BCC培训设备记录(用于离职/退休,设置del_flag=9) -->
+    <update id="deleteTTrainingbccDeviceByIdsForStaffLeft" parameterType="String">
+        update t_trainingbcc_device set del_flag = 9 where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
 
 </mapper>

+ 61 - 24
ui/src/views/plant/organization/branch.vue

@@ -4,7 +4,7 @@
         'liequal': item.post.trim()==='安全专员'},{
         'litop': item.post.trim()==='首席经理'
         }]">
-        <div class="branch-box" @click.prevent="clickHandle(item)" v-if="item.post.trim() !== 'EHS督导'">
+        <div class="branch-box" @click.prevent="clickHandle(item)" v-if="item.post.trim() !== '安全专员'">
           <div class="branch-title">{{item.post}}</div>
           <img class="branch-pic" :src=item.img>
           <div class="branch-name">{{item.label}}</div>
@@ -246,33 +246,39 @@ export default {
   ul li:before{
     content: '';
     position: absolute;
-    width: 50%;
+    width: calc(50% + 1px);
     top: 20px;
     height: 20px;
-    left: -0.5px;
-    border-top:1px solid #000;
-    border-right: 1px solid #000000;
+    left: -1px;
+    border-top: 2px solid #2E6491;
+    border-right: 2px solid #2E6491;
+    border-radius: 0 2px 0 0;
   }
   ul li:after{
     content: '';
     position: absolute;
-    width: 50%;
+    width: calc(50% + 1px);
     top: 20px;
     height: 20px;
-    left: calc(50% - 0.5px);
-    border-top:1px solid #000;
-    border-left: 1px solid #000000;
+    left: calc(50% - 1px);
+    border-top: 2px solid #2E6491;
+    border-left: 2px solid #2E6491;
+    border-radius: 2px 0 0 0;
   }
   ul li:first-child:before,ul li:last-child:after{
     content: none;
   }
   ul:before{
     content: '';
-    height: 20px;
-    width: 1px;
-    background-color: #000000;
+    height: 22px;
+    width: 2px;
+    background-color: #2E6491;
     position: absolute;
-    top: 0px;
+    top: -1px;
+    left: 50%;
+    transform: translateX(-50%);
+    border-radius: 1px;
+    z-index: 1;
   }
   ul:first-child:before{
     content: none;
@@ -288,25 +294,33 @@ export default {
     margin-top: 40px;
   }
   ul.double:before{
-    height: calc(100% - 92px);
+    height: calc(100% - 90px);
     left: 50%;
+    width: 2px;
+    background-color: #2E6491;
+    transform: translateX(-50%);
+    border-radius: 1px;
+    top: 0;
+    z-index: 1;
   }
   ul.double> li{
     padding-top: 0;
   }
   ul.double>li:after,ul.double> li:before{
-    width: 21px;
-    height: 1px;
+    width: 27px;
+    height: 2px;
     top: 60%;
-    border-left: 0;
-    border-right: 0;
+    background-color: #2E6491;
+    border: none;
+    border-radius: 1px;
+    z-index: 2;
   }
   ul.double> li:nth-child(2n+1):after{
-    right: -21px;
+    right: -25px;
     left: auto;
   }
   ul.double >li:nth-child(2n):before{
-    left: -20px;
+    left: -25px;
   }
   ul.double >li:nth-child(2n+1):before,ul.double >li:nth-child(2n):after{content: none;}
   ul.double >li:nth-child(2n+1):last-child:after{
@@ -314,6 +328,24 @@ export default {
   }
   .iconfont{
     color: #ff0000;
+    width: 16px;
+    display: inline-block;
+    text-align: center;
+  }
+  .iconfont.icon-kache{
+    color: #28a745 !important; /* 绿色:卡车 */
+  }
+  .iconfont.icon-chache{
+    color: #28a745 !important; /* 绿色:叉车 */
+    -webkit-text-stroke: 1px #28a745; /* 镂空效果 */
+    -webkit-text-fill-color: transparent; /* 透明填充 */
+  }
+  .iconfont.icon-plus,
+  .iconfont.icon-round{
+    color: #f0ad4e !important; /* 黄色:急救、消防员 */
+  }
+  .iconfont.icon-square{
+    color: #2E6491 !important; /* 蓝色:安全代表 */
   }
   .bz-box{
     width: 18px;
@@ -327,9 +359,12 @@ export default {
   ul.level:after{
     content: '';
     position: absolute;
-    width: 1px;
+    width: 2px;
     height: 100%;
-    background-color: #000000;
+    background-color: #2E6491;
+    left: 50%;
+    transform: translateX(-50%);
+    border-radius: 1px;
   }
   ul li.surplus{
     visibility: hidden;
@@ -340,9 +375,11 @@ export default {
   .org-chart ul li .line {
     width: 2px;
     height: 30px;
-    background-color: #ccc;
+    background-color: #2E6491;
     position: absolute;
-    left: -5px;
+    left: 50%;
     top: 0;
+    transform: translateX(-50%);
+    border-radius: 1px;
   }
 </style>

+ 52 - 4
ui/src/views/plant/organization/index.vue

@@ -147,7 +147,7 @@ export default {
       },
       units: [],
       teams: [],
-      actualposts: ["24","25", "26", "14", "16", "18", "20", "12", "10", "34", "36","15","11"],
+      actualposts: ["24","25", "26", "14", "16", "18", "20", "12", "10", "34", "36","15","11","22","44"],
       dragMove: true,
       drawer: false,
       info: {},
@@ -542,7 +542,7 @@ export default {
       let zoom = parseInt(obj.style.zoom, 10) || 100
       // 滚轮滚一下wheelDelta的值增加或减少120
       zoom += event.wheelDelta / 12
-      if (zoom > 50) {
+      if (zoom > 20) {
         obj.style.zoom = zoom + '%'
       }
       return false
@@ -693,7 +693,7 @@ export default {
               map[item.pId].secretary ? map[item.pId].secretary[0].push(item) : map[item.pId].secretary[0] = [item];
             } else if (item.post == '生产主管' || item.post == '工长') {
               map[item.pId].secretary[0].push(item)
-            }else if ((item.post == '资深工程师' || item.post == '工程师') && map[item.pId].pId == 0) { //直属装置经理的资深工程师
+            }else if ((item.post == '资深工程师' || item.post == 'OTS专员' || item.post == '工程师') && map[item.pId].pId == 0) { //直属装置经理的资深工程师/OTS专员
               map[item.pId].secretary ? map[item.pId].secretary[0].push(item) : map[item.pId].secretary[0] = [item];
             } else {
               map[item.pId].children ? map[item.pId].children.push(item) : map[item.pId].children = [item];
@@ -706,7 +706,7 @@ export default {
               map[item.pId].secretary ? map[item.pId].secretary[0].push(item) : map[item.pId].secretary[0] = [item];
             } else if (item.post == '生产主管' || item.post == '工长') {
               map[item.pId].secretary[1].push(item)
-            }else if ((item.post == '资深工程师' || item.post == '工程师') && map[item.pId].pId == 0) { //直属装置经理的资深工程师
+            }else if ((item.post == '资深工程师' || item.post == 'OTS专员' || item.post == '工程师') && map[item.pId].pId == 0) { //直属装置经理的资深工程师/OTS专员
               map[item.pId].secretary ? map[item.pId].secretary[0].push(item) : map[item.pId].secretary[0] = [item];
             } else {
               map[item.pId].children ? map[item.pId].children.push(item) : map[item.pId].children = [item];
@@ -715,6 +715,54 @@ export default {
         }
 
       });
+
+      // 对每个节点的children进行重新排序
+      list.forEach(item => {
+        if (item.children && item.children.length > 0) {
+          // 按照职位优先级排序:片区工长 -> 装置副经理 -> 生产主管 -> 工长 -> 职员 -> 倒班班长 -> 主操(白班)
+          item.children.sort((a, b) => {
+            const getPriority = (post) => {
+              switch(post) {
+                case '片区工长': return 1;
+                case '装置副经理': return 2;
+                case '生产主管': return 3;
+                case '工长': return 4;
+                case '职员': return 5;
+                case '倒班班长': return 6;
+                case '主操(白班)': return 7;
+                default: return 8;
+              }
+            };
+            return getPriority(a.post) - getPriority(b.post);
+          });
+        }
+
+        // 对secretary数组进行排序
+        if (item.secretary && item.secretary.length > 0) {
+          item.secretary.forEach(secretaryGroup => {
+            if (Array.isArray(secretaryGroup) && secretaryGroup.length > 0) {
+              secretaryGroup.sort((a, b) => {
+                const getPriority = (post) => {
+                  switch(post) {
+                    case '安全专员': return 1;
+                    case '首席专家': return 1;
+                    case '装置经理': return 2;
+                    case '生产主管': return 3;
+                    case '工长': return 3;
+                    case '资深工程师': return 4;
+                    case 'OTS专员': return 4;
+                    case '工程师': return 4;
+                    case '主操(白班)': return 5;
+                    default: return 6;
+                  }
+                };
+                return getPriority(a.post) - getPriority(b.post);
+              });
+            }
+          });
+        }
+      });
+
       return list.filter(item => {
         if (item.pId === 0) {
           return item;

+ 21 - 21
ui/src/views/training/bccregular/new-regular.vue

@@ -82,7 +82,7 @@
         </template>
       </el-table-column>
       <el-table-column :label="$t('授课人')" align="center" prop="lecturerName" width="230" :show-overflow-tooltip="true" />
-      <el-table-column :label="$t('时')" align="center" prop="hour" :show-overflow-tooltip="true"/>
+      <el-table-column :label="$t('时长(小时)')" align="center" prop="hour" :show-overflow-tooltip="true"/>
       <el-table-column :label="$t('备注')" align="center" prop="remarks" width="300" :show-overflow-tooltip="true"/>
       <el-table-column :label="$t('操作')" align="center" fixed="right" width="120" class-name="small-padding fixed-width">
         <template slot-scope="scope">
@@ -206,8 +206,8 @@
             </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item :label="$t('时')" prop="hour">
-          <el-input v-model="form.hour" :placeholder="$t('请输入') + $t('时')" />
+        <el-form-item :label="$t('时长(小时)')" prop="hour">
+          <el-input v-model="form.hour" :placeholder="$t('请输入') + $t('时长(小时)')" />
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -362,24 +362,24 @@ export default {
       let _this = this
       listBccregularNew(this.queryParams).then(response => {
         this.regularList = response.rows;
-        // this.regularList.forEach(function (value,key,arr) {
-        //   let stffmgrName = "";
-        //   if (value.lecturer != null) {
-        //     let staffId = value.lecturer.split(",");
-        //     staffId.forEach(function (id, index) {
-        //       _this.stffmgrOptions.forEach(function (item) {
-        //         if (item.staffid === id) {
-        //           if (index === 0) {
-        //             stffmgrName = item.name
-        //           }else {
-        //             stffmgrName = stffmgrName + "," + item.name
-        //           }
-        //         }
-        //       });
-        //     });
-        //   }
-        //   _this.regularList[key].lecturerName = stffmgrName
-        // });
+        this.regularList.forEach(function (value,key,arr) {
+          let stffmgrName = "";
+          if (value.lecturer != null) {
+            let staffId = value.lecturer.split(",");
+            staffId.forEach(function (id, index) {
+              _this.stffmgrOptions.forEach(function (item) {
+                if (item.staffid === id) {
+                  if (index === 0) {
+                    stffmgrName = item.name
+                  }else {
+                    stffmgrName = stffmgrName + "," + item.name
+                  }
+                }
+              });
+            });
+          }
+          _this.regularList[key].lecturerName = stffmgrName
+        });
         this.total = response.total;
         this.loading = false;
         this.$nextTick(() => {

+ 3 - 3
ui/src/views/training/bccregular/regular.vue

@@ -450,17 +450,17 @@ export default {
     },
     // 判断是否需要隐藏列
     shouldHideColumn(columnLabel) {
-      // 基础隐藏的列
+      // 基础隐藏的列(精确匹配)
       const baseHideColumns = ['工程师', '见习生', '机械维修经理', '电仪维修经理'];
 
       // 如果用户homeType为6,额外隐藏更多列
       if (this.$store.state.user.homeType == 6) {
         const additionalHideColumns = ['首席专家', '片区工长', 'OTS培训专员'];
         const allHideColumns = [...baseHideColumns, ...additionalHideColumns];
-        return allHideColumns.some(hideLabel => columnLabel.includes(hideLabel));
+        return allHideColumns.some(hideLabel => columnLabel === hideLabel);
       }
 
-      return baseHideColumns.some(hideLabel => columnLabel.includes(hideLabel));
+      return baseHideColumns.some(hideLabel => columnLabel === hideLabel);
     },
     // 获取岗位在原始数组中的索引
     getOriginalIndex(item) {

+ 3 - 3
ui/src/views/training/regular/index.vue

@@ -473,17 +473,17 @@
       },
       // 判断是否需要隐藏列
       shouldHideColumn(columnLabel) {
-        // 基础隐藏的列
+        // 基础隐藏的列(精确匹配)
         const baseHideColumns = ['工程师', '见习生', '机械维修经理', '电仪维修经理'];
 
         // 如果用户homeType为6,额外隐藏更多列
         if (this.$store.state.user.homeType == 6) {
           const additionalHideColumns = ['首席专家', '片区工长', 'OTS培训专员'];
           const allHideColumns = [...baseHideColumns, ...additionalHideColumns];
-          return allHideColumns.some(hideLabel => columnLabel.includes(hideLabel));
+          return allHideColumns.some(hideLabel => columnLabel === hideLabel);
         }
 
-        return baseHideColumns.some(hideLabel => columnLabel.includes(hideLabel));
+        return baseHideColumns.some(hideLabel => columnLabel === hideLabel);
       },
       // 获取岗位在原始数组中的索引
       getOriginalIndex(item) {

+ 3 - 3
ui/src/views/training/trainingbcc/index.vue

@@ -173,7 +173,7 @@
       </el-table-column>
       <el-table-column :label="$t('培训频率')" align="center" prop="period" :show-overflow-tooltip="true"/>
       <el-table-column label="学习时长(min)" align="center" prop="timerNeed" :show-overflow-tooltip="true"/>
-      <el-table-column :label="$t('时')" align="center" prop="duration" :show-overflow-tooltip="true"/>
+      <el-table-column :label="$t('时长(小时)')" align="center" prop="duration" :show-overflow-tooltip="true"/>
       <el-table-column :label="$t('培训人')" align="center" prop="trainer" :show-overflow-tooltip="true"/>
       <el-table-column label="课件" align="center" width="120">
         <template slot-scope="scope">
@@ -303,8 +303,8 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item :label="$t('时')" prop="duration">
-              <el-input v-model="form.duration" :placeholder="$t('请输入') + $t('时')" />
+            <el-form-item :label="$t('时长(小时)')" prop="duration">
+              <el-input v-model="form.duration" :placeholder="$t('请输入') + $t('时长(小时)')" />
             </el-form-item>
           </el-col>
         </el-row>