Преглед на файлове

“培训审批修改v1.2”

weizhong преди 11 месеца
родител
ревизия
53e5afaafe

+ 77 - 1
backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsTrainingApplicationController.java

@@ -19,7 +19,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
-import cn.dev33.satoken.annotation.SaIgnore;
 import cn.dev33.satoken.stp.StpUtil;
 import jakarta.servlet.http.HttpServletResponse;
 
@@ -28,6 +27,10 @@ import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.system.service.ISysUserService;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.rs.domain.vo.SysUserBasicVo;
+import cn.hutool.core.collection.CollUtil;
+
+import java.util.ArrayList;
 
 /**
  * 培训申请控制器
@@ -439,4 +442,77 @@ public class RsTrainingApplicationController extends BaseController {
             return R.fail("查询失败: " + e.getMessage());
         }
     }
+
+    /**
+     * 批量查询同行人基本信息
+     * 用于培训申请/记录详情展示,不限制account_type
+     * 
+     * 权限说明:
+     * - 只需要培训申请的查询权限即可
+     * - 不需要system:teacher:list权限
+     * - 只返回用户的基本信息(姓名、部门等),不暴露敏感数据
+     * 
+     * @param userIds 用户ID列表(逗号分隔)
+     * @return 用户基本信息列表
+     */
+    @SaCheckPermission("rs:trainingapplication:query")
+    @GetMapping("/companions/batch")
+    public R<List<SysUserBasicVo>> batchQueryCompanions(@RequestParam String userIds) {
+        log.info("批量查询同行人信息,用户ID列表: {}", userIds);
+
+        // 参数验证
+        if (StringUtils.isBlank(userIds)) {
+            log.warn("同行人ID列表为空");
+            return R.ok(new ArrayList<>());
+        }
+
+        // 解析用户ID列表
+        List<Long> userIdList = new ArrayList<>();
+        try {
+            String[] idArray = userIds.split(",");
+            for (String idStr : idArray) {
+                if (StringUtils.isNotBlank(idStr)) {
+                    userIdList.add(Long.parseLong(idStr.trim()));
+                }
+            }
+        } catch (NumberFormatException e) {
+            log.error("解析用户ID失败: {}", e.getMessage());
+            return R.fail("用户ID格式错误");
+        }
+
+        if (CollUtil.isEmpty(userIdList)) {
+            log.warn("解析后的用户ID列表为空");
+            return R.ok(new ArrayList<>());
+        }
+
+        // 批量查询用户信息
+        List<SysUserBasicVo> userList = new ArrayList<>();
+        for (Long userId : userIdList) {
+            try {
+                // 调用用户服务查询
+                SysUserVo sysUser = sysUserService.selectUserById(userId);
+                if (sysUser != null) {
+                    SysUserBasicVo vo = new SysUserBasicVo();
+                    vo.setUserId(sysUser.getUserId());
+                    vo.setUserName(sysUser.getUserName());
+                    vo.setNickName(sysUser.getNickName());
+                    vo.setAccountType(sysUser.getAccountType());
+                    vo.setStatus(sysUser.getStatus());
+                    vo.setDeptId(sysUser.getDeptId());
+                    // 设置部门名称(SysUserVo已经包含deptName字段)
+                    vo.setDeptName(sysUser.getDeptName());
+                    userList.add(vo);
+                    log.debug("查询到用户: userId={}, nickName={}", userId, sysUser.getNickName());
+                } else {
+                    log.warn("用户不存在: userId={}", userId);
+                }
+            } catch (Exception e) {
+                log.error("查询用户{}失败: {}", userId, e.getMessage());
+                // 继续查询下一个用户,不中断整个流程
+            }
+        }
+
+        log.info("批量查询同行人完成,请求{}个,查询到{}个", userIdList.size(), userList.size());
+        return R.ok(userList);
+    }
 }

+ 26 - 0
web/src/api/rs/training.ts

@@ -458,3 +458,29 @@ export function getPendingTrainingApplications() {
     method: 'get'
   });
 }
+
+/**
+ * 批量查询同行人基本信息
+ * 用于培训申请/记录详情展示
+ * @param userIds 用户ID数组
+ * @returns 用户基本信息列表
+ */
+export interface UserBasicVO {
+  userId: number;
+  userName: string;
+  nickName: string;
+  accountType: string;
+  status: string;
+  deptId?: number;
+  deptName?: string;
+}
+
+export function batchQueryCompanions(userIds: number[]) {
+  return request({
+    url: '/rs/training/application/companions/batch',
+    method: 'get',
+    params: {
+      userIds: userIds.join(',')
+    }
+  });
+}

+ 76 - 44
web/src/views/rs/training/index.vue

@@ -999,6 +999,7 @@ import {
   deleteTrainingApplication,
   getTrainingApprovalHistory,
   resubmitTrainingApplication,
+  batchQueryCompanions,
   updateRecordAttachments,
   listAllApprovalRecords
 } from '@/api/rs/training';
@@ -1432,6 +1433,9 @@ const applicationForm = reactive<{
 // 同行人选择器相关
 const selectedCompanions = ref<TeacherVO[]>([]);
 
+// 同行人姓名缓存(用于详情展示)
+const companionNamesCache = ref<Record<string, string>>({});
+
 // 培训申请表单校验规则
 const applicationRules = reactive<any>({
   // 情况基本信息验证
@@ -1716,7 +1720,7 @@ function getTeacherList() {
     });
 }
 
-/** 异步加载教师列表用于详情显示 */
+/** 异步加载教师列表用于详情显示(容错版,权限不足时不报错)*/
 async function loadTeacherListForDisplay() {
   try {
     console.log('开始加载教师列表...');
@@ -1733,8 +1737,9 @@ async function loadTeacherListForDisplay() {
     }));
     console.log('教师列表加载完成:', teacherList.value.length, '位教师');
     console.log('教师ID列表:', teacherList.value.map(t => t.teacherId));
-  } catch (error) {
-    console.error('获取教师列表失败:', error);
+  } catch (error: any) {
+    console.warn('获取教师列表失败(可能是权限不足),将使用备用查询方式:', error);
+    // ⚠️ 不再抛出错误,允许使用备用的同行人查询接口
     teacherList.value = [];
   }
 }
@@ -2131,66 +2136,91 @@ function formatFileSize(bytes: number | string | undefined): string {
   return parseFloat((size / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
 }
 
-/** 获取同行人姓名列表 */
-function getCompanionNames(companions: (string | number)[] | string | null | undefined): string {
+/** 异步加载同行人姓名并缓存(使用新的批量查询接口)*/
+async function loadCompanionNames(companions: (string | number)[] | string | null | undefined): Promise<string> {
   if (!companions) {
-    console.log('[同行人解析] 同行人数据为空');
     return '';
   }
   
-  let companionArray: (string | number)[] = [];
+  // 生成缓存key
+  const cacheKey = typeof companions === 'string' ? companions : JSON.stringify(companions);
+  
+  // 如果已缓存,直接返回
+  if (companionNamesCache.value[cacheKey]) {
+    return companionNamesCache.value[cacheKey];
+  }
+  
+  let companionArray: number[] = [];
   
-  // 如果是字符串,尝试解析JSON
+  // 解析同行人ID列表
   if (typeof companions === 'string') {
     try {
-      companionArray = JSON.parse(companions);
-      if (!Array.isArray(companionArray)) {
-        console.log('[同行人解析] JSON解析结果不是数组:', companionArray);
-        return '';
+      const parsed = JSON.parse(companions);
+      if (Array.isArray(parsed)) {
+        companionArray = parsed.map(id => Number(id));
       }
       console.log('[同行人解析] JSON解析成功:', companionArray);
     } catch (e) {
       // 如果JSON解析失败,可能是逗号分隔的字符串
-      companionArray = companions.split(',').map(id => id.trim()).filter(id => id);
+      companionArray = companions.split(',')
+        .map(id => Number(id.trim()))
+        .filter(id => !isNaN(id));
       console.log('[同行人解析] 使用逗号分隔解析:', companionArray);
     }
   } else if (Array.isArray(companions)) {
-    companionArray = companions;
+    companionArray = companions.map(id => Number(id));
     console.log('[同行人解析] 直接使用数组:', companionArray);
   }
   
   if (companionArray.length === 0) {
-    console.log('[同行人解析] 同行人数组为空');
     return '';
   }
   
-  // 如果教师列表还未加载,显示"加载中..."
-  if (teacherList.value.length === 0) {
-    console.log('[同行人解析] 教师列表未加载,返回加载中');
-    return '加载中...';
+  try {
+    // 调用新的批量查询接口(不需要system:teacher:list权限)
+    console.log('[同行人查询] 调用批量查询接口:', companionArray);
+    const response: any = await batchQueryCompanions(companionArray);
+    const users = response.data || [];
+    
+    console.log('[同行人查询] 查询结果:', users.length, '个用户');
+    
+    // 拼接姓名
+    const names = companionArray.map(userId => {
+      const user = users.find((u: any) => u.userId === userId);
+      const name = user ? (user.nickName || user.userName) : `未知用户(ID:${userId})`;
+      console.log(`[同行人查询] ID ${userId} -> ${name}`);
+      return name;
+    }).join('、');
+    
+    // 缓存结果
+    companionNamesCache.value[cacheKey] = names;
+    console.log('[同行人查询] 最终结果:', names);
+    return names;
+  } catch (error) {
+    console.error('[同行人查询] 接口调用失败:', error);
+    // 降级方案:显示ID列表
+    const fallback = `同行人ID: ${companionArray.join('、')}`;
+    companionNamesCache.value[cacheKey] = fallback;
+    return fallback;
+  }
+}
+
+/** 获取同行人姓名列表(同步版,从缓存读取)*/
+function getCompanionNames(companions: (string | number)[] | string | null | undefined): string {
+  if (!companions) {
+    return '';
   }
   
-  console.log('[同行人解析] 教师列表已加载:', teacherList.value.length, '位教师');
-  console.log('[同行人解析] 需要查找的ID:', companionArray);
+  const cacheKey = typeof companions === 'string' ? companions : JSON.stringify(companions);
   
-  const names = companionArray.map(teacherId => {
-    // 确保teacherId转换为正确的类型进行比较
-    const teacher = teacherList.value.find(t => 
-      String(t.teacherId) === String(teacherId) || 
-      Number(t.teacherId) === Number(teacherId)
-    );
-    const name = teacher ? teacher.teacherName : `未知教师(ID:${teacherId})`;
-    console.log(`[同行人解析] ID ${teacherId} -> ${name}`);
-    return name;
-  }).join('、');
+  // 从缓存返回
+  if (companionNamesCache.value[cacheKey]) {
+    return companionNamesCache.value[cacheKey];
+  }
   
-  console.log('[同行人解析] 最终结果:', names);
-  return names;
-}
-
-/** 格式化同行人员显示(表格列使用) */
-function formatCompanions(companions: string | undefined): string {
-  return getCompanionNames(companions);
+  // 如果缓存中没有,返回"加载中..."并异步加载
+  loadCompanionNames(companions);
+  return '加载中...';
 }
 
 /** 查看申请详情 */
@@ -2210,20 +2240,22 @@ async function handleViewApplicant(row: any) {
     }
   }
   
-  // 确保教师列表已加载(用于显示同行人姓名)
-  if (teacherList.value.length === 0) {
-    await loadTeacherListForDisplay();
-  }
-  
   // 复制申请详情
   Object.assign(applicationDetail, row);
   
   console.log('[查看申请详情] 详情数据已设置:', {
     userName: applicationDetail.userName,
     nickName: applicationDetail.nickName,
-    attachmentIds: applicationDetail.attachmentIds // 添加附件ID日志
+    companions: applicationDetail.companions,
+    attachmentIds: applicationDetail.attachmentIds
   });
   
+  // 预加载同行人信息(使用新接口,不需要system:teacher:list权限)
+  if (row.companions) {
+    await loadCompanionNames(row.companions);
+    console.log('[查看申请详情] 同行人信息已预加载');
+  }
+  
   // 加载申请详情的附件数据
   await handleApplicationDetailAttachmentData(row.attachmentIds);