“人事修复更新weiz”

This commit is contained in:
weizhong committed 2025-08-26 00:46:38 +08:00
1 parent 6bffe5fa08
commit 138b3e4d1f
18 files changed
+542 -330

No files matched your search

@@ -118,7 +118,7 @@ public class RsOverseasRecordController extends BaseController {
/**
* 基于申请信息创建出国记录
*/
@SaCheckPermission("rs:overseas:create")
@SaCheckPermission("rs:overseas:add")
@Log(title = "创建出国记录", businessType = BusinessType.INSERT)
@PostMapping("/create/{applicationId}")
public R<Void> createFromApplication(@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) {
@@ -128,7 +128,7 @@ public class RsOverseasRecordController extends BaseController {
/**
* 更新出国记录完成状态
*/
@SaCheckPermission("rs:overseas:complete")
@SaCheckPermission("rs:overseas:edit")
@Log(title = "更新出国记录完成状态", businessType = BusinessType.UPDATE)
@PutMapping("/complete")
public R<Void> updateCompletion(@RequestBody CompletionRequest request) {
@@ -23,6 +23,11 @@ import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.http.HttpServletResponse;
// 新增导入
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService;
import org.dromara.common.core.utils.StringUtils;
/**
* 培训申请控制器
*
@@ -37,6 +42,10 @@ public class RsTrainingApplicationController extends BaseController {
@Autowired
private IRsTrainingApplicationService trainingApplicationService;
// 新增用户服务注入
@Autowired
private ISysUserService sysUserService;
/**
* 查询培训申请列表
@@ -67,6 +76,114 @@ public class RsTrainingApplicationController extends BaseController {
return R.ok(trainingApplicationService.selectRsTrainingApplicationById(applicationId));
}
/**
* 获取当前登录用户的真实信息
*
* @return 用户信息对象,包含用户ID和真实姓名
*/
private UserInfo getCurrentUserInfo() {
try {
// 获取用户ID,处理可能的字符串格式
Object loginId = StpUtil.getLoginId();
Long userId = null;
if (loginId != null) {
if (loginId instanceof Long) {
userId = (Long) loginId;
} else if (loginId instanceof String) {
String loginIdStr = (String) loginId;
// 处理 "sys_user:1" 这种格式,提取数字部分
if (loginIdStr.contains(":")) {
String[] parts = loginIdStr.split(":");
if (parts.length > 1) {
try {
userId = Long.parseLong(parts[1]);
} catch (NumberFormatException e) {
log.warn("无法解析用户ID字符串: {}", loginIdStr);
}
}
} else {
try {
userId = Long.parseLong(loginIdStr);
} catch (NumberFormatException e) {
log.warn("无法解析用户ID字符串: {}", loginIdStr);
}
}
} else if (loginId instanceof Number) {
userId = ((Number) loginId).longValue();
}
}
if (userId != null) {
log.info("获取当前登录用户信息: userId={}", userId);
// 从系统用户表获取用户真实信息
SysUserVo userVo = sysUserService.selectUserById(userId);
if (userVo != null) {
String nickName = userVo.getNickName();
String userName = userVo.getUserName();
log.info("成功获取用户信息: userId={}, nickName={}, userName={}", userId, nickName, userName);
// 使用用户名(根据前端要求)
String displayName = StringUtils.isNotBlank(userName) ? userName : "用户" + userId;
return new UserInfo(userId, displayName);
} else {
log.warn("未找到用户信息: userId={}", userId);
}
} else {
log.warn("获取登录用户ID失败或格式不正确: loginId={}", loginId);
}
} catch (Exception e) {
log.error("获取用户信息异常: loginId={}, error={}", StpUtil.getLoginId(), e.getMessage(), e);
}
// 如果获取失败,尝试从Sa-Token获取基本信息
try {
Object fallbackLoginId = StpUtil.getLoginId();
if (fallbackLoginId != null) {
log.warn("使用备用方案获取用户信息: loginId={}", fallbackLoginId);
// 尝试提取数字ID
if (fallbackLoginId instanceof String) {
String loginIdStr = (String) fallbackLoginId;
if (loginIdStr.contains(":")) {
String[] parts = loginIdStr.split(":");
if (parts.length > 1) {
try {
Long fallbackUserId = Long.parseLong(parts[1]);
return new UserInfo(fallbackUserId, "用户" + fallbackUserId);
} catch (NumberFormatException e) {
log.warn("备用方案也无法解析用户ID: {}", loginIdStr);
}
}
}
}
}
} catch (Exception e) {
log.error("备用方案也失败: {}", e.getMessage(), e);
}
// 最后的备用方案
log.error("所有用户信息获取方案都失败,返回默认值");
return new UserInfo(1L, "系统用户");
}
/**
* 用户信息内部类
*/
private static class UserInfo {
private final Long userId;
private final String userName;
public UserInfo(Long userId, String userName) {
this.userId = userId;
this.userName = StringUtils.isNotBlank(userName) ? userName : "用户" + userId;
}
public Long getUserId() { return userId; }
public String getUserName() { return userName; }
}
/**
* 新增培训申请
*/
@@ -76,30 +193,10 @@ public class RsTrainingApplicationController extends BaseController {
@PostMapping
public R<Void> add(@Validated @RequestBody RsTrainingApplication application) {
try {
// 尝试设置申请人为当前登录用户
String loginId = StpUtil.getLoginIdAsString();
if (loginId != null && loginId.startsWith("sys_user:")) {
// 从"sys_user:1"格式中提取数字部分
String[] parts = loginId.split(":");
if (parts.length > 1) {
application.setUserId(Long.parseLong(parts[1]));
application.setUserName("用户" + parts[1]); // 临时用户名
} else {
// 如果无法解析,使用默认值
application.setUserId(1L);
application.setUserName("默认用户");
}
} else {
// 尝试正常转换
try {
application.setUserId(StpUtil.getLoginIdAsLong());
application.setUserName(loginId);
} catch (Exception e) {
// 转换失败,使用默认值
application.setUserId(1L);
application.setUserName("默认用户");
}
}
// 获取当前登录用户的真实信息
UserInfo userInfo = getCurrentUserInfo();
application.setUserId(userInfo.getUserId());
application.setUserName(userInfo.getUserName());
// 确保courseId的类型正确
if (application.getCourseId() == null) {
@@ -144,7 +241,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 取消培训申请
*/
@SaCheckPermission("rs:trainingapplication:edit")
@SaCheckPermission("rs:trainingapplication:cancel")
@Log(title = "培训申请", businessType = BusinessType.UPDATE)
@PutMapping("/cancel/{applicationId}")
public R<Void> cancel(@PathVariable("applicationId") Long applicationId) {
@@ -154,7 +251,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 查询我的培训申请列表
*/
@SaCheckPermission("rs:trainingapplication:query")
@SaCheckPermission("rs:trainingapplication:list")
@GetMapping("/my")
public TableDataInfo<RsTrainingApplication> listMyApplications(RsTrainingApplication application, PageQuery pageQuery) {
return trainingApplicationService.selectPageMyApplications(StpUtil.getLoginIdAsLong(), application, pageQuery);
@@ -170,39 +267,18 @@ public class RsTrainingApplicationController extends BaseController {
}
/**
* 新增培训申请(无需权限验证,供普通用户使用)
* 新增培训申请(供普通用户使用)
*/
@SaIgnore
@SaCheckPermission("rs:trainingapplication:submit")
@Log(title = "培训申请", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/submit")
public R<Void> submit(@Validated @RequestBody RsTrainingApplication application) {
try {
// 尝试设置申请人为当前登录用户,如果未登录则使用默认值
try {
String loginId = StpUtil.getLoginIdAsString();
if (loginId != null && loginId.startsWith("sys_user:")) {
// 从"sys_user:1"格式中提取数字部分
String[] parts = loginId.split(":");
if (parts.length > 1) {
application.setUserId(Long.parseLong(parts[1]));
application.setUserName("用户" + parts[1]); // 临时用户名
} else {
// 如果无法解析,使用默认值
application.setUserId(1L);
application.setUserName("默认用户");
}
} else {
// 尝试正常转换
application.setUserId(StpUtil.getLoginIdAsLong());
application.setUserName(loginId);
}
} catch (Exception e) {
// 如果未登录或转换失败,使用默认值
application.setUserId(1L);
application.setUserName("默认用户");
log.info("用户未登录,使用默认用户信息");
}
// 获取当前登录用户的真实信息
UserInfo userInfo = getCurrentUserInfo();
application.setUserId(userInfo.getUserId());
application.setUserName(userInfo.getUserName());
// 确保courseId的类型正确
if (application.getCourseId() == null) {
@@ -226,7 +302,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 更新培训申请状态
*/
@SaCheckPermission("rs:trainingapplication:edit")
@SaCheckPermission("rs:trainingapplication:process")
@Log(title = "培训申请状态", businessType = BusinessType.UPDATE)
@PutMapping("/status/{applicationId}/{status}")
public R<Void> updateStatus(
@@ -239,7 +315,7 @@ public class RsTrainingApplicationController extends BaseController {
/**
* 重新提交被拒绝的培训申请
*/
@SaCheckPermission("rs:trainingapplication:edit")
@SaCheckPermission("rs:trainingapplication:submit")
@Log(title = "重新提交培训申请", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/resubmit/{applicationId}")
@@ -45,7 +45,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 查询培训记录列表
*/
@SaCheckPermission("rs:training:record:list")
@SaCheckPermission("rs:trainingrecord:list")
@GetMapping("/list")
public TableDataInfo<RsTrainingRecord> list(RsTrainingRecord record, PageQuery pageQuery) {
return trainingRecordService.selectPageRsTrainingRecordList(record, pageQuery);
@@ -54,7 +54,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 导出培训记录列表
*/
@SaCheckPermission("rs:training:record:export")
@SaCheckPermission("rs:trainingrecord:export")
@Log(title = "培训记录", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, RsTrainingRecord record) {
@@ -65,7 +65,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 获取培训记录详细信息
*/
@SaCheckPermission("rs:training:record:query")
@SaCheckPermission("rs:trainingrecord:query")
@GetMapping("/{recordId}")
public R<RsTrainingRecord> getInfo(@PathVariable("recordId") Long recordId) {
return R.ok(trainingRecordService.selectRsTrainingRecordById(recordId));
@@ -74,7 +74,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 新增培训记录
*/
@SaCheckPermission("rs:training:record:add")
@SaCheckPermission("rs:trainingrecord:add")
@Log(title = "培训记录", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping
@@ -85,7 +85,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 修改培训记录
*/
@SaCheckPermission("rs:training:record:edit")
@SaCheckPermission("rs:trainingrecord:edit")
@Log(title = "培训记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping
@@ -96,7 +96,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 删除培训记录
*/
@SaCheckPermission("rs:training:record:remove")
@SaCheckPermission("rs:trainingrecord:remove")
@Log(title = "培训记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{recordIds}")
public R<Void> remove(@PathVariable Long[] recordIds) {
@@ -106,7 +106,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 修复培训记录中缺失的课程信息
*/
@SaCheckPermission("rs:training:record:edit")
@SaCheckPermission("rs:trainingrecord:edit")
@Log(title = "培训记录", businessType = BusinessType.UPDATE)
@PostMapping("/fixCourseInfo")
public R<Integer> fixCourseInfo() {
@@ -118,7 +118,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 查询我的培训记录列表
*/
@SaCheckPermission("rs:training:record:query")
@SaCheckPermission("rs:trainingrecord:query")
@GetMapping("/my")
public TableDataInfo<RsTrainingRecord> listMyRecords(RsTrainingRecord record, PageQuery pageQuery) {
return trainingRecordService.selectPageMyRecords(StpUtil.getLoginIdAsLong(), record, pageQuery);
@@ -127,7 +127,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 更新培训记录完成状态
*/
@SaCheckPermission("rs:training:record:edit")
@SaCheckPermission("rs:trainingrecord:edit")
@Log(title = "培训记录", businessType = BusinessType.UPDATE)
@PutMapping("/status/{recordId}/{status}")
public R<Void> updateCompletionStatus(@PathVariable("recordId") Long recordId, @PathVariable("status") String status) {
@@ -137,7 +137,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 上传培训证书
*/
@SaCheckPermission("rs:training:record:edit")
@SaCheckPermission("rs:trainingrecord:edit")
@Log(title = "培训记录", businessType = BusinessType.UPDATE)
@PutMapping("/certificate/{recordId}")
public R<Void> uploadCertificate(@PathVariable("recordId") Long recordId, @RequestBody Map<String, String> params) {
@@ -149,7 +149,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 上传培训证书文件
*/
@SaCheckPermission("rs:training:record:edit")
@SaCheckPermission("rs:trainingrecord:edit")
@Log(title = "上传培训证书文件", businessType = BusinessType.UPDATE)
@PostMapping("/certificate/upload/{recordId}")
public R<Map<String, String>> uploadCertificateFile(@PathVariable Long recordId, @RequestParam("file") MultipartFile file) throws IOException {
@@ -196,7 +196,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 获取培训统计数据
*/
@SaCheckPermission("rs:training:record:list")
@SaCheckPermission("rs:trainingrecord:list")
@GetMapping("/statistics")
public R<Map<String, Object>> getStatistics() {
// 这里可以实现培训统计数据的获取逻辑
@@ -207,7 +207,7 @@ public class RsTrainingRecordController extends BaseController {
/**
* 更新培训记录的证书附件ID
*/
@SaCheckPermission("rs:training:record:edit")
@SaCheckPermission("rs:trainingrecord:edit")
@Log(title = "更新培训记录证书附件", businessType = BusinessType.UPDATE)
@PutMapping("/attachments/{recordId}")
public R<Void> updateAttachments(@PathVariable Long recordId, @RequestBody Map<String, String> params) {
@@ -2,6 +2,8 @@ package org.dromara.rs.mapper;
import org.dromara.rs.domain.RsTrainingApplication;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -20,6 +22,10 @@ public interface RsTrainingApplicationMapper extends BaseMapperPlus<RsTrainingAp
* @param application 培训申请
* @return 培训申请集合
*/
@DataPermission({
@DataColumn(key = "deptName", value = "create_dept"),
@DataColumn(key = "userName", value = "create_by")
})
List<RsTrainingApplication> selectRsTrainingApplicationList(RsTrainingApplication application);
/**
@@ -30,6 +36,10 @@ public interface RsTrainingApplicationMapper extends BaseMapperPlus<RsTrainingAp
* @param limit 限制数量
* @return 培训申请集合
*/
@DataPermission({
@DataColumn(key = "deptName", value = "create_dept"),
@DataColumn(key = "userName", value = "create_by")
})
List<RsTrainingApplication> selectRsTrainingApplicationListPage(@Param("app") RsTrainingApplication app, @Param("offset") long offset, @Param("limit") long limit);
/**
@@ -38,5 +48,9 @@ public interface RsTrainingApplicationMapper extends BaseMapperPlus<RsTrainingAp
* @param applicationId 培训申请ID
* @return 培训申请
*/
@DataPermission({
@DataColumn(key = "deptName", value = "create_dept"),
@DataColumn(key = "userName", value = "create_by")
})
RsTrainingApplication selectRsTrainingApplicationById(Long applicationId);
}
@@ -276,7 +276,7 @@ public class RsOverseasApplicationServiceImpl implements IRsOverseasApplicationS
// 手动设置多租户和删除标志字段
if (approval.getTenantId() == null || approval.getTenantId().isEmpty()) {
approval.setTenantId("000000");
approval.setTenantId(LoginHelper.getTenantId());
}
if (approval.getDeleted() == null || approval.getDeleted().isEmpty()) {
approval.setDeleted("0");
@@ -8,12 +8,14 @@ import java.util.List;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.dev33.satoken.stp.StpUtil;
import org.dromara.rs.mapper.RsOverseasApprovalMapper;
import org.dromara.rs.domain.RsOverseasApproval;
import org.dromara.rs.domain.bo.RsOverseasApprovalBo;
@@ -146,18 +148,43 @@ public class RsOverseasApprovalServiceImpl implements IRsOverseasApprovalService
*/
private LambdaQueryWrapper<RsOverseasApproval> buildQueryWrapper(RsOverseasApprovalQuery query) {
LambdaQueryWrapper<RsOverseasApproval> lqw = Wrappers.lambdaQuery();
// ===== 业务逻辑层权限过滤 =====
// 根据用户角色进行数据权限过滤
if (!LoginHelper.isSuperAdmin()) {
// 获取当前登录用户信息
Long currentUserId = LoginHelper.getUserId();
// 检查用户角色并应用相应的数据过滤
if (StpUtil.hasRole("rs-Applicant")) {
// 申请人只能看到与自己相关的审批记录
lqw.exists("SELECT 1 FROM rs_overseas_application app WHERE app.application_id = rs_overseas_approval.application_id AND app.applicant_id = " + currentUserId);
} else if (StpUtil.hasRole("rs-admin")) {
// 人事管理员可以看到所有审批记录
// 不设置额外过滤条件,让管理员能看到所有审批
} else {
// 默认情况:只能看到自己创建或处理的审批
lqw.and(wrapper -> wrapper.eq(RsOverseasApproval::getCreateBy, currentUserId)
.or().eq(RsOverseasApproval::getApproverId, currentUserId));
}
}
// 添加租户过滤
lqw.eq(RsOverseasApproval::getTenantId, LoginHelper.getTenantId());
lqw.eq(query.getApplicationId() != null, RsOverseasApproval::getApplicationId, query.getApplicationId());
lqw.eq(query.getApproverId() != null, RsOverseasApproval::getApproverId, query.getApproverId());
lqw.like(StringUtils.isNotBlank(query.getApproverName()), RsOverseasApproval::getApproverName, query.getApproverName());
lqw.eq(StringUtils.isNotBlank(query.getApprovalLevel()), RsOverseasApproval::getApprovalLevel, query.getApprovalLevel());
// 如果指定了审批状态,使用指定的状态;否则只查询已完成审批的记录(排除待审核状态)
// 如果指定了审批状态,使用指定的状态;对于管理员,显示所有状态
if (StringUtils.isNotBlank(query.getApprovalStatus())) {
lqw.eq(RsOverseasApproval::getApprovalStatus, query.getApprovalStatus());
} else {
// 默认只查询已完成审批的记录:1-通过,2-拒绝(排除0-待审核)
} else if (!LoginHelper.isSuperAdmin() && !StpUtil.hasRole("rs-admin")) {
// 非管理员默认只查询已完成审批的记录:1-通过,2-拒绝(排除0-待审核)
lqw.in(RsOverseasApproval::getApprovalStatus, "1", "2");
}
// 管理员和超级管理员可以看到所有状态的审批记录
lqw.ge(query.getApprovalTimeBegin() != null, RsOverseasApproval::getApprovalTime, query.getApprovalTimeBegin());
lqw.le(query.getApprovalTimeEnd() != null, RsOverseasApproval::getApprovalTime, query.getApprovalTimeEnd());
@@ -220,7 +247,7 @@ public class RsOverseasApprovalServiceImpl implements IRsOverseasApprovalService
try {
// 1. 清理现有测试数据(谨慎操作,只清理测试租户数据)
baseMapper.delete(new LambdaQueryWrapper<RsOverseasApproval>()
.eq(RsOverseasApproval::getTenantId, "000000")
.eq(RsOverseasApproval::getTenantId, LoginHelper.getTenantId())
.eq(RsOverseasApproval::getDeleted, "0"));
// 2. 创建并插入标准测试数据
@@ -265,7 +292,7 @@ public class RsOverseasApprovalServiceImpl implements IRsOverseasApprovalService
approval.setApprovalStatus(approvalStatuses[i]);
approval.setApprovalOpinion(opinions[i]);
approval.setApprovalTime(Date.from(LocalDateTime.now().minusDays(i + 1).atZone(ZoneId.systemDefault()).toInstant()));
approval.setTenantId("000000");
approval.setTenantId(LoginHelper.getTenantId());
approval.setDeleted("0");
approval.setCreateBy(1L);
approval.setCreateTime(Date.from(LocalDateTime.now().minusDays(i + 1).atZone(ZoneId.systemDefault()).toInstant()));
@@ -1,5 +1,6 @@
package org.dromara.rs.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -10,6 +11,7 @@ import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.rs.domain.RsTrainingApplication;
import org.dromara.rs.domain.RsTrainingRecord;
import org.dromara.rs.domain.bo.RsTrainingApplicationBo;
@@ -62,9 +64,30 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
// 计算偏移量
long offset = (current - 1) * size;
// 设置租户ID
// 设置租户ID - 使用当前登录用户的租户ID
if (application.getTenantId() == null || application.getTenantId().isEmpty()) {
application.setTenantId("000000");
application.setTenantId(LoginHelper.getTenantId());
}
// ===== 业务逻辑层权限过滤 =====
// 根据用户角色进行数据权限过滤
if (!LoginHelper.isSuperAdmin()) {
// 获取当前登录用户信息
Long currentUserId = LoginHelper.getUserId();
Long currentDeptId = LoginHelper.getDeptId();
String username = LoginHelper.getUsername();
// 检查用户角色并应用相应的数据过滤
if (StpUtil.hasRole("rs-Applicant")) {
// 申请人只能看到自己的申请
application.setUserId(currentUserId);
} else if (StpUtil.hasRole("rs-admin")) {
// 人事管理员可以看到所有申请(包括审批和部门管理功能)
// 不设置额外过滤条件,让管理员能看到所有申请
} else {
// 默认情况:只能看到自己创建的申请
application.setCreateBy(currentUserId);
}
}
// 查询总数
@@ -95,6 +118,26 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
*/
@Override
public List<RsTrainingApplication> selectRsTrainingApplicationList(RsTrainingApplication application) {
// ===== 业务逻辑层权限过滤 =====
// 根据用户角色进行数据权限过滤
if (!LoginHelper.isSuperAdmin()) {
// 获取当前登录用户信息
Long currentUserId = LoginHelper.getUserId();
Long currentDeptId = LoginHelper.getDeptId();
// 检查用户角色并应用相应的数据过滤
if (StpUtil.hasRole("rs-Applicant")) {
// 申请人只能看到自己的申请
application.setUserId(currentUserId);
} else if (StpUtil.hasRole("rs-admin")) {
// 人事管理员可以看到所有申请(包括审批和部门管理功能)
// 不设置额外过滤条件,让管理员能看到所有申请
} else {
// 默认情况:只能看到自己创建的申请
application.setCreateBy(currentUserId);
}
}
// 使用自定义的Mapper方法查询,已经关联了课程表获取课程名称
List<RsTrainingApplication> list = trainingApplicationMapper.selectRsTrainingApplicationList(application);
@@ -143,9 +186,10 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
*/
private LambdaQueryWrapper<RsTrainingApplication> buildQueryWrapper(RsTrainingApplication application) {
LambdaQueryWrapper<RsTrainingApplication> lqw = new LambdaQueryWrapper<>();
// 排除已审批完成的数据:1-已通过,3-进行中,4-已完成
// 只保留:0-待审核,2-已拒绝
lqw.notIn(RsTrainingApplication::getStatus, "1", "3", "4");
// 移除硬编码状态过滤,让管理员能看到所有状态的申请
// 原注释:排除已审批完成的数据:1-已通过,3-进行中,4-已完成
// 原注释:只保留:0-待审核,2-已拒绝
// 修复:移除 lqw.notIn(RsTrainingApplication::getStatus, "1", "3", "4") 限制
lqw.like(application.getUserName() != null, RsTrainingApplication::getUserName, application.getUserName());
lqw.eq(application.getCourseId() != null, RsTrainingApplication::getCourseId, application.getCourseId());
lqw.eq(application.getUserId() != null, RsTrainingApplication::getUserId, application.getUserId());
@@ -166,6 +210,34 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
// 使用自定义的Mapper方法查询,已经关联了课程表获取课程名称
RsTrainingApplication application = trainingApplicationMapper.selectRsTrainingApplicationById(applicationId);
// ===== 业务逻辑层权限过滤 =====
// 检查当前用户是否有权限查看此申请
if (application != null && !LoginHelper.isSuperAdmin()) {
Long currentUserId = LoginHelper.getUserId();
Long currentDeptId = LoginHelper.getDeptId();
boolean hasPermission = false;
// 检查用户角色权限
if (StpUtil.hasRole("rs-Applicant")) {
// 申请人只能查看自己的申请
hasPermission = application.getUserId().equals(currentUserId);
} else if (StpUtil.hasRole("rs-admin")) {
// 人事管理员可以查看所有申请
hasPermission = true;
} else {
// 默认情况:只能查看自己创建的申请
hasPermission = application.getCreateBy() != null &&
application.getCreateBy().equals(currentUserId);
}
// 如果没有权限,返回null
if (!hasPermission) {
log.warn("用户 {} 尝试访问无权限的培训申请: {}", currentUserId, applicationId);
return null;
}
}
// 处理自定义培训课程(courseId=0)的情况
if (application != null && application.getCourseId() != null && application.getCourseId() == 0
&& (application.getCourseName() == null || application.getCourseName().isEmpty())) {
@@ -189,15 +261,6 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
application.setApplicationId(System.currentTimeMillis());
}
// 设置默认值,防止空指针异常
if (application.getUserId() == null) {
application.setUserId(1L); // 默认用户ID
}
if (application.getUserName() == null || application.getUserName().isEmpty()) {
application.setUserName("默认用户");
}
// 处理自定义培训课程的情况(courseId = 0 或 null)
// 确保courseName不为空
if (application.getCourseName() == null || application.getCourseName().isEmpty()) {
@@ -207,7 +270,7 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
application.setCreateTime(now);
application.setApplyTime(now);
application.setStatus("0"); // 待审核状态
application.setTenantId("000000"); // 设置默认租户ID
application.setTenantId(LoginHelper.getTenantId()); // 设置当前用户租户ID
return trainingApplicationMapper.insert(application);
// 以上代码已处理所有情况
@@ -420,7 +483,7 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
record.setUpdateTime(now);
// 设置租户和用户信息
record.setTenantId(application.getTenantId() != null ? application.getTenantId() : "000000");
record.setTenantId(application.getTenantId() != null ? application.getTenantId() : LoginHelper.getTenantId());
record.setCreateBy(application.getUserId()); // 创建者设为申请人
record.setUpdateBy(application.getUserId()); // 更新者设为申请人
record.setCreateDept(application.getCreateDept() != null ? application.getCreateDept() : 1L); // 使用createDept,如果没有则设为默认值
@@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import cn.dev33.satoken.stp.StpUtil;
import org.dromara.rs.mapper.RsTrainingApprovalMapper;
import org.dromara.rs.mapper.RsTrainingApplicationMapper;
import org.dromara.rs.domain.RsTrainingApproval;
@@ -65,6 +66,31 @@ public class RsTrainingApprovalServiceImpl implements IRsTrainingApprovalService
*/
private LambdaQueryWrapper<RsTrainingApproval> buildQueryWrapper(RsTrainingApproval approval) {
LambdaQueryWrapper<RsTrainingApproval> lqw = new LambdaQueryWrapper<>();
// ===== 业务逻辑层权限过滤 =====
// 根据用户角色进行数据权限过滤
if (!LoginHelper.isSuperAdmin()) {
// 获取当前登录用户信息
Long currentUserId = LoginHelper.getUserId();
// 检查用户角色并应用相应的数据过滤
if (StpUtil.hasRole("rs-Applicant")) {
// 申请人只能看到与自己相关的审批记录
// 这里需要通过申请表关联查询
lqw.exists("SELECT 1 FROM rs_training_application app WHERE app.application_id = rs_training_approval.application_id AND app.user_id = " + currentUserId);
} else if (StpUtil.hasRole("rs-admin")) {
// 人事管理员可以看到所有审批记录
// 不设置额外过滤条件,让管理员能看到所有审批
} else {
// 默认情况:只能看到自己创建或处理的审批
lqw.and(wrapper -> wrapper.eq(RsTrainingApproval::getCreateBy, currentUserId)
.or().eq(RsTrainingApproval::getApproverId, currentUserId));
}
}
// 添加租户过滤
lqw.eq(RsTrainingApproval::getTenantId, LoginHelper.getTenantId());
lqw.eq(approval.getApplicationId() != null, RsTrainingApproval::getApplicationId, approval.getApplicationId());
lqw.eq(approval.getApproverId() != null, RsTrainingApproval::getApproverId, approval.getApproverId());
lqw.eq(approval.getApprovalLevel() != null, RsTrainingApproval::getApprovalLevel, approval.getApprovalLevel());
@@ -158,9 +184,9 @@ public class RsTrainingApprovalServiceImpl implements IRsTrainingApprovalService
// 计算偏移量
long offset = (current - 1) * size;
// 设置租户ID
// 设置租户ID - 使用当前登录用户的租户ID
if (approval.getTenantId() == null || approval.getTenantId().isEmpty()) {
approval.setTenantId("000000");
approval.setTenantId(LoginHelper.getTenantId());
}
// 查询总数
@@ -88,9 +88,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select 1 from rs_overseas_record ors
where ors.application_id = oa.application_id
and ors.deleted = '0'
and ors.tenant_id = '000000'
and ors.tenant_id = #{tenantId}
)
and oa.tenant_id = '000000'
and oa.tenant_id = #{tenantId}
</sql>
<select id="selectOverseasRecordList" parameterType="org.dromara.rs.domain.query.RsOverseasRecordQuery" resultMap="RsOverseasRecordVoResult">
@@ -45,9 +45,10 @@
<select id="selectRsTrainingApplicationList" parameterType="org.dromara.rs.domain.RsTrainingApplication" resultMap="RsTrainingApplicationResult">
<include refid="selectRsTrainingApplicationVo"/>
<where>
<!-- 排除已审批完成的数据:1-已通过,3-进行中,4-已完成 -->
<!-- 只保留:0-待审核,2-已拒绝 -->
AND a.status NOT IN ('1', '3', '4')
<!-- 移除硬编码状态过滤,让管理员能看到所有状态的申请 -->
<!-- 原注释:排除已审批完成的数据:1-已通过,3-进行中,4-已完成 -->
<!-- 原注释:只保留:0-待审核,2-已拒绝 -->
<!-- 修复:移除 AND a.status NOT IN ('1', '3', '4') 限制 -->
<if test="courseId != null ">and a.course_id = #{courseId}</if>
<if test="userId != null ">and a.user_id = #{userId}</if>
<if test="userName != null and userName != ''">and a.user_name like concat('%', #{userName}, '%')</if>
@@ -66,9 +67,10 @@
<select id="selectRsTrainingApplicationListPage" resultMap="RsTrainingApplicationResult">
<include refid="selectRsTrainingApplicationVo"/>
<where>
<!-- 排除已审批完成的数据:1-已通过,3-进行中,4-已完成 -->
<!-- 只保留:0-待审核,2-已拒绝 -->
AND a.status NOT IN ('1', '3', '4')
<!-- 移除硬编码状态过滤,让管理员能看到所有状态的申请 -->
<!-- 原注释:排除已审批完成的数据:1-已通过,3-进行中,4-已完成 -->
<!-- 原注释:只保留:0-待审核,2-已拒绝 -->
<!-- 修复:移除 AND a.status NOT IN ('1', '3', '4') 限制 -->
<if test="app.courseId != null ">and a.course_id = #{app.courseId}</if>
<if test="app.userId != null ">and a.user_id = #{app.userId}</if>
<if test="app.userName != null and app.userName != ''">and a.user_name like concat('%', #{app.userName}, '%')</if>
@@ -233,14 +233,16 @@ CREATE TABLE `rs_training_record` (
## 四、前端页面设计
### 1. 培训课程管理页面
### 1. 培训课程管理页面 ⚠️【已废弃 - 2025-01-03】
- **页面路径**:/rs/training/course
- **功能组件**:
- 搜索表单(课程名称、类别、状态)
- 操作按钮(新增、修改、删除、导出)
- 数据表格(课程信息展示)
- 表单对话框(新增/修改课程信息)
**重要说明**:培训课程模块已废弃,课程信息现在直接存储在 `rs_training_application` 表中,通过培训申请功能管理。
~~- **页面路径**:/rs/training/course~~
~~- **功能组件**:~~
~~- 搜索表单(课程名称、类别、状态)~~
~~- 操作按钮(新增、修改、删除、导出)~~
~~- 数据表格(课程信息展示)~~
~~- 表单对话框(新增/修改课程信息)~~
### 2. 培训申请管理页面
@@ -276,12 +278,12 @@ CREATE TABLE `rs_training_record` (
### 1. 权限项定义
```
# 培训课程管理权限
- rs:training:course:list 查看培训课程列表
- rs:training:course:query 查询培训课程详情
- rs:training:course:add 新增培训课程
- rs:training:course:edit 修改培训课程
- rs:training:course:remove 删除培训课程
- rs:training:course:export 导出培训课程
~~- rs:training:course:list 查看培训课程列表~~ 【已废弃】
~~- rs:training:course:query 查询培训课程详情~~ 【已废弃】
~~- rs:training:course:add 新增培训课程~~ 【已废弃】
~~- rs:training:course:edit 修改培训课程~~ 【已废弃】
~~- rs:training:course:remove 删除培训课程~~ 【已废弃】
~~- rs:training:course:export 导出培训课程~~ 【已废弃】
# 培训申请管理权限
- rs:training:application:list 查看培训申请列表
@@ -295,11 +297,11 @@ CREATE TABLE `rs_training_record` (
- rs:training:approval:process 处理培训审批
# 培训记录管理权限
- rs:training:record:list 查看培训记录列表
- rs:training:record:query 查询培训记录详情
- rs:training:record:add 新增培训记录
- rs:training:record:edit 修改培训记录
- rs:training:record:export 导出培训记录
- rs:trainingrecord:list 查看培训记录列表
- rs:trainingrecord:query 查询培训记录详情
- rs:trainingrecord:add 新增培训记录
- rs:trainingrecord:edit 修改培训记录
- rs:trainingrecord:export 导出培训记录
```
### 2. 角色权限分配
@@ -310,14 +312,14 @@ CREATE TABLE `rs_training_record` (
- rs:training:course:query
- rs:training:application:add
- rs:training:application:query
- rs:training:record:query (个人)
- rs:trainingrecord:query (个人)
# 培训管理员角色 (TRAINING_ADMIN)
- rs:training:course:*
- rs:training:application:list
- rs:training:application:query
- rs:training:application:export
- rs:training:record:*
- rs:trainingrecord:*
# 审批领导角色 (APPROVER)
- rs:training:course:list
+8 -64
View File
@@ -56,6 +56,7 @@ export interface ApplicationQuery {
userName?: string;
department?: string;
status?: string;
excludeStatus?: string; // 排除指定状态的数据
params?: Record<string, any>;
}
@@ -110,6 +111,7 @@ export interface RecordQuery {
location?: string;
department?: string;
completionStatus?: string;
includeApprovedApplications?: boolean; // 是否包含已通过审批的申请数据
params?: Record<string, any>;
}
@@ -158,66 +160,11 @@ export interface RecordForm {
remark?: string;
}
// 查询培训课程列表
export function listTrainingCourse(query: CourseQuery) {
return request({
url: '/rs/training/course/list',
method: 'get',
params: query
});
}
// 获取培训课程详情
export function getTrainingCourse(courseId: string | number) {
return request({
url: `/rs/training/course/${courseId}`,
method: 'get'
});
}
// 新增培训课程
export function addTrainingCourse(data: CourseForm) {
return request({
url: '/rs/training/course',
method: 'post',
data: data
});
}
// 修改培训课程
export function updateTrainingCourse(data: CourseForm) {
return request({
url: '/rs/training/course',
method: 'put',
data: data
});
}
// 删除培训课程
export function delTrainingCourse(courseId: string | number | Array<string | number>) {
return request({
url: `/rs/training/course/${typeof courseId === 'object' ? courseId.join(',') : courseId}`,
method: 'delete'
});
}
// 切换培训课程状态
export function toggleCourseStatus(courseId: string | number) {
return request({
url: `/rs/training/course/toggle/${courseId}`,
method: 'put'
});
}
// 导出培训课程
export function exportTrainingCourse(query: CourseQuery) {
return request({
url: '/rs/training/course/export',
method: 'get',
params: query,
responseType: 'blob'
});
}
// ============================================
// 注意:培训课程模块已废弃 (2025-01-03)
// 课程信息现在直接存储在 rs_training_application 表中
// 以下培训课程相关接口已移除,请使用培训申请相关接口
// ============================================
// 查询培训申请列表
export function listTrainingApplication(query: ApplicationQuery) {
@@ -356,13 +303,10 @@ export function exportTrainingRecord(query: RecordQuery) {
});
}
// 提交培训申请(无需权限验证,供普通用户使用)
// 提交培训申请(需要权限验证)
export function submitTrainingApplication(data: ApplicationForm) {
return request({
url: '/rs/training/application/submit',
headers: {
isToken: false
},
method: 'post',
data: data
});
+1 -1
View File
@@ -230,7 +230,7 @@
<el-button icon="Download" @click="handleExportApplications" v-hasPermi="['rs:application:export']" style="display: none">导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Printer" @click="handlePrintApplications">打印</el-button>
<el-button icon="Printer" @click="handlePrintApplications" style="display: none">打印</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Document" :disabled="applicationIds.length !== 1" @click="handleExportResume">导出简历</el-button>
+6 -6
View File
@@ -83,9 +83,9 @@
</template>
<!-- 待审批申请表格 -->
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleViewApplication">
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
<el-table v-loading="pendingLoading" border :data="pendingList" @row-dblclick="handleViewApplication" v-hasPermi="['rs:overseas:list']">
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
<el-table-column label="出国目的" align="center" prop="purposeText" width="100" :show-overflow-tooltip="true" />
<el-table-column label="目的地" align="center" prop="destination" width="120" :show-overflow-tooltip="true" />
<el-table-column label="出国时间" align="center" min-width="220" :show-overflow-tooltip="true">
@@ -226,9 +226,9 @@
</template>
<!-- 审批记录表格 -->
<el-table v-loading="recordLoading" border :data="recordList" @row-dblclick="handleViewRecord">
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
<el-table v-loading="recordLoading" border :data="recordList" @row-dblclick="handleViewRecord" v-hasPermi="['rs:overseas:list']">
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
<el-table-column label="出国目的" align="center" prop="purposeText" width="100" :show-overflow-tooltip="true" />
<el-table-column label="目的地" align="center" prop="destination" width="120" :show-overflow-tooltip="true" />
<el-table-column label="出国时间" align="center" min-width="220" :show-overflow-tooltip="true">
+37 -1
View File
@@ -328,7 +328,12 @@
<template #footer>
<div class="dialog-footer">
<el-button type="primary" :loading="state.submitLoading" @click="submitApplicationForm">确 定</el-button>
<el-button
type="primary"
:loading="state.submitLoading"
@click="submitApplicationForm"
v-hasPermi="state.applicationForm.applicationId ? ['rs:overseas:edit'] : ['rs:overseas:add', 'rs:overseas:submit']"
>确 定</el-button>
<el-button @click="cancelApplicationForm">取 消</el-button>
</div>
</template>
@@ -470,6 +475,7 @@ import {
import { ElMessage, ElMessageBox } from 'element-plus';
import animate from '@/animate';
import { parseTime } from '@/utils/ruoyi';
import { checkPermi } from '@/utils/permission';
// 响应式数据
const { proxy } = getCurrentInstance() as any;
@@ -759,6 +765,12 @@ const handleViewApplication = async (row: OverseasApplicationVO) => {
// 提交申请
const handleSubmitApplication = (row: OverseasApplicationVO) => {
// 权限检查
if (!checkPermi(['rs:overseas:submit'])) {
ElMessage.error('您没有提交权限');
return;
}
ElMessageBox.confirm(`确认重新提交申请"${row.applicationId}"?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
@@ -965,6 +977,30 @@ const resetApplicationForm = () => {
const submitApplicationForm = () => {
applicationFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
// 权限检查
if (state.applicationForm.applicationId) {
// 编辑模式:检查编辑权限
if (!checkPermi(['rs:overseas:edit'])) {
ElMessage.error('您没有编辑权限');
return;
}
// 重新提交模式:额外检查提交权限
if (state.isResubmitMode && !checkPermi(['rs:overseas:submit'])) {
ElMessage.error('您没有提交权限');
return;
}
} else {
// 新增模式:检查新增和提交权限
if (!checkPermi(['rs:overseas:add'])) {
ElMessage.error('您没有新增权限');
return;
}
if (!checkPermi(['rs:overseas:submit'])) {
ElMessage.error('您没有提交权限');
return;
}
}
state.submitLoading = true;
try {
if (state.applicationForm.applicationId) {
@@ -80,6 +80,7 @@
style="width: 100%"
class="overseas-approval-table"
empty-text="暂无数据"
v-hasPermi="['rs:overseas:list']"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="申请人" align="center" prop="applicantName" width="100" show-overflow-tooltip />
@@ -202,6 +203,7 @@
@row-dblclick="handleViewRecord"
style="width: 100%"
empty-text="暂无数据"
v-hasPermi="['rs:overseas:list']"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="申请人" align="center" prop="applicantName" width="100" show-overflow-tooltip />
+133 -130
View File
@@ -3,7 +3,7 @@
<el-card class="box-card">
<!-- 培训申请子选项卡 -->
<el-tabs v-model="trainingSubTab" @tab-click="handleTrainingSubTabClick">
<el-tab-pane label="培训申请" name="application">
<el-tab-pane label="培训申请" name="application" v-hasPermi="['rs:trainingapplication:list']">
<!-- 搜索区域 -->
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
<div v-show="showApplicationSearch" class="mb-[10px]">
@@ -148,7 +148,7 @@
</el-card>
</el-tab-pane>
<el-tab-pane label="培训记录" name="record">
<el-tab-pane label="培训记录" name="record" v-hasPermi="['rs:trainingrecord:list']">
<!-- 搜索区域 -->
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
<div v-show="showRecordSearch" class="mb-[10px]">
@@ -190,7 +190,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportRecord" v-hasPermi="['rs:training:record:export']" style="display: none">导出</el-button>
<el-button icon="Download" @click="handleExportRecord" v-hasPermi="['rs:trainingrecord:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showRecordSearch" @queryTable="getRecordList"></right-toolbar>
</el-row>
@@ -227,7 +227,7 @@
<el-table-column label="操作" align="center" width="200">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button link type="primary" icon="View" @click.stop="handleViewRecord(scope.row)"></el-button>
<el-button link type="primary" icon="View" @click.stop="handleViewRecord(scope.row)" v-hasPermi="['rs:trainingrecord:query']"></el-button>
</el-tooltip>
<el-tooltip content="编辑" placement="top">
<el-button
@@ -235,7 +235,7 @@
type="primary"
icon="Edit"
@click.stop="handleUpdateRecord(scope.row)"
v-hasPermi="['rs:training:record:edit']"
v-hasPermi="['rs:trainingrecord:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
@@ -244,7 +244,7 @@
type="danger"
icon="Delete"
@click.stop="handleDeleteRecord(scope.row)"
v-hasPermi="['rs:training:record:remove']"
v-hasPermi="['rs:trainingrecord:remove']"
></el-button>
</el-tooltip>
</template>
@@ -448,6 +448,14 @@
<el-form-item label="学时" prop="duration">
<el-input-number v-model="applicationForm.duration" :min="0" :max="1000" :step="0.5" style="width: 200px" />
</el-form-item>
<el-form-item label="申请人" prop="userName">
<el-input
v-model="applicationForm.userName"
placeholder="申请人姓名"
readonly
class="readonly-input"
/>
</el-form-item>
<el-form-item label="所属部门" prop="department">
<el-input
v-model="applicationForm.department"
@@ -744,13 +752,8 @@ import {
} from '@element-plus/icons-vue';
import ImagePreview from '@/components/ImagePreview/index.vue';
import {
listTrainingCourse,
getTrainingCourse,
addTrainingCourse,
updateTrainingCourse,
delTrainingCourse,
toggleCourseStatus,
exportTrainingCourse,
// 培训课程相关接口已废弃 (2025-01-03)
// listTrainingCourse, getTrainingCourse, addTrainingCourse, updateTrainingCourse, delTrainingCourse, toggleCourseStatus, exportTrainingCourse,
listTrainingApplication,
getTrainingApplication,
addTrainingApplication,
@@ -945,6 +948,7 @@ const applicationQueryParams = reactive({
location: '',
duration: undefined as number | undefined,
status: '',
excludeStatus: '1', // 默认排除已通过状态的数据
params: {}
});
@@ -992,6 +996,7 @@ const showRecordSearch = ref(true);
userName: '',
department: '',
completionStatus: '',
includeApprovedApplications: true, // 包含已通过审批的申请数据
params: {}
});
@@ -1073,6 +1078,7 @@ const applicationFormRef = ref<any>(null);
const applicationForm = reactive<{
applicationId?: number | string;
courseName: string;
userName: string; // 申请人姓名
department: string;
location: string;
duration: number;
@@ -1083,6 +1089,7 @@ const applicationForm = reactive<{
}>({
applicationId: undefined,
courseName: '',
userName: '', // 申请人姓名
department: '',
location: '',
duration: 0,
@@ -1106,14 +1113,14 @@ const applicationRules = reactive({
// 部门选项 - 从数据库sys_dept表获取
const departmentOptions = ref<Array<{ value: string; label: string }>>([]);
/** 查询培训课程列表 */
/** 查询培训课程列表 - 已废弃 (2025-01-03) */
function getTrainingList() {
trainingLoading.value = true;
listTrainingCourse(trainingQueryParams).then((response) => {
courseList.value = response.rows;
trainingTotal.value = response.total;
trainingLoading.value = false;
});
// 培训课程模块已废弃,课程信息现在通过培训申请管理
console.warn('培训课程模块已废弃,请使用培训申请功能');
courseList.value = [];
trainingTotal.value = 0;
trainingLoading.value = false;
}
/** 获取部门列表 */
@@ -1199,11 +1206,9 @@ function handleUpdateCourse(row?: CourseVO) {
return;
}
getTrainingCourse(courseId).then((response) => {
Object.assign(courseForm, response.data);
courseDialogVisible.value = true;
courseDialogTitle.value = '修改培训课程';
});
// 培训课程模块已废弃 (2025-01-03)
console.warn('培训课程编辑功能已废弃,请使用培训申请功能');
proxy?.$modal.msgWarning('培训课程模块已废弃,请使用培训申请功能');
}
/** 提交按钮 */
@@ -1211,27 +1216,10 @@ function submitCourseForm() {
courseFormRef.value?.validate((valid: boolean) => {
if (valid) {
courseDialogLoading.value = true;
if (courseForm.courseId) {
updateTrainingCourse(courseForm)
.then(() => {
proxy?.$modal.msgSuccess('修改成功');
courseDialogVisible.value = false;
getTrainingList();
})
.finally(() => {
courseDialogLoading.value = false;
});
} else {
addTrainingCourse(courseForm)
.then(() => {
proxy?.$modal.msgSuccess('新增成功');
courseDialogVisible.value = false;
getTrainingList();
})
.finally(() => {
courseDialogLoading.value = false;
});
}
// 培训课程模块已废弃 (2025-01-03)
console.warn('培训课程新增/编辑功能已废弃,请使用培训申请功能');
proxy?.$modal.msgWarning('培训课程模块已废弃,请使用培训申请功能');
courseDialogLoading.value = false;
}
});
}
@@ -1259,11 +1247,9 @@ function handleDeleteCourse(row?: CourseVO) {
proxy?.$modal
.confirm(confirmMessage)
.then(() => {
return delTrainingCourse(courseIds);
})
.then(() => {
getTrainingList();
proxy?.$modal.msgSuccess('删除成功');
// 培训课程模块已废弃 (2025-01-03)
console.warn('培训课程删除功能已废弃,请使用培训申请功能');
proxy?.$modal.msgWarning('培训课程模块已废弃,请使用培训申请功能');
});
}
@@ -1278,26 +1264,16 @@ function handleExportCourse() {
);
}
/** 查看培训课程详情 */
/** 查看培训课程详情 - 已废弃 (2025-01-03) */
function handleViewCourse(row: CourseVO) {
getTrainingCourse(row.courseId).then((response) => {
Object.assign(courseDetail, response.data);
courseDetailDialogVisible.value = true;
});
console.warn('培训课程查看功能已废弃,请使用培训申请功能');
proxy?.$modal.msgWarning('培训课程模块已废弃,请使用培训申请功能');
}
/** 切换课程状态 */
/** 切换课程状态 - 已废弃 (2025-01-03) */
function handleToggleStatus(row: CourseVO) {
const text = row.status === '0' ? '结束' : '启用';
proxy?.$modal
.confirm(`确认要${text}该课程吗?`)
.then(() => {
return toggleCourseStatus(row.courseId);
})
.then(() => {
getTrainingList();
proxy?.$modal.msgSuccess(`${text}成功`);
});
console.warn('培训课程状态切换功能已废弃,请使用培训申请功能');
proxy?.$modal.msgWarning('培训课程模块已废弃,请使用培训申请功能');
}
/** 查看培训申请记录 */
@@ -1313,13 +1289,25 @@ function handleViewApplications(row: CourseVO) {
/** 培训申请管理相关 */
function getApplicationList() {
applicationLoading.value = true;
// 构建查询参数,自动过滤掉已通过的数据
const queryParams = {
...applicationQueryParams,
// 排除已通过状态的数据,只显示待审核、已拒绝、已取消等状态
excludeStatus: '1' // 1表示已通过状态
};
// 调用API获取申请列表
listTrainingApplication(applicationQueryParams)
listTrainingApplication(queryParams)
.then((response) => {
// 获取申请列表数据
const applications = response.rows || [];
applicationList.value = applications;
applicationTotal.value = response.total || 0;
// 前端二次过滤,确保不显示已通过的数据
const filteredApplications = applications.filter(app => app.status !== '1');
applicationList.value = filteredApplications;
applicationTotal.value = filteredApplications.length;
applicationLoading.value = false;
})
.catch(() => {
@@ -1339,68 +1327,82 @@ async function handleAddApplication() {
loadCourseOptions();
}
// 自动获取登录用户的部门信息
try {
// 确保用户信息已加载
if (!userStore.userId) {
await userStore.getInfo();
}
console.log('开始自动填充用户部门信息:', {
userId: userStore.userId,
cachedDeptName: userStore.deptName
});
// 重新获取用户信息以确保获取到deptId
await userStore.getInfo();
const userInfo = await getUserInfo();
const userDeptId = userInfo.data.user.deptId;
console.log('用户信息详情:', {
userId: userStore.userId,
deptId: userDeptId,
cachedDeptName: userStore.deptName
});
// 如果有deptId,从数据库sys_dept表实时获取部门名称
if (userDeptId) {
console.log('开始从数据库sys_dept表获取部门信息 - deptId:', userDeptId);
// 自动获取登录用户的部门信息和申请人信息
try {
// 确保用户信息已加载
if (!userStore.userId) {
await userStore.getInfo();
}
try {
const deptResponse = await getDept(userDeptId);
if (deptResponse.code === 200 && deptResponse.data) {
const realDeptName = deptResponse.data.deptName;
applicationForm.department = realDeptName;
console.log('培训申请部门自动匹配成功:', {
userId: userStore.userId,
userDeptId: userDeptId,
deptNameFromDatabase: realDeptName,
关联逻辑: 'sys_user.dept_id -> sys_dept.dept_name'
});
} else {
console.warn('从数据库获取部门信息失败,使用缓存的部门名称');
console.log('开始自动填充用户信息:', {
userId: userStore.userId,
userName: userStore.name,
nickname: userStore.nickname,
cachedDeptName: userStore.deptName
});
// 自动填充申请人姓名(使用用户名称)
const displayName = userStore.name || `用户${userStore.userId}`;
applicationForm.userName = displayName;
console.log('申请人姓名自动填充:', {
userId: userStore.userId,
displayName: displayName,
userName: userStore.name
});
// 重新获取用户信息以确保获取到deptId
await userStore.getInfo();
const userInfo = await getUserInfo();
const userDeptId = userInfo.data.user.deptId;
console.log('用户信息详情:', {
userId: userStore.userId,
deptId: userDeptId,
cachedDeptName: userStore.deptName
});
// 如果有deptId,从数据库sys_dept表实时获取部门名称
if (userDeptId) {
console.log('开始从数据库sys_dept表获取部门信息 - deptId:', userDeptId);
try {
const deptResponse = await getDept(userDeptId);
if (deptResponse.code === 200 && deptResponse.data) {
const realDeptName = deptResponse.data.deptName;
applicationForm.department = realDeptName;
console.log('培训申请部门自动匹配成功:', {
userId: userStore.userId,
userDeptId: userDeptId,
deptNameFromDatabase: realDeptName,
关联逻辑: 'sys_user.dept_id -> sys_dept.dept_name'
});
} else {
console.warn('从数据库获取部门信息失败,使用缓存的部门名称');
applicationForm.department = userStore.deptName || '';
}
} catch (deptError) {
console.error('查询部门信息API调用失败:', deptError);
applicationForm.department = userStore.deptName || '';
}
} catch (deptError) {
console.error('查询部门信息API调用失败:', deptError);
} else {
console.warn('用户deptId不存在,使用缓存的部门名称');
applicationForm.department = userStore.deptName || '';
}
} else {
console.warn('用户deptId不存在,使用缓存的部门名称');
applicationForm.department = userStore.deptName || '';
} catch (error) {
console.error('获取用户信息失败:', error);
// 如果获取失败,使用空字符串,用户可以手动输入
applicationForm.department = '';
applicationForm.userName = `用户${userStore.userId || '未知'}`;
}
} catch (error) {
console.error('获取用户信息失败:', error);
// 如果获取失败,使用空字符串,用户可以手动输入
applicationForm.department = '';
}
}
/** 重置培训申请表单 */
function resetApplicationForm() {
applicationForm.applicationId = undefined;
applicationForm.courseName = '';
applicationForm.userName = ''; // 重置申请人字段
applicationForm.department = '';
applicationForm.location = '';
applicationForm.duration = 0;
@@ -1778,16 +1780,12 @@ function handleTrainingSubTabClick() {
}
}
// 加载课程选项
// 加载课程选项 - 已废弃 (2025-01-03)
function loadCourseOptions() {
return listTrainingCourse({ pageNum: 1, pageSize: 100, status: '0' }).then((response) => {
courseOptions.value = response.rows || [];
return courseOptions.value;
}).catch((error) => {
console.error('加载课程选项失败:', error);
courseOptions.value = [];
throw error;
});
// 培训课程模块已废弃,现在课程信息通过申请时的自定义课程名称管理
console.warn('培训课程选项已废弃,现在使用自定义课程名称');
courseOptions.value = [];
return Promise.resolve([]);
}
// 远程搜索用户
@@ -1818,10 +1816,13 @@ function remoteSearchUser(query: string) {
/** 获取培训记录列表 */
function getRecordList() {
recordLoading.value = true;
// 设置当前用户ID,只查询当前用户的培训记录
// 构建查询参数,包含已通过审批的申请数据
const queryParams = {
...recordQueryParams,
userId: userStore.userId // 设置当前用户ID
userId: userStore.userId, // 设置当前用户ID
// 确保包含已通过状态的培训申请数据
includeApprovedApplications: true
};
// 调用培训记录接口,获取培训记录数据
@@ -2973,6 +2974,8 @@ function resetApplicationQuery() {
applicationQueryParams.location = '';
applicationQueryParams.duration = undefined;
applicationQueryParams.status = '';
// 保持排除已通过状态的设置
applicationQueryParams.excludeStatus = '1';
handleApplicationQuery();
}
+23 -6
View File
@@ -175,7 +175,7 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button icon="Download" @click="handleExportApprovalRecord" v-hasPermi="['rs:trainingapproval:export']">导出</el-button>
<el-button icon="Download" @click="handleExportApprovalRecord" v-hasPermi="['rs:trainingapproval:export']" style="display: none">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showApprovalRecordSearch" @queryTable="getApprovalRecordList"></right-toolbar>
</el-row>
@@ -551,6 +551,7 @@ const applicationQueryParams = reactive({
courseName: '',
location: '',
status: '',
excludeStatus: '1', // 默认排除已通过状态的数据
params: {}
});
@@ -600,6 +601,7 @@ const approvalRecordQueryParams = reactive({
courseName: '',
userName: '',
status: '',
includeApprovedApplications: true, // 包含已通过审批的申请数据
params: {}
});
@@ -665,9 +667,20 @@ const getStatusLabel = (status: string) => {
const getApplicationList = async () => {
applicationLoading.value = true;
try {
const response = await listTrainingApplication(applicationQueryParams);
applicationList.value = response.rows;
applicationTotal.value = response.total;
// 构建查询参数,自动过滤掉已通过的数据
const queryParams = {
...applicationQueryParams,
// 排除已通过状态的数据,只显示待审核、已拒绝、已取消等状态
excludeStatus: '1' // 1表示已通过状态
};
const response = await listTrainingApplication(queryParams);
// 前端二次过滤,确保不显示已通过的数据
const filteredApplications = response.rows.filter(app => app.status !== '1');
applicationList.value = filteredApplications;
applicationTotal.value = filteredApplications.length;
} catch (error) {
console.error('获取申请列表失败:', error);
} finally {
@@ -689,6 +702,7 @@ const resetApplicationQuery = () => {
courseName: '',
location: '',
status: '',
excludeStatus: '1', // 保持排除已通过状态的设置
params: {}
});
getApplicationList();
@@ -780,7 +794,8 @@ const getApprovalRecordList = async () => {
// 获取所有审批记录(不限于当前用户审批过的)
const queryParams = {
...approvalRecordQueryParams,
status: approvalRecordQueryParams.status || '1,2' // 默认获取已通过和已拒绝的记录
status: approvalRecordQueryParams.status || '1,2', // 默认获取已通过和已拒绝的记录
includeApprovedApplications: true // 确保包含已通过状态的培训申请数据
};
const response = await listAllApprovalRecords(queryParams);
approvalRecordList.value = response.rows;
@@ -809,6 +824,7 @@ const resetApprovalRecordQuery = () => {
courseName: '',
userName: '',
status: '',
includeApprovedApplications: true, // 保持包含已通过审批的申请数据
params: {}
});
getApprovalRecordList();
@@ -858,7 +874,8 @@ const handleExportApprovalRecord = async () => {
try {
const queryParams = {
...approvalRecordQueryParams,
status: approvalRecordQueryParams.status || '1,2' // 默认导出已通过和已拒绝的记录
status: approvalRecordQueryParams.status || '1,2', // 默认导出已通过和已拒绝的记录
includeApprovedApplications: true // 确保包含已通过状态的培训申请数据
};
const response = await exportApprovalRecord(queryParams);