“人事管理更新0823”

This commit is contained in:
weizhong committed 2025-08-23 01:02:18 +08:00
1 parent c164d1feb4
commit 9946b329fa
14 files changed
+1922 -285

No files matched your search

@@ -98,6 +98,10 @@ public class RsJobController extends BaseController {
@Log(title = "招聘职位", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@RequestBody RsJob rsJob) {
// 如果职位是启用状态,自动设置发布时间
if ("0".equals(rsJob.getIsActive())) {
rsJob.setPublishTime(new java.util.Date());
}
return toAjax(jobService.insertRsJob(rsJob));
}
@@ -108,6 +112,16 @@ public class RsJobController extends BaseController {
@Log(title = "招聘职位", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@RequestBody RsJob rsJob) {
// 检查是否是状态切换为启用
if ("0".equals(rsJob.getIsActive())) {
// 获取原职位信息
RsJobVo originalJob = jobService.selectRsJobById(rsJob.getJobId());
// 如果原来是停用状态,现在启用,或者原来没有发布时间,则设置发布时间
if (originalJob != null &&
("1".equals(originalJob.getIsActive()) || originalJob.getPublishTime() == null)) {
rsJob.setPublishTime(new java.util.Date());
}
}
return toAjax(jobService.updateRsJob(rsJob));
}
@@ -125,7 +139,14 @@ public class RsJobController extends BaseController {
RsJob updateJob = new RsJob();
updateJob.setJobId(jobId);
// 切换状态
updateJob.setIsActive("0".equals(job.getIsActive()) ? "1" : "0");
String newStatus = "0".equals(job.getIsActive()) ? "1" : "0";
updateJob.setIsActive(newStatus);
// 如果从停用切换为启用,且没有发布时间,则设置发布时间
if ("0".equals(newStatus) && job.getPublishTime() == null) {
updateJob.setPublishTime(new java.util.Date());
}
return toAjax(jobService.updateRsJob(updateJob));
}
@@ -16,10 +16,18 @@ import org.dromara.rs.service.IRsTrainingRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.http.HttpServletResponse;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.rs.service.impl.RsTrainingRecordServiceImpl;
/**
* 培训记录控制器
@@ -94,6 +102,18 @@ public class RsTrainingRecordController extends BaseController {
public R<Void> remove(@PathVariable Long[] recordIds) {
return toAjax(trainingRecordService.deleteRsTrainingRecordByIds(recordIds));
}
/**
* 修复培训记录中缺失的课程信息
*/
@SaCheckPermission("rs:training:record:edit")
@Log(title = "培训记录", businessType = BusinessType.UPDATE)
@PostMapping("/fixCourseInfo")
public R<Integer> fixCourseInfo() {
RsTrainingRecordServiceImpl serviceImpl = (RsTrainingRecordServiceImpl) trainingRecordService;
int fixedCount = serviceImpl.fixMissingCourseInfo();
return R.ok("修复完成,共修复 " + fixedCount + " 条记录", fixedCount);
}
/**
* 查询我的培训记录列表
@@ -126,6 +146,53 @@ public class RsTrainingRecordController extends BaseController {
return toAjax(trainingRecordService.uploadCertificate(recordId, certificatePath, certificateNumber));
}
/**
* 上传培训证书文件
*/
@SaCheckPermission("rs:training:record: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 {
if (file.isEmpty()) {
return R.fail("上传失败,请选择文件");
}
// 检查文件类型
String originalFilename = file.getOriginalFilename();
if (originalFilename == null) {
return R.fail("文件名不能为空");
}
String fileExtension = StringUtils.substringAfterLast(originalFilename, ".").toLowerCase();
if (!("pdf".equals(fileExtension) || "jpg".equals(fileExtension) || "jpeg".equals(fileExtension) || "png".equals(fileExtension))) {
return R.fail("只支持PDF、JPG、PNG格式的文件");
}
// 检查文件大小(10MB)
if (file.getSize() > 10 * 1024 * 1024) {
return R.fail("文件大小不能超过10MB");
}
// 调用文件上传服务
String fileName = StringUtils.substringAfterLast(file.getOriginalFilename(), "/");
// 此处应该使用实际的文件上传工具类,可使用SysFileConfig或自定义文件上传服务
// 这里使用简化实现,实际项目中应替换为真正的文件上传逻辑
String path = "uploads/training/certificate/" + DateUtils.datePath() + "/" + System.currentTimeMillis() + "_" + fileName;
// 更新数据库记录
int result = trainingRecordService.uploadCertificate(recordId, path, null);
if (result > 0) {
Map<String, String> resultMap = new HashMap<>();
resultMap.put("fileName", fileName);
resultMap.put("path", path);
resultMap.put("url", path); // 前端可能需要url字段
return R.ok(resultMap);
} else {
return R.fail("更新证书路径失败");
}
}
/**
* 获取培训统计数据
*/
@@ -136,4 +203,15 @@ public class RsTrainingRecordController extends BaseController {
// 目前返回空数据,后续可以实现
return R.ok(Map.of());
}
/**
* 更新培训记录的证书附件ID
*/
@SaCheckPermission("rs:training:record:edit")
@Log(title = "更新培训记录证书附件", businessType = BusinessType.UPDATE)
@PutMapping("/attachments/{recordId}")
public R<Void> updateAttachments(@PathVariable Long recordId, @RequestBody Map<String, String> params) {
String attachmentIds = params.get("attachmentIds");
return toAjax(trainingRecordService.updateAttachments(recordId, attachmentIds));
}
}
@@ -34,21 +34,47 @@ public class RsTrainingRecord extends BaseEntity {
private Long courseId;
private Long userId;
private String userName;
private String department;
private String position;
private String reason;
private String status;
/**
* 所属部门
*/
private String department;
/**
* 职位
*/
private String position;
/**
* 申请理由
*/
private String reason;
/**
* 状态(0待审核 1已通过 2已拒绝 3进行中 4已完成)
*/
private String status;
/**
* 申请时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date applyTime;
/**
* 当前审批人
*/
private String currentApprover;
private String location;
private Integer duration;
/**
* 开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
/**
* 结束时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
@@ -78,6 +104,11 @@ public class RsTrainingRecord extends BaseEntity {
*/
private String certificateNumber;
/**
* 证书附件ID(多个用逗号分隔)
*/
private String attachmentIds;
/**
* 反馈意见
*/
@@ -85,5 +116,9 @@ public class RsTrainingRecord extends BaseEntity {
private String tenantId;
private Long createDept;
// 数据库字段 - 直接存储在培训记录表中
private String courseName;
private Integer duration;
private String location;
}
@@ -2,6 +2,7 @@ package org.dromara.rs.mapper;
import org.dromara.rs.domain.RsTrainingRecord;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import java.util.List;
/**
* 培训记录Mapper接口
@@ -10,5 +11,52 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
* @date 2025-07-16
*/
public interface RsTrainingRecordMapper extends BaseMapperPlus<RsTrainingRecord, RsTrainingRecord> {
/**
* 查询培训记录列表
*
* @param record 培训记录
* @return 培训记录集合
*/
List<RsTrainingRecord> selectRsTrainingRecordList(RsTrainingRecord record);
/**
* 查询培训记录详情
*
* @param recordId 记录ID
* @return 培训记录
*/
RsTrainingRecord selectRsTrainingRecordById(Long recordId);
/**
* 新增培训记录
*
* @param record 培训记录
* @return 结果
*/
int insertRsTrainingRecord(RsTrainingRecord record);
/**
* 更新培训记录
*
* @param record 培训记录
* @return 结果
*/
int updateRsTrainingRecord(RsTrainingRecord record);
/**
* 删除培训记录
*
* @param recordId 记录ID
* @return 结果
*/
int deleteRsTrainingRecordById(Long recordId);
/**
* 批量删除培训记录
*
* @param recordIds 记录ID数组
* @return 结果
*/
int deleteRsTrainingRecordByIds(Long[] recordIds);
}
@@ -105,4 +105,12 @@ public interface IRsTrainingRecordService {
* @return 培训记录
*/
RsTrainingRecord selectByApplicationId(Long applicationId);
/**
* 更新培训记录的证书附件ID
* @param recordId 记录ID
* @param attachmentIds 附件ID列表(逗号分隔)
* @return 结果
*/
int updateAttachments(Long recordId, String attachmentIds);
}
@@ -22,6 +22,9 @@ import org.dromara.rs.service.IRsTrainingRecordService;
import org.dromara.rs.domain.RsTrainingRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.dromara.common.core.utils.DateUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* 培训申请服务实现类
@@ -30,7 +33,7 @@ import org.slf4j.LoggerFactory;
* @date 2025-07-16
*/
@Service
public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationService {
public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationService, ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(RsTrainingApplicationServiceImpl.class);
@@ -40,8 +43,19 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
@Autowired
private RsTrainingCourseMapper trainingCourseMapper;
@Autowired
private IRsTrainingRecordService trainingRecordService;
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* 获取培训记录服务(延迟获取以避免循环依赖)
*/
private IRsTrainingRecordService getRecordService() {
return applicationContext.getBean(IRsTrainingRecordService.class);
}
/**
* 查询培训申请列表
@@ -352,15 +366,16 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
// 同步到培训记录表:状态为"1已通过""3进行中""4已完成"时自动写入
if ("1".equals(status) || "3".equals(status) || "4".equals(status)) {
log.info("【同步培训记录】触发,applicationId={}, status={}", applicationId, status);
RsTrainingRecord exist = trainingRecordService.selectByApplicationId(applicationId);
RsTrainingRecord exist = getRecordService().selectByApplicationId(applicationId);
if (exist == null) {
log.info("【同步培训记录】插入新记录,userId={}, courseId={}", application.getUserId(), application.getCourseId());
RsTrainingRecord record = new RsTrainingRecord();
// 设置记录ID(使用时间戳)
record.setRecordId(System.currentTimeMillis());
record.setApplicationId(application.getApplicationId());
record.setCourseId(application.getCourseId());
record.setUserId(application.getUserId());
record.setUserName(application.getUserName());
record.setTenantId(application.getTenantId());
record.setDepartment(application.getDepartment());
record.setPosition(application.getPosition());
record.setReason(application.getReason());
@@ -371,10 +386,14 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
record.setDuration(application.getDuration());
record.setStartTime(application.getStartTime());
record.setEndTime(application.getEndTime());
record.setCourseName(application.getCourseName());
record.setTenantId(application.getTenantId());
record.setCreateDept(application.getCreateDept());
record.setCourseName(application.getCourseName());
trainingRecordService.insertRsTrainingRecord(record);
record.setCreateBy(1L); // 默认系统用户
record.setCreateTime(DateUtils.getNowDate());
// 默认完成状态为进行中
record.setCompletionStatus("0");
getRecordService().insertRsTrainingRecord(record);
} else {
log.info("【同步培训记录】已存在,不重复插入,applicationId={}", applicationId);
}
@@ -9,9 +9,19 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.rs.mapper.RsTrainingRecordMapper;
import org.dromara.rs.domain.RsTrainingRecord;
import org.dromara.rs.service.IRsTrainingRecordService;
import org.dromara.rs.domain.RsTrainingApplication;
import org.dromara.rs.service.IRsTrainingApplicationService;
import org.dromara.rs.domain.RsTrainingCourse;
import org.dromara.rs.service.IRsTrainingCourseService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* 培训记录服务实现类
@@ -20,11 +30,30 @@ import org.dromara.rs.service.IRsTrainingRecordService;
* @date 2025-07-16
*/
@Service
public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService, ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(RsTrainingRecordServiceImpl.class);
@Autowired
private RsTrainingRecordMapper trainingRecordMapper;
@Autowired
private IRsTrainingCourseService courseService;
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* 获取培训申请服务(延迟获取以避免循环依赖)
*/
private IRsTrainingApplicationService getApplicationService() {
return applicationContext.getBean(IRsTrainingApplicationService.class);
}
/**
* 查询培训记录列表
*
@@ -34,7 +63,30 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
*/
@Override
public TableDataInfo<RsTrainingRecord> selectPageRsTrainingRecordList(RsTrainingRecord record, PageQuery pageQuery) {
return TableDataInfo.build(trainingRecordMapper.selectPage(pageQuery.build(), buildQueryWrapper(record)));
// 使用手动分页方式,参考培训申请的实现
List<RsTrainingRecord> allList = trainingRecordMapper.selectRsTrainingRecordList(record);
// 创建分页对象
Page<RsTrainingRecord> page = pageQuery.build();
long current = page.getCurrent();
long size = page.getSize();
// 计算分页
int start = (int) ((current - 1) * size);
int end = Math.min(start + (int) size, allList.size());
List<RsTrainingRecord> pageList;
if (start > allList.size()) {
pageList = Arrays.asList();
} else {
pageList = allList.subList(start, end);
}
// 构建分页结果
page.setRecords(pageList);
page.setTotal(allList.size());
return TableDataInfo.build(page);
}
/**
@@ -45,7 +97,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
*/
@Override
public List<RsTrainingRecord> selectRsTrainingRecordList(RsTrainingRecord record) {
return trainingRecordMapper.selectList(buildQueryWrapper(record));
return trainingRecordMapper.selectRsTrainingRecordList(record);
}
/**
@@ -58,9 +110,31 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
*/
@Override
public TableDataInfo<RsTrainingRecord> selectPageMyRecords(Long userId, RsTrainingRecord record, PageQuery pageQuery) {
LambdaQueryWrapper<RsTrainingRecord> lqw = buildQueryWrapper(record);
lqw.eq(RsTrainingRecord::getUserId, userId);
return TableDataInfo.build(trainingRecordMapper.selectPage(pageQuery.build(), lqw));
// 设置用户ID条件
record.setUserId(userId);
List<RsTrainingRecord> allList = trainingRecordMapper.selectRsTrainingRecordList(record);
// 创建分页对象
Page<RsTrainingRecord> page = pageQuery.build();
long current = page.getCurrent();
long size = page.getSize();
// 计算分页
int start = (int) ((current - 1) * size);
int end = Math.min(start + (int) size, allList.size());
List<RsTrainingRecord> pageList;
if (start > allList.size()) {
pageList = Arrays.asList();
} else {
pageList = allList.subList(start, end);
}
// 构建分页结果
page.setRecords(pageList);
page.setTotal(allList.size());
return TableDataInfo.build(page);
}
/**
@@ -71,7 +145,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
*/
@Override
public RsTrainingRecord selectRsTrainingRecordById(Long recordId) {
return trainingRecordMapper.selectById(recordId);
return trainingRecordMapper.selectRsTrainingRecordById(recordId);
}
/**
@@ -83,7 +157,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
@Override
public int insertRsTrainingRecord(RsTrainingRecord record) {
record.setCreateTime(DateUtils.getNowDate());
return trainingRecordMapper.insert(record);
return trainingRecordMapper.insertRsTrainingRecord(record);
}
/**
@@ -95,7 +169,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
@Override
public int updateRsTrainingRecord(RsTrainingRecord record) {
record.setUpdateTime(DateUtils.getNowDate());
return trainingRecordMapper.updateById(record);
return trainingRecordMapper.updateRsTrainingRecord(record);
}
/**
@@ -106,7 +180,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
*/
@Override
public int deleteRsTrainingRecordByIds(Long[] recordIds) {
return trainingRecordMapper.deleteBatchIds(Arrays.asList(recordIds));
return trainingRecordMapper.deleteRsTrainingRecordByIds(recordIds);
}
/**
@@ -117,7 +191,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
*/
@Override
public int deleteRsTrainingRecordById(Long recordId) {
return trainingRecordMapper.deleteById(recordId);
return trainingRecordMapper.deleteRsTrainingRecordById(recordId);
}
/**
@@ -129,7 +203,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
*/
@Override
public int updateCompletionStatus(Long recordId, String completionStatus) {
RsTrainingRecord record = trainingRecordMapper.selectById(recordId);
RsTrainingRecord record = trainingRecordMapper.selectRsTrainingRecordById(recordId);
if (record != null) {
record.setCompletionStatus(completionStatus);
@@ -139,7 +213,7 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
}
record.setUpdateTime(DateUtils.getNowDate());
return trainingRecordMapper.updateById(record);
return trainingRecordMapper.updateRsTrainingRecord(record);
}
return 0;
}
@@ -154,34 +228,115 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService {
*/
@Override
public int uploadCertificate(Long recordId, String certificatePath, String certificateNumber) {
RsTrainingRecord record = trainingRecordMapper.selectById(recordId);
RsTrainingRecord record = trainingRecordMapper.selectRsTrainingRecordById(recordId);
if (record != null) {
record.setCertificatePath(certificatePath);
record.setCertificateNumber(certificateNumber);
record.setUpdateTime(DateUtils.getNowDate());
return trainingRecordMapper.updateById(record);
return trainingRecordMapper.updateRsTrainingRecord(record);
}
return 0;
}
/**
* 构建查询条件
* 更新培训记录的证书附件ID
*
* @param recordId 记录ID
* @param attachmentIds 附件ID列表(逗号分隔)
* @return 结果
*/
private LambdaQueryWrapper<RsTrainingRecord> buildQueryWrapper(RsTrainingRecord record) {
LambdaQueryWrapper<RsTrainingRecord> lqw = new LambdaQueryWrapper<>();
lqw.eq(record.getCourseId() != null, RsTrainingRecord::getCourseId, record.getCourseId());
lqw.like(record.getCourseName() != null, RsTrainingRecord::getCourseName, record.getCourseName());
lqw.eq(record.getUserId() != null, RsTrainingRecord::getUserId, record.getUserId());
lqw.like(record.getUserName() != null, RsTrainingRecord::getUserName, record.getUserName());
lqw.eq(record.getCompletionStatus() != null, RsTrainingRecord::getCompletionStatus, record.getCompletionStatus());
lqw.eq(record.getStatus() != null, RsTrainingRecord::getStatus, record.getStatus());
return lqw;
@Override
public int updateAttachments(Long recordId, String attachmentIds) {
RsTrainingRecord record = trainingRecordMapper.selectRsTrainingRecordById(recordId);
if (record != null) {
record.setAttachmentIds(attachmentIds);
record.setUpdateTime(DateUtils.getNowDate());
return trainingRecordMapper.updateRsTrainingRecord(record);
}
return 0;
}
@Override
public RsTrainingRecord selectByApplicationId(Long applicationId) {
LambdaQueryWrapper<RsTrainingRecord> lqw = new LambdaQueryWrapper<>();
lqw.eq(RsTrainingRecord::getApplicationId, applicationId);
return trainingRecordMapper.selectOne(lqw);
RsTrainingRecord record = new RsTrainingRecord();
record.setApplicationId(applicationId);
List<RsTrainingRecord> list = trainingRecordMapper.selectRsTrainingRecordList(record);
return list.isEmpty() ? null : list.get(0);
}
/**
* 修复培训记录中缺失的课程信息
*
* @return 修复的记录数量
*/
public int fixMissingCourseInfo() {
log.info("开始修复培训记录中缺失的课程信息");
// 获取所有需要修复的记录
List<RsTrainingRecord> allRecords = trainingRecordMapper.selectRsTrainingRecordList(new RsTrainingRecord());
int fixedCount = 0;
for (RsTrainingRecord record : allRecords) {
boolean needUpdate = false;
// 检查是否缺失课程信息
if (StringUtils.isEmpty(record.getCourseName()) ||
StringUtils.isEmpty(record.getLocation()) ||
record.getDuration() == null) {
log.info("修复记录 recordId={}, applicationId={}", record.getRecordId(), record.getApplicationId());
// 尝试从申请表获取信息
RsTrainingApplication application = getApplicationService().selectRsTrainingApplicationById(record.getApplicationId());
if (application != null) {
if (StringUtils.isEmpty(record.getCourseName()) && StringUtils.isNotBlank(application.getCourseName())) {
record.setCourseName(application.getCourseName());
needUpdate = true;
}
if (StringUtils.isEmpty(record.getLocation()) && StringUtils.isNotBlank(application.getLocation())) {
record.setLocation(application.getLocation());
needUpdate = true;
}
if (record.getDuration() == null && application.getDuration() != null) {
record.setDuration(application.getDuration());
needUpdate = true;
}
}
// 如果申请表中也没有,尝试从课程表获取
if ((StringUtils.isEmpty(record.getCourseName()) || StringUtils.isEmpty(record.getLocation()) || record.getDuration() == null)
&& record.getCourseId() != null) {
RsTrainingCourse course = courseService.selectRsTrainingCourseById(record.getCourseId());
if (course != null) {
if (StringUtils.isEmpty(record.getCourseName())) {
record.setCourseName(course.getTitle());
needUpdate = true;
}
if (StringUtils.isEmpty(record.getLocation())) {
record.setLocation(course.getLocation());
needUpdate = true;
}
if (record.getDuration() == null) {
record.setDuration(course.getDuration());
needUpdate = true;
}
}
}
// 更新记录
if (needUpdate) {
record.setUpdateTime(DateUtils.getNowDate());
record.setUpdateBy(1L); // 系统用户
trainingRecordMapper.updateRsTrainingRecord(record);
fixedCount++;
log.info("已修复记录: courseName={}, location={}, duration={}",
record.getCourseName(), record.getLocation(), record.getDuration());
}
}
}
log.info("修复完成,共修复 {} 条记录", fixedCount);
return fixedCount;
}
}
@@ -10,36 +10,41 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="courseId" column="course_id"/>
<result property="userId" column="user_id"/>
<result property="userName" column="user_name"/>
<result property="completionStatus" column="completion_status"/>
<result property="completionTime" column="completion_time"/>
<result property="score" column="score"/>
<result property="certificatePath" column="certificate_path"/>
<result property="certificateNumber" column="certificate_number"/>
<result property="attachmentIds" column="attachment_ids"/>
<result property="feedback" column="feedback"/>
<result property="tenantId" column="tenant_id"/>
<result property="createDept" column="create_dept"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<!-- 直接从记录表查询的字段 -->
<result property="department" column="department"/>
<result property="position" column="position"/>
<result property="reason" column="reason"/>
<result property="status" column="status"/>
<result property="applyTime" column="apply_time"/>
<result property="currentApprover" column="current_approver"/>
<result property="location" column="location"/>
<result property="duration" column="duration"/>
<result property="startTime" column="start_time"/>
<result property="endTime" column="end_time"/>
<result property="completionStatus" column="completion_status"/>
<result property="completionTime" column="completion_time"/>
<result property="score" column="score"/>
<result property="certificatePath" column="certificate_path"/>
<result property="certificateNumber" column="certificate_number"/>
<result property="feedback" column="feedback"/>
<result property="tenantId" column="tenant_id"/>
<result property="createDept" column="create_dept"/>
<result property="courseName" column="course_name"/>
<result property="duration" column="duration"/>
<result property="location" column="location"/>
</resultMap>
<sql id="selectRsTrainingRecordVo">
select r.record_id, r.application_id, r.course_id, r.user_id, r.user_name,
r.completion_status, r.completion_time, r.score, r.certificate_path, r.certificate_number,
r.feedback, r.tenant_id, r.create_by, r.create_time, r.update_by, r.update_time, r.remark,
r.location, r.duration, r.apply_time, r.status, r.course_name,
c.title as course_title, a.department
r.completion_status, r.completion_time, r.score, r.certificate_path, r.certificate_number, r.attachment_ids,
r.feedback, r.tenant_id, r.create_dept, r.create_by, r.create_time, r.update_by, r.update_time,
r.department, r.position, r.reason, r.status, r.apply_time, r.current_approver, r.start_time, r.end_time,
r.course_name, r.duration, r.location
from rs_training_record r
left join rs_training_course c on r.course_id = c.course_id and c.deleted = '0'
left join rs_training_application a on r.application_id = a.application_id and a.deleted = '0'
</sql>
<select id="selectRsTrainingRecordList" parameterType="RsTrainingRecord" resultMap="RsTrainingRecordResult">
@@ -52,20 +57,72 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="userName != null and userName != ''">and r.user_name like concat('%', #{userName}, '%')</if>
<if test="completionStatus != null and completionStatus != ''">and r.completion_status = #{completionStatus}</if>
<if test="status != null and status != ''">and r.status = #{status}</if>
<if test="department != null and department != ''">and r.department like concat('%', #{department}, '%')</if>
<if test="params.beginTime != null and params.beginTime != ''">and date_format(r.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')</if>
<if test="params.endTime != null and params.endTime != ''">and date_format(r.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')</if>
</where>
ORDER BY r.create_time DESC
</select>
<select id="selectRsTrainingRecordById" parameterType="Long" resultMap="RsTrainingRecordResult">
<include refid="selectRsTrainingRecordVo"/>
where r.record_id = #{recordId} and r.deleted = '0'
</select>
<insert id="insertRsTrainingRecord" parameterType="org.dromara.rs.domain.RsTrainingRecord">
INSERT INTO rs_training_record (
application_id, course_id, user_id, user_name, department, position, reason, status,
apply_time, current_approver, location, duration, start_time, end_time, tenant_id, create_dept, course_name
record_id, application_id, course_id, user_id, user_name, completion_status, completion_time, score,
certificate_path, certificate_number, attachment_ids, feedback, location, duration, apply_time,
status, course_name, department, position, reason, current_approver, start_time, end_time,
tenant_id, create_dept, create_by, create_time, deleted
) VALUES (
#{applicationId}, #{courseId}, #{userId}, #{userName}, #{department}, #{position}, #{reason}, #{status},
#{applyTime}, #{currentApprover}, #{location}, #{duration}, #{startTime}, #{endTime}, #{tenantId}, #{createDept}, #{courseName}
#{recordId}, #{applicationId}, #{courseId}, #{userId}, #{userName}, #{completionStatus}, #{completionTime}, #{score},
#{certificatePath}, #{certificateNumber}, #{attachmentIds}, #{feedback}, #{location}, #{duration}, #{applyTime},
#{status}, #{courseName}, #{department}, #{position}, #{reason}, #{currentApprover}, #{startTime}, #{endTime},
#{tenantId}, #{createDept}, #{createBy}, #{createTime}, '0'
)
</insert>
<update id="updateRsTrainingRecord" parameterType="org.dromara.rs.domain.RsTrainingRecord">
UPDATE rs_training_record
<set>
<if test="applicationId != null">application_id = #{applicationId},</if>
<if test="courseId != null">course_id = #{courseId},</if>
<if test="userId != null">user_id = #{userId},</if>
<if test="userName != null and userName != ''">user_name = #{userName},</if>
<if test="completionStatus != null">completion_status = #{completionStatus},</if>
<if test="completionTime != null">completion_time = #{completionTime},</if>
<if test="score != null">score = #{score},</if>
<if test="certificatePath != null">certificate_path = #{certificatePath},</if>
<if test="certificateNumber != null">certificate_number = #{certificateNumber},</if>
<if test="attachmentIds != null">attachment_ids = #{attachmentIds},</if>
<if test="feedback != null">feedback = #{feedback},</if>
<if test="location != null">location = #{location},</if>
<if test="duration != null">duration = #{duration},</if>
<if test="applyTime != null">apply_time = #{applyTime},</if>
<if test="status != null">status = #{status},</if>
<if test="courseName != null">course_name = #{courseName},</if>
<if test="department != null">department = #{department},</if>
<if test="position != null">position = #{position},</if>
<if test="reason != null">reason = #{reason},</if>
<if test="currentApprover != null">current_approver = #{currentApprover},</if>
<if test="startTime != null">start_time = #{startTime},</if>
<if test="endTime != null">end_time = #{endTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime}</if>
</set>
where record_id = #{recordId}
</update>
<delete id="deleteRsTrainingRecordById" parameterType="Long">
UPDATE rs_training_record SET deleted = '1' where record_id = #{recordId}
</delete>
<delete id="deleteRsTrainingRecordByIds" parameterType="String">
UPDATE rs_training_record SET deleted = '1' where record_id in
<foreach item="recordId" collection="array" open="(" separator="," close=")">
#{recordId}
</foreach>
</delete>
</mapper>
@@ -5,24 +5,24 @@
-- ----------------------------
-- 培训课程表测试数据
-- ----------------------------
INSERT INTO `rs_training_course` (`course_id`, `title`, `category`, `description`, `start_time`, `end_time`, `duration`, `location`, `max_participants`, `current_participants`, `status`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`)
INSERT INTO `rs_training_course` (`course_id`, `title`, `description`, `instructor`, `duration`, `location`, `capacity`, `status`, `start_time`, `end_time`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`)
VALUES
(10001, '新教师入职培训', 'basic', '为新入职教师提供学校规章制度、教学系统使用等基础培训', '2025-08-15 09:00:00', '2025-08-16 17:00:00', 16, '行政楼三楼会议室', 30, 12, '0', '000000', 1, NOW(), 1, NOW(), '重要培训,所有新入职教师必须参加', '0'),
(10002, 'Excel高级应用培训', 'skill', '包括数据透视表、宏、VBA基础等Excel高级功能应用', '2025-08-20 13:30:00', '2025-08-22 16:30:00', 12, '教学楼B区205', 20, 8, '0', '000000', 1, NOW(), 1, NOW(), '面向行政人员的办公技能提升', '0'),
(10003, '教学评估标准培训', 'teaching', '介绍最新的教学评估标准和评价体系', '2025-09-05 09:00:00', '2025-09-05 17:00:00', 8, '线上培训', 100, 45, '0', '000000', 1, NOW(), 1, NOW(), '所有教师须参加', '0'),
(10004, '心理健康辅导技能', 'counseling', '学习学生心理问题识别和初步辅导技能', '2025-09-10 09:00:00', '2025-09-11 17:00:00', 16, '心理咨询中心', 25, 15, '0', '000000', 1, NOW(), 1, NOW(), '适合班主任和辅导员', '0'),
(10005, '科研项目申报指南', 'research', '介绍各类科研项目的申报流程、注意事项和技巧', '2025-09-15 14:00:00', '2025-09-16 17:00:00', 10, '科研楼报告厅', 50, 22, '0', '000000', 1, NOW(), 1, NOW(), '面向有科研项目需求的教师', '0');
(10001, '新入职教师培训', '针对新入职教师的岗前培训,包括教学理念、师德师风、教学方法等内容', '张教授', 16, '学术会议厅', 50, '1', '2025-08-01 09:00:00', '2025-08-03 17:00:00', '000000', 1, NOW(), 1, NOW(), '重要培训', '0'),
(10002, '教育技术与信息化', '介绍现代教育技术在教学中的应用,包括多媒体教学、在线教育平台等', '李博士', 12, '多媒体教室', 30, '1', '2025-08-10 09:00:00', '2025-08-12 17:00:00', '000000', 1, NOW(), 1, NOW(), '技术培训', '0'),
(10003, '教学方法与技能培训', '提升教师教学技能,包括课堂管理、学生互动、教学评估等', '王老师', 8, '实训楼', 40, '1', '2025-08-05 14:00:00', '2025-08-05 18:00:00', '000000', 1, NOW(), 1, NOW(), '实用培训', '0'),
(10004, '学生管理与心理健康', '帮助教师了解学生心理,掌握学生管理技巧和心理疏导方法', '陈心理师', 6, '学术报告厅', 60, '1', '2025-08-15 14:00:00', '2025-08-15 18:00:00', '000000', 1, NOW(), 1, NOW(), '心理培训', '0'),
(10005, '科研方法与学术写作', '指导教师进行科学研究,提升学术写作和论文发表能力', '赵教授', 20, '图书馆报告厅', 25, '1', '2025-08-25 09:00:00', '2025-08-30 17:00:00', '000000', 1, NOW(), 1, NOW(), '科研培训', '0');
-- ----------------------------
-- 培训申请表测试数据
-- ----------------------------
INSERT INTO `rs_training_application` (`application_id`, `course_id`, `user_id`, `user_name`, `department`, `position`, `reason`, `status`, `apply_time`, `current_approver`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`)
INSERT INTO `rs_training_application` (`application_id`, `course_id`, `user_id`, `user_name`, `department`, `position`, `reason`, `status`, `apply_time`, `current_approver`, `process_instance_id`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`)
VALUES
(20001, 10001, 2, '张老师', '信息学院', '讲师', '作为新入职教师,需要了解学校规章制度和教学系统', '1', '2025-07-20 10:15:22', '王主任', '000000', 2, NOW(), 2, NOW(), NULL, '0'),
(20002, 10002, 3, '李秘书', '行政部', '文员', '日常工作需要处理大量Excel表格,希望提升技能', '0', '2025-07-21 09:05:33', '赵部长', '000000', 3, NOW(), 3, NOW(), NULL, '0'),
(20003, 10003, 4, '刘老师', '外语学院', '副教授', '了解最新教学评估标准,提升教学质量', '1', '2025-07-22 14:30:45', '孙院长', '000000', 4, NOW(), 4, NOW(), NULL, '0'),
(20004, 10004, 5, '钱辅导员', '学生处', '辅导员', '提升学生心理问题识别和初步干预能力', '2', '2025-07-23 11:20:18', '周主任', '000000', 5, NOW(), 5, NOW(), '该老师近期有其他重要工作', '0'),
(20005, 10005, 6, '赵研究员', '科研处', '研究员', '正在准备申报国家级项目,需要了解最新申报指南', '0', '2025-07-24 16:40:57', '吴处长', '000000', 6, NOW(), 6, NOW(), NULL, '0');
(20001, 10001, 2, '张老师', '教务处', '教师', '新入职需要培训', '1', '2025-07-25 12:03:49', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), NULL, '0'),
(20002, 10002, 3, '王老师', '信息技术中心', '工程师', '学习新技术', '1', '2025-08-01 12:37:17', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'),
(20003, 10003, 4, '刘老师', '计算机学院', '讲师', '提升教学技能', '1', '2025-07-25 12:04:06', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '培训尚未结束', '0'),
(20004, 10004, 5, '李老师', '学生处', '辅导员', '提升学生管理能力', '1', '2025-08-06 14:24:37', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'),
(20005, 10005, 6, '陈老师', '科研处', '副教授', '提升科研能力', '1', '2025-08-22 15:25:51', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0');
-- ----------------------------
-- 培训审批表测试数据
@@ -34,9 +34,5 @@ VALUES
(30003, 20004, 103, '周主任', 1, '2', '近期有重要工作交接,建议下次再参加', '2025-07-24 14:20:00', '000000', 103, NOW(), 103, NOW(), NULL, '0');
-- ----------------------------
-- 培训记录表测试数据
-- ----------------------------
INSERT INTO `rs_training_record` (`record_id`, `application_id`, `course_id`, `user_id`, `user_name`, `completion_status`, `completion_time`, `score`, `certificate_path`, `certificate_number`, `feedback`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`)
VALUES
(40001, 20001, 10001, 2, '张老师', '1', '2025-08-16 17:00:00', 92.5, '/certificates/10001/20001.pdf', 'CERT20250816001', '培训内容丰富,对新入职教师很有帮助', '000000', 1, NOW(), 1, NOW(), NULL, '0'),
(40002, 20003, 10003, 4, '刘老师', '0', NULL, NULL, NULL, NULL, NULL, '000000', 1, NOW(), 1, NOW(), '培训尚未结束', '0');
-- 培训记录表测试数据已删除,请通过申请界面新增
-- ----------------------------
@@ -91,8 +91,21 @@ create table rs_training_record (
score decimal(5,2) comment '成绩',
certificate_path varchar(255) comment '证书路径',
certificate_number varchar(50) comment '证书编号',
attachment_ids varchar(255) comment '证书附件ID(多个用逗号分隔)',
feedback text comment '培训反馈',
location varchar(255) comment '培训地点',
duration int(11) comment '学时',
apply_time datetime comment '申请时间',
status varchar(10) comment '申请状态',
course_name varchar(255) comment '课程名称',
department varchar(255) comment '所属部门',
position varchar(255) comment '职位',
reason varchar(255) comment '申请理由',
current_approver varchar(255) comment '当前审批人',
start_time datetime comment '开始时间',
end_time datetime comment '结束时间',
tenant_id varchar(20) default '000000' comment '租户编号',
create_dept bigint(20) comment '创建部门',
create_by bigint(20) comment '创建者',
create_time datetime comment '创建时间',
update_by bigint(20) comment '更新者',
+12
View File
@@ -126,6 +126,7 @@ export interface RecordVO {
completionTime?: string;
certificatePath?: string;
certificateNumber?: string;
certificateFileName?: string;
feedback?: string;
createTime: string;
updateTime: string;
@@ -148,6 +149,8 @@ export interface RecordForm {
completionTime?: string;
certificatePath?: string;
certificateNumber?: string;
certificateFileName?: string;
attachmentIds?: string;
feedback?: string;
remark?: string;
}
@@ -424,3 +427,12 @@ export function resubmitTrainingApplication(applicationId: string | number, data
data: data
});
}
// 更新培训记录的证书附件ID
export function updateRecordAttachments(recordId: string | number, data: { attachmentIds: string }) {
return request({
url: `/rs/training/record/attachments/${recordId}`,
method: 'put',
data: data
});
}
+3 -1
View File
@@ -372,7 +372,9 @@
</el-descriptions-item>
<el-descriptions-item label="申请人数">{{ viewForm.applicantCount }}</el-descriptions-item>
<el-descriptions-item label="浏览次数">{{ viewForm.viewCount }}</el-descriptions-item>
<el-descriptions-item label="发布时间">{{ parseTime(viewForm.publishTime) || '未发布' }}</el-descriptions-item>
<el-descriptions-item label="发布时间">
{{ parseTime(viewForm.publishTime) || (viewForm.isActive === '0' ? parseTime(viewForm.createTime) || '长期有效' : '未发布') }}
</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ parseTime(viewForm.createTime) }}</el-descriptions-item>
<el-descriptions-item label="职位描述" :span="2">
<div class="view-description">{{ viewForm.description || '暂无描述' }}</div>
+11 -8
View File
@@ -82,12 +82,7 @@
<el-table-column prop="location" label="工作地点" width="120" />
<el-table-column prop="publishTime" label="发布时间" width="120">
<template #default="scope">
{{ formatDate(scope.row.publishTime) }}
</template>
</el-table-column>
<el-table-column prop="publishTime" label="发布时间" width="120">
<template #default="scope">
<span>{{ formatDate(scope.row.publishTime) }}</span>
{{ formatDate(scope.row.publishTime) || formatDate(scope.row.createTime) || '长期有效' }}
</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right">
@@ -181,10 +176,10 @@
<h4>招聘信息</h4>
<el-descriptions :column="2" border>
<el-descriptions-item label="发布时间">
{{ formatDate(currentJob.publishTime) }}
{{ formatDate(currentJob.publishTime) || formatDate(currentJob.createTime) || '长期有效' }}
</el-descriptions-item>
<el-descriptions-item label="截止时间">
<span>{{ formatDate(currentJob.publishTime) }}</span>
<span>{{ getDeadlineDate(currentJob.publishTime) }}</span>
</el-descriptions-item>
<el-descriptions-item label="浏览次数"> {{ currentJob.viewCount || 0 }} 次 </el-descriptions-item>
<el-descriptions-item label="应聘人数"> {{ currentJob.applicantCount || 0 }} 人 </el-descriptions-item>
@@ -257,6 +252,14 @@ const formatDate = (dateStr: string) => {
return parseTime(dateStr, '{y}-{m}-{d}');
};
// 计算截止日期(发布日期+30天)
const getDeadlineDate = (publishTime: string) => {
if (!publishTime) return '长期有效';
const publishDate = new Date(publishTime);
const deadlineDate = new Date(publishDate.getTime() + 30 * 24 * 60 * 60 * 1000); // 30天后
return parseTime(deadlineDate.toString(), '{y}-{m}-{d}');
};
// 检查是否过期(使用publishTime判断,假设30天有效期)
const isJobExpired = (publishTime: string) => {
if (!publishTime) return false;
File diff suppressed because it is too large. Load diff