提交代码

This commit is contained in:
zlwu committed 2025-11-06 15:22:00 +08:00
1 parent 4fbd380a07
commit bbfab6d5b6
7 files changed
+191 -51

No files matched your search

@@ -278,7 +278,9 @@ public class RsTrainingApplicationController extends BaseController {
@GetMapping("/my")
public TableDataInfo<RsTrainingApplication> 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<Long> 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)) {
@@ -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<RsTrainingApproval> 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,请重新登录");
}
/**
@@ -150,7 +150,38 @@ public class RsTrainingRecordController extends BaseController {
@SaCheckPermission("rs:trainingrecord:query")
@GetMapping("/my")
public TableDataInfo<RsTrainingRecord> 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,请重新登录");
}
/**
@@ -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();
@@ -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();
@@ -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'
<where>
a.deleted = '0'
<if test="applicationId != null">
@@ -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'
)
</if>
)
@@ -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'
<where>
a.deleted = '0'
<if test="applicationId != null">
@@ -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'
)
</if>
)
+56 -38
View File
@@ -275,7 +275,9 @@
<el-descriptions-item label="开始时间">{{ parseTime(applicationDetail.startTime) }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ parseTime(applicationDetail.endTime) }}</el-descriptions-item>
<el-descriptions-item label="培训类型">{{ applicationDetail.trainingType || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训费用">{{ applicationDetail.trainingFee ? `${applicationDetail.trainingFee}元` : '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训费用">{{
applicationDetail.trainingFee ? `${applicationDetail.trainingFee}元` : '暂无'
}}</el-descriptions-item>
<el-descriptions-item label="申请状态">
<el-tag
:type="
@@ -293,7 +295,9 @@
</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ parseTime(applicationDetail.applyTime) }}</el-descriptions-item>
<el-descriptions-item label="同行人" :span="2">{{ getCompanionNames(applicationDetail.companions) || '无' }}</el-descriptions-item>
<el-descriptions-item label="培训目的" :span="2">{{ applicationDetail.trainingPurpose || applicationDetail.reason || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训目的" :span="2">{{
applicationDetail.trainingPurpose || applicationDetail.reason || '暂无'
}}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ applicationDetail.remark || '无' }}</el-descriptions-item>
<!-- 显示拒绝原因,如果申请被拒绝 -->
@@ -313,9 +317,9 @@
</el-descriptions>
<!-- 申请附件显示区域 -->
<div v-if="applicationDetailAttachmentList.length > 0" class="attachment-section" style="margin-top: 20px;">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa;">
<el-icon style="margin-right: 8px;"><Paperclip /></el-icon>
<div v-if="applicationDetailAttachmentList.length > 0" class="attachment-section" style="margin-top: 20px">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa">
<el-icon style="margin-right: 8px"><Paperclip /></el-icon>
<strong>申请附件</strong>
</div>
<el-table :data="applicationDetailAttachmentList" border size="small">
@@ -328,9 +332,7 @@
</el-table-column>
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button type="primary" link icon="Download" @click="handleDownloadApplicationAttachment(scope.row)" size="small">
下载
</el-button>
<el-button type="primary" link icon="Download" @click="handleDownloadApplicationAttachment(scope.row)" size="small"> 下载 </el-button>
</template>
</el-table-column>
</el-table>
@@ -400,12 +402,16 @@
<el-descriptions-item label="联系电话">{{ approvalRecordDetail.phone || '暂无' }}</el-descriptions-item>
<!-- 培训情况信息 -->
<el-descriptions-item label="主办单位">{{ approvalRecordDetail.organizerUnit || approvalRecordDetail.courseName || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="主办单位">{{
approvalRecordDetail.organizerUnit || approvalRecordDetail.courseName || '暂无'
}}</el-descriptions-item>
<el-descriptions-item label="培训地点">{{ approvalRecordDetail.location }}</el-descriptions-item>
<el-descriptions-item label="开始时间">{{ parseTime(approvalRecordDetail.startTime) }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ parseTime(approvalRecordDetail.endTime) }}</el-descriptions-item>
<el-descriptions-item label="培训类型">{{ approvalRecordDetail.trainingType || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训费用">{{ approvalRecordDetail.trainingFee ? `${approvalRecordDetail.trainingFee}元` : '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训费用">{{
approvalRecordDetail.trainingFee ? `${approvalRecordDetail.trainingFee}元` : '暂无'
}}</el-descriptions-item>
<el-descriptions-item label="审批状态">
<el-tag
:type="
@@ -423,7 +429,9 @@
</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ parseTime(approvalRecordDetail.applyTime) }}</el-descriptions-item>
<el-descriptions-item label="同行人" :span="2">{{ getCompanionNames(approvalRecordDetail.companions) || '无' }}</el-descriptions-item>
<el-descriptions-item label="培训目的" :span="2">{{ approvalRecordDetail.trainingPurpose || approvalRecordDetail.reason || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训目的" :span="2">{{
approvalRecordDetail.trainingPurpose || approvalRecordDetail.reason || '暂无'
}}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ approvalRecordDetail.remark || '无' }}</el-descriptions-item>
<!-- 显示拒绝原因,如果申请被拒绝 -->
@@ -440,9 +448,9 @@
</el-descriptions>
<!-- 申请附件显示区域 -->
<div v-if="approvalRecordDetailAttachmentList.length > 0" class="attachment-section" style="margin-top: 20px;">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa;">
<el-icon style="margin-right: 8px;"><Paperclip /></el-icon>
<div v-if="approvalRecordDetailAttachmentList.length > 0" class="attachment-section" style="margin-top: 20px">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa">
<el-icon style="margin-right: 8px"><Paperclip /></el-icon>
<strong>申请附件</strong>
</div>
<el-table :data="approvalRecordDetailAttachmentList" border size="small">
@@ -464,9 +472,9 @@
</div>
<!-- 审批历史记录 -->
<div v-if="approvalHistoryList.length > 0" style="margin-top: 20px;">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa;">
<el-icon style="margin-right: 8px;"><Clock /></el-icon>
<div v-if="approvalHistoryList.length > 0" style="margin-top: 20px">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa">
<el-icon style="margin-right: 8px"><Clock /></el-icon>
<strong>审批历史</strong>
</div>
<el-table :data="approvalHistoryList" style="width: 100%" border stripe>
@@ -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';
// 动画配置
@@ -924,8 +933,8 @@ const getApprovalRecordList = async () => {
// 检查字段是否为空字符串或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);
@@ -1195,12 +1204,19 @@ const teacherList = ref<any[]>([]);
/** 异步加载教师列表用于详情显示 */
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) {
@@ -1226,7 +1242,10 @@ 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;
@@ -1241,14 +1260,13 @@ function getCompanionNames(companions: (string | number)[] | string | null | und
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('、');
}
/** 查看审批历史 */
@@ -1281,12 +1299,12 @@ async function handleDownloadApplicationAttachment(file: any) {
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) {
@@ -1336,12 +1354,12 @@ async function handleDownloadApprovalRecordAttachment(file: any) {
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) {