diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApplicationController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApplicationController.java index 20f43e90..5121a954 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApplicationController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApplicationController.java @@ -278,7 +278,9 @@ public class RsTrainingApplicationController extends BaseController { @GetMapping("/my") public TableDataInfo listMyApplications(RsTrainingApplication application, PageQuery pageQuery) { - return trainingApplicationService.selectPageMyApplications(StpUtil.getLoginIdAsLong(), application, pageQuery); + // 安全获取用户ID,兼容 "sys_user:2091" 格式 + Long userId = getCurrentUserInfo().getUserId(); + return trainingApplicationService.selectPageMyApplications(userId, application, pageQuery); } /** @@ -466,18 +468,26 @@ public class RsTrainingApplicationController extends BaseController { return R.ok(new ArrayList<>()); } - // 解析用户ID列表 + // 解析用户ID列表(兼容 Redis 缓存键格式 "sys_user:2091") List userIdList = new ArrayList<>(); try { String[] idArray = userIds.split(","); for (String idStr : idArray) { if (StringUtils.isNotBlank(idStr)) { - userIdList.add(Long.parseLong(idStr.trim())); + String trimmedId = idStr.trim(); + // 处理 Redis 缓存键格式 "sys_user:2091" -> "2091" + if (trimmedId.contains(":")) { + String[] parts = trimmedId.split(":"); + if (parts.length > 1) { + trimmedId = parts[1]; // 提取冒号后面的数字部分 + } + } + userIdList.add(Long.parseLong(trimmedId)); } } } catch (NumberFormatException e) { - log.error("解析用户ID失败: {}", e.getMessage()); - return R.fail("用户ID格式错误"); + log.error("解析用户ID失败,原始字符串: {}, 错误: {}", userIds, e.getMessage()); + return R.fail("用户ID格式错误: " + e.getMessage()); } if (CollUtil.isEmpty(userIdList)) { diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApprovalController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApprovalController.java index f6af716c..6acaf2c5 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApprovalController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApprovalController.java @@ -21,6 +21,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.stp.StpUtil; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; /** * 培训审批控制器 @@ -28,6 +29,7 @@ import jakarta.servlet.http.HttpServletResponse; * @author weizzzz * @date 2025-07-16 */ +@Slf4j @RestController @RequestMapping("/rs/training/approval") public class RsTrainingApprovalController extends BaseController { @@ -102,7 +104,38 @@ public class RsTrainingApprovalController extends BaseController { @SaCheckPermission("rs:trainingapproval:process") @GetMapping("/pending") public TableDataInfo listPendingApprovals(RsTrainingApproval approval, PageQuery pageQuery) { - return trainingApprovalService.selectPagePendingApprovals(StpUtil.getLoginIdAsLong(), approval, pageQuery); + // 安全获取用户ID,兼容 "sys_user:2091" 格式 + Long userId = getSafeUserId(); + return trainingApprovalService.selectPagePendingApprovals(userId, approval, pageQuery); + } + + /** + * 安全获取当前登录用户ID,兼容多种格式 + */ + private Long getSafeUserId() { + try { + Object loginId = StpUtil.getLoginId(); + if (loginId instanceof Long) { + return (Long) loginId; + } else if (loginId instanceof String) { + String loginIdStr = (String) loginId; + // 处理 "sys_user:2091" 格式 + if (loginIdStr.contains(":")) { + String[] parts = loginIdStr.split(":"); + if (parts.length > 1) { + return Long.parseLong(parts[1]); + } + } else { + return Long.parseLong(loginIdStr); + } + } else if (loginId instanceof Number) { + return ((Number) loginId).longValue(); + } + } catch (Exception e) { + log.error("❌ 获取用户ID失败: {}", e.getMessage()); + throw new RuntimeException("无法获取当前用户ID,请重新登录"); + } + throw new RuntimeException("无法获取当前用户ID,请重新登录"); } /** diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingRecordController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingRecordController.java index 26e2c0e2..c9d7d31d 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingRecordController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingRecordController.java @@ -150,7 +150,38 @@ public class RsTrainingRecordController extends BaseController { @SaCheckPermission("rs:trainingrecord:query") @GetMapping("/my") public TableDataInfo listMyRecords(RsTrainingRecord record, PageQuery pageQuery) { - return trainingRecordService.selectPageMyRecords(StpUtil.getLoginIdAsLong(), record, pageQuery); + // 安全获取用户ID,兼容 "sys_user:2091" 格式 + Long userId = getSafeUserId(); + return trainingRecordService.selectPageMyRecords(userId, record, pageQuery); + } + + /** + * 安全获取当前登录用户ID,兼容多种格式 + */ + private Long getSafeUserId() { + try { + Object loginId = StpUtil.getLoginId(); + if (loginId instanceof Long) { + return (Long) loginId; + } else if (loginId instanceof String) { + String loginIdStr = (String) loginId; + // 处理 "sys_user:2091" 格式 + if (loginIdStr.contains(":")) { + String[] parts = loginIdStr.split(":"); + if (parts.length > 1) { + return Long.parseLong(parts[1]); + } + } else { + return Long.parseLong(loginIdStr); + } + } else if (loginId instanceof Number) { + return ((Number) loginId).longValue(); + } + } catch (Exception e) { + log.error("❌ 获取用户ID失败: {}", e.getMessage()); + throw new RuntimeException("无法获取当前用户ID,请重新登录"); + } + throw new RuntimeException("无法获取当前用户ID,请重新登录"); } /** diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalServiceImpl.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalServiceImpl.java index 0742d3b3..11cbc923 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApprovalServiceImpl.java @@ -220,7 +220,31 @@ public class RsOverseasApprovalServiceImpl implements IRsOverseasApprovalService * 应用数据权限过滤 */ private void applyDataPermissionFilter(RsOverseasApprovalQuery query) { - Long currentUserId = StpUtil.getLoginIdAsLong(); + // 安全获取用户ID,兼容 "sys_user:2091" 格式 + Long currentUserId = null; + try { + Object loginId = StpUtil.getLoginId(); + if (loginId instanceof Long) { + currentUserId = (Long) loginId; + } else if (loginId instanceof String) { + String loginIdStr = (String) loginId; + // 处理 "sys_user:2091" 格式 + if (loginIdStr.contains(":")) { + String[] parts = loginIdStr.split(":"); + if (parts.length > 1) { + currentUserId = Long.parseLong(parts[1]); + } + } else { + currentUserId = Long.parseLong(loginIdStr); + } + } else if (loginId instanceof Number) { + currentUserId = ((Number) loginId).longValue(); + } + } catch (Exception e) { + log.error("❌ 获取用户ID失败: {}", e.getMessage()); + throw new RuntimeException("无法获取当前用户ID,请重新登录"); + } + String currentUsername = LoginHelper.getUsername(); Long currentDeptId = LoginHelper.getDeptId(); boolean isSuperAdmin = LoginHelper.isSuperAdmin(); diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsTrainingApprovalServiceImpl.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsTrainingApprovalServiceImpl.java index 7497f1a5..52095718 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsTrainingApprovalServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsTrainingApprovalServiceImpl.java @@ -497,7 +497,31 @@ public class RsTrainingApprovalServiceImpl implements IRsTrainingApprovalService * 应用数据权限过滤 */ private void applyDataPermissionFilter(RsTrainingApproval approval) { - Long currentUserId = StpUtil.getLoginIdAsLong(); + // 安全获取用户ID,兼容 "sys_user:2091" 格式 + Long currentUserId = null; + try { + Object loginId = StpUtil.getLoginId(); + if (loginId instanceof Long) { + currentUserId = (Long) loginId; + } else if (loginId instanceof String) { + String loginIdStr = (String) loginId; + // 处理 "sys_user:2091" 格式 + if (loginIdStr.contains(":")) { + String[] parts = loginIdStr.split(":"); + if (parts.length > 1) { + currentUserId = Long.parseLong(parts[1]); + } + } else { + currentUserId = Long.parseLong(loginIdStr); + } + } else if (loginId instanceof Number) { + currentUserId = ((Number) loginId).longValue(); + } + } catch (Exception e) { + log.error("❌ 获取用户ID失败: {}", e.getMessage()); + throw new RuntimeException("无法获取当前用户ID,请重新登录"); + } + String currentUsername = LoginHelper.getUsername(); Long currentDeptId = LoginHelper.getDeptId(); boolean isSuperAdmin = LoginHelper.isSuperAdmin(); diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsTrainingApprovalMapper.xml b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsTrainingApprovalMapper.xml index 5b08ea35..561335ed 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsTrainingApprovalMapper.xml +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsTrainingApprovalMapper.xml @@ -125,7 +125,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" FROM rs_training_approval a LEFT JOIN rs_training_application app ON a.application_id = app.application_id AND app.deleted = '0' LEFT JOIN rs_training_course c ON app.course_id = c.course_id AND c.deleted = '0' - LEFT JOIN sys_user u ON app.user_id = u.user_id AND u.deleted = '0' + LEFT JOIN sys_user u ON app.user_id = u.user_id AND u.del_flag = '0' a.deleted = '0' @@ -149,7 +149,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" SELECT 1 FROM sys_user u WHERE u.user_id = app.user_id AND u.dept_id = #{deptId} - AND u.deleted = '0' + AND u.del_flag = '0' ) ) @@ -206,7 +206,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" FROM rs_training_approval a LEFT JOIN rs_training_application app ON a.application_id = app.application_id AND app.deleted = '0' LEFT JOIN rs_training_course c ON app.course_id = c.course_id AND c.deleted = '0' - LEFT JOIN sys_user u ON app.user_id = u.user_id AND u.deleted = '0' + LEFT JOIN sys_user u ON app.user_id = u.user_id AND u.del_flag = '0' a.deleted = '0' @@ -230,7 +230,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" SELECT 1 FROM sys_user u WHERE u.user_id = app.user_id AND u.dept_id = #{deptId} - AND u.deleted = '0' + AND u.del_flag = '0' ) ) diff --git a/web/src/views/rs/trainingApproval/index.vue b/web/src/views/rs/trainingApproval/index.vue index a5f4fd4b..e4dd0ec9 100644 --- a/web/src/views/rs/trainingApproval/index.vue +++ b/web/src/views/rs/trainingApproval/index.vue @@ -268,14 +268,16 @@ {{ applicationDetail.currentPosition || '暂无' }} {{ parseTime(applicationDetail.workStartTime) || '暂无' }} {{ applicationDetail.phone || '暂无' }} - + {{ applicationDetail.organizerUnit || applicationDetail.courseName || '暂无' }} {{ applicationDetail.location }} {{ parseTime(applicationDetail.startTime) }} {{ parseTime(applicationDetail.endTime) }} {{ applicationDetail.trainingType || '暂无' }} - {{ applicationDetail.trainingFee ? `${applicationDetail.trainingFee}元` : '暂无' }} + {{ + applicationDetail.trainingFee ? `${applicationDetail.trainingFee}元` : '暂无' + }} {{ parseTime(applicationDetail.applyTime) }} {{ getCompanionNames(applicationDetail.companions) || '无' }} - {{ applicationDetail.trainingPurpose || applicationDetail.reason || '暂无' }} + {{ + applicationDetail.trainingPurpose || applicationDetail.reason || '暂无' + }} {{ applicationDetail.remark || '无' }} @@ -313,9 +317,9 @@ -
-
- +
+
+ 申请附件
@@ -328,9 +332,7 @@ @@ -398,14 +400,18 @@ {{ approvalRecordDetail.currentPosition || '暂无' }} {{ parseTime(approvalRecordDetail.workStartTime) || '暂无' }} {{ approvalRecordDetail.phone || '暂无' }} - + - {{ approvalRecordDetail.organizerUnit || approvalRecordDetail.courseName || '暂无' }} + {{ + approvalRecordDetail.organizerUnit || approvalRecordDetail.courseName || '暂无' + }} {{ approvalRecordDetail.location }} {{ parseTime(approvalRecordDetail.startTime) }} {{ parseTime(approvalRecordDetail.endTime) }} {{ approvalRecordDetail.trainingType || '暂无' }} - {{ approvalRecordDetail.trainingFee ? `${approvalRecordDetail.trainingFee}元` : '暂无' }} + {{ + approvalRecordDetail.trainingFee ? `${approvalRecordDetail.trainingFee}元` : '暂无' + }} {{ parseTime(approvalRecordDetail.applyTime) }} {{ getCompanionNames(approvalRecordDetail.companions) || '无' }} - {{ approvalRecordDetail.trainingPurpose || approvalRecordDetail.reason || '暂无' }} + {{ + approvalRecordDetail.trainingPurpose || approvalRecordDetail.reason || '暂无' + }} {{ approvalRecordDetail.remark || '无' }} @@ -440,9 +448,9 @@ -
-
- +
+
+ 申请附件
@@ -464,9 +472,9 @@
-
-
- +
+
+ 审批历史
@@ -604,6 +612,7 @@ import { processTrainingApproval } from '@/api/rs/training'; import { listClassTeacherCounselorRel } from '@/api/xg/classTeacherCounselorRel'; +import { listUser } from '@/api/system/user'; import request from '@/utils/request'; // 动画配置 @@ -895,16 +904,16 @@ const getApprovalRecordList = async () => { // status: '1,2', // 暂时注释掉状态过滤,获取所有记录 includeApprovedApplications: true // 确保包含已通过状态的培训申请数据 }; - + console.log('开始获取审批记录列表,查询参数:', queryParams); const response = await listAllApprovalRecords(queryParams); - + if (response && response.rows) { approvalRecordList.value = response.rows; approvalRecordTotal.value = response.total || 0; console.log('获取审批记录列表成功,数据条数:', response.rows.length, '总数:', response.total); console.log('审批记录数据详情:', response.rows); - + // 详细检查前3条记录的字段 if (response.rows.length > 0) { console.log('=== 审批记录字段详细检查 ==='); @@ -921,12 +930,12 @@ const getApprovalRecordList = async () => { console.log('applicationUserName:', record.applicationUserName); console.log('approvalTime:', record.approvalTime); console.log('status:', record.status); - + // 检查字段是否为空字符串或null const targetFields = ['organizerUnit', 'location', 'trainingType', 'companions']; - const emptyFields = targetFields.filter(field => !record[field]); - const hasDataFields = targetFields.filter(field => record[field]); - + const emptyFields = targetFields.filter((field) => !record[field]); + const hasDataFields = targetFields.filter((field) => record[field]); + console.log('空字段:', emptyFields); console.log('有数据字段:', hasDataFields); console.log('完整对象键:', Object.keys(record)); @@ -1195,12 +1204,19 @@ const teacherList = ref([]); /** 异步加载教师列表用于详情显示 */ async function loadTeacherListForDisplay() { try { - const response = await listClassTeacherCounselorRel({}); - const teachers = response.rows || []; - teacherList.value = teachers.map((item: any) => ({ - teacherId: item.teacherId, - teacherName: item.teacherName, - teacherType: item.teacherType + // 使用系统用户API获取完整的教师/用户列表 + // 查询所有正常状态的用户作为候选教师 + const response = await listUser({ + status: '0', // 0表示正常状态 + pageNum: 1, + pageSize: 9999 // 获取所有用户 + }); + + const users = response.rows || []; + teacherList.value = users.map((item: any) => ({ + teacherId: item.userId, // 使用userId作为teacherId + teacherName: item.nickName, // 使用nickName作为教师姓名 + teacherType: 'teacher' // 统一标记为教师类型 })); console.log('教师列表加载完成:', teacherList.value.length, '位教师'); } catch (error) { @@ -1214,9 +1230,9 @@ function getCompanionNames(companions: (string | number)[] | string | null | und if (!companions) { return ''; } - + let companionArray: (string | number)[] = []; - + // 如果是字符串,尝试解析JSON if (typeof companions === 'string') { try { @@ -1226,29 +1242,31 @@ function getCompanionNames(companions: (string | number)[] | string | null | und } } catch (e) { // 如果JSON解析失败,可能是逗号分隔的字符串 - companionArray = companions.split(',').map(id => id.trim()).filter(id => id); + companionArray = companions + .split(',') + .map((id) => id.trim()) + .filter((id) => id); } } else if (Array.isArray(companions)) { companionArray = companions; } - + if (companionArray.length === 0) { return ''; } - + // 如果教师列表还未加载,显示"加载中..." if (teacherList.value.length === 0) { return '加载中...'; } - - return companionArray.map(teacherId => { - // 确保teacherId转换为正确的类型进行比较 - const teacher = teacherList.value.find(t => - String(t.teacherId) === String(teacherId) || - Number(t.teacherId) === Number(teacherId) - ); - return teacher ? teacher.teacherName : `未知教师(ID:${teacherId})`; - }).join('、'); + + return companionArray + .map((teacherId) => { + // 确保teacherId转换为正确的类型进行比较 + const teacher = teacherList.value.find((t) => String(t.teacherId) === String(teacherId) || Number(t.teacherId) === Number(teacherId)); + return teacher ? teacher.teacherName : `未知教师(ID:${teacherId})`; + }) + .join('、'); } /** 查看审批历史 */ @@ -1279,14 +1297,14 @@ async function handleDownloadApplicationAttachment(file: any) { try { console.log('开始下载申请附件:', file); - + // 通过OSS下载API获取文件二进制数据 - const response = await request({ + const response = (await request({ url: `/resource/oss/download/${file.ossId}`, method: 'get', responseType: 'blob', timeout: 300000 // 300秒超时 - }) as Blob; + })) as Blob; // 验证响应数据 if (!response) { @@ -1309,7 +1327,7 @@ async function handleDownloadApplicationAttachment(file: any) { // 释放Blob URL window.URL.revokeObjectURL(url); loading.close(); - + console.log('下载申请附件成功:', file.name); ElMessage.success('文件下载成功'); } catch (error) { @@ -1334,14 +1352,14 @@ async function handleDownloadApprovalRecordAttachment(file: any) { try { console.log('开始下载审批记录附件:', file); - + // 通过OSS下载API获取文件二进制数据 - const response = await request({ + const response = (await request({ url: `/resource/oss/download/${file.ossId}`, method: 'get', responseType: 'blob', timeout: 300000 // 300秒超时 - }) as Blob; + })) as Blob; // 验证响应数据 if (!response) { @@ -1364,7 +1382,7 @@ async function handleDownloadApprovalRecordAttachment(file: any) { // 释放Blob URL window.URL.revokeObjectURL(url); loading.close(); - + console.log('下载审批记录附件成功:', file.name); ElMessage.success('文件下载成功'); } catch (error) {