后端考勤:增加课次学员考勤名册查询

This commit is contained in:
wangzhibiao committed 2026-09-21 23:21:29 +08:00
1 parent b716e55ac7
commit 18b8a07f55
4 files changed
+354

No files matched your search

@@ -3,6 +3,7 @@ package com.ruoyi.web.controller.train;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
@@ -70,4 +71,14 @@ public class AttendanceController extends BaseController
attendanceAppService.adjust(checkinId, dto, SecurityUtils.getUsername());
return success();
}
/**
* 查询课次学员考勤名册 (纯只读,供培训管理员点名签到)。
*/
@PreAuthorize("@ss.hasPermi('train:attendance:list')")
@GetMapping("/lessons/{lessonId}/roster")
public AjaxResult getLessonRoster(@PathVariable Long lessonId)
{
return success(attendanceAppService.getLessonRoster(lessonId));
}
}
@@ -0,0 +1,227 @@
package com.ruoyi.train.domain.vo;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 课次学员考勤名册对象 (Attendance Lesson Roster VO)
* 供培训管理员在业务端查看课次应到学员并完成首次签到。
*
* @author ruoyi
* @date 2026-09-21
*/
public class AttendanceLessonRosterVO implements Serializable
{
private static final long serialVersionUID = 1L;
/** 参训档案ID */
private Long enrollmentId;
/** 课次ID */
private Long lessonId;
/** 学员ID/工号 */
private String studentId;
/** 学员姓名 */
private String studentName;
/** 性别 */
private String gender;
/** 所属部门/机构 */
private String deptName;
/** 脱敏手机号 */
private String phoneMasked;
/** 参训状态 (0:待报到, 1:已报到, 2:请假, 3:放弃, 4:已完成) */
private Integer enrollmentStatus;
/** 考勤打卡记录ID (未打卡为null) */
private Long checkinId;
/** 签到方式 (1:人脸, 2:刷卡, 3:扫码, 4:教师/管理代签,未打卡为null) */
private Integer checkinWay;
/** 考勤状态 (0:缺勤, 1:正常, 2:迟到, 3:早退, 4:请假,未打卡为null) */
private Integer attendanceStatus;
/** 签到时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date signInTime;
/** 签退时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date signOutTime;
/** 是否允许签到 (尚未产生签到事实且参训有效) */
private Boolean canSignIn;
/** 是否允许签退 (已签到且未签退) */
private Boolean canSignOut;
/** 是否允许调整 (已有考勤记录) */
private Boolean canAdjust;
public Long getEnrollmentId()
{
return enrollmentId;
}
public void setEnrollmentId(Long enrollmentId)
{
this.enrollmentId = enrollmentId;
}
public Long getLessonId()
{
return lessonId;
}
public void setLessonId(Long lessonId)
{
this.lessonId = lessonId;
}
public String getStudentId()
{
return studentId;
}
public void setStudentId(String studentId)
{
this.studentId = studentId;
}
public String getStudentName()
{
return studentName;
}
public void setStudentName(String studentName)
{
this.studentName = studentName;
}
public String getGender()
{
return gender;
}
public void setGender(String gender)
{
this.gender = gender;
}
public String getDeptName()
{
return deptName;
}
public void setDeptName(String deptName)
{
this.deptName = deptName;
}
public String getPhoneMasked()
{
return phoneMasked;
}
public void setPhoneMasked(String phoneMasked)
{
this.phoneMasked = phoneMasked;
}
public Integer getEnrollmentStatus()
{
return enrollmentStatus;
}
public void setEnrollmentStatus(Integer enrollmentStatus)
{
this.enrollmentStatus = enrollmentStatus;
}
public Long getCheckinId()
{
return checkinId;
}
public void setCheckinId(Long checkinId)
{
this.checkinId = checkinId;
}
public Integer getCheckinWay()
{
return checkinWay;
}
public void setCheckinWay(Integer checkinWay)
{
this.checkinWay = checkinWay;
}
public Integer getAttendanceStatus()
{
return attendanceStatus;
}
public void setAttendanceStatus(Integer attendanceStatus)
{
this.attendanceStatus = attendanceStatus;
}
public Date getSignInTime()
{
return signInTime;
}
public void setSignInTime(Date signInTime)
{
this.signInTime = signInTime;
}
public Date getSignOutTime()
{
return signOutTime;
}
public void setSignOutTime(Date signOutTime)
{
this.signOutTime = signOutTime;
}
public Boolean getCanSignIn()
{
return canSignIn;
}
public void setCanSignIn(Boolean canSignIn)
{
this.canSignIn = canSignIn;
}
public Boolean getCanSignOut()
{
return canSignOut;
}
public void setCanSignOut(Boolean canSignOut)
{
this.canSignOut = canSignOut;
}
public Boolean getCanAdjust()
{
return canAdjust;
}
public void setCanAdjust(Boolean canAdjust)
{
this.canAdjust = canAdjust;
}
}
@@ -37,4 +37,12 @@ public interface AttendanceAppService
* @param operator 操作人
*/
void adjust(Long checkinId, AttendanceAdjustDTO dto, String operator);
/**
* 查询课次学员考勤名册 (纯只读,供培训管理员点名签到)。
*
* @param lessonId 课次ID
* @return 考勤名册列表
*/
java.util.List<com.ruoyi.train.domain.vo.AttendanceLessonRosterVO> getLessonRoster(Long lessonId);
}
@@ -1,6 +1,10 @@
package com.ruoyi.train.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,6 +21,9 @@ import com.ruoyi.train.domain.TrainStudentCheckin;
import com.ruoyi.train.domain.dto.AttendanceAdjustDTO;
import com.ruoyi.train.domain.dto.AttendanceSignInDTO;
import com.ruoyi.train.domain.dto.AttendanceSignOutDTO;
import com.ruoyi.train.domain.query.EnrollmentQuery;
import com.ruoyi.train.domain.vo.AttendanceLessonRosterVO;
import com.ruoyi.train.domain.vo.EnrollmentItemVO;
import com.ruoyi.train.mapper.TrainEnrollmentMapper;
import com.ruoyi.train.mapper.TrainLessonMapper;
import com.ruoyi.train.mapper.TrainPlanCourseMapper;
@@ -231,4 +238,105 @@ public class AttendanceAppServiceImpl implements AttendanceAppService
trainStudentCheckinMapper.updateTrainStudentCheckin(checkin);
}
@Override
public List<AttendanceLessonRosterVO> getLessonRoster(Long lessonId)
{
if (lessonId == null)
{
throw new ServiceException("课次ID不能为空", 400);
}
TrainLesson lesson = trainLessonMapper.selectTrainLessonById(lessonId);
if (lesson == null)
{
throw new ServiceException(AttendanceErrorCode.LESSON_NOT_FOUND_MESSAGE, AttendanceErrorCode.LESSON_NOT_FOUND);
}
TrainPlanCourse planCourse = trainPlanCourseMapper.selectTrainPlanCourseById(lesson.getPlanCourseId());
if (planCourse == null || planCourse.getPlanId() == null)
{
throw new ServiceException("课次未关联有效的培训课程计划", 400);
}
EnrollmentQuery query = new EnrollmentQuery();
query.setPlanId(planCourse.getPlanId());
List<EnrollmentItemVO> enrollments = trainEnrollmentMapper.selectEnrollmentItemList(query);
List<TrainStudentCheckin> checkins = trainStudentCheckinMapper.selectCheckinsByLessonId(lessonId);
Map<Long, TrainStudentCheckin> checkinMap = new HashMap<>();
if (checkins != null)
{
for (TrainStudentCheckin c : checkins)
{
if (c.getEnrollmentId() != null)
{
checkinMap.put(c.getEnrollmentId(), c);
}
}
}
List<AttendanceLessonRosterVO> roster = new ArrayList<>();
if (enrollments != null)
{
for (EnrollmentItemVO item : enrollments)
{
// 排除已放弃参训学员 (STATUS_ABANDONED = 3)
if (item.getEnrollmentStatus() != null && item.getEnrollmentStatus() == EnrollmentErrorCode.STATUS_ABANDONED)
{
continue;
}
AttendanceLessonRosterVO vo = new AttendanceLessonRosterVO();
vo.setEnrollmentId(item.getEnrollmentId());
vo.setLessonId(lessonId);
vo.setStudentId(item.getStudentId());
vo.setStudentName(item.getStudentName());
vo.setGender(item.getGender());
vo.setDeptName(item.getOrgName());
vo.setPhoneMasked(maskPhone(item.getPhone()));
vo.setEnrollmentStatus(item.getEnrollmentStatus());
TrainStudentCheckin c = checkinMap.get(item.getEnrollmentId());
if (c != null)
{
vo.setCheckinId(c.getCheckinId());
vo.setCheckinWay(c.getCheckinWay());
vo.setAttendanceStatus(c.getAttendanceStatus());
vo.setSignInTime(c.getSignInTime());
vo.setSignOutTime(c.getSignOutTime());
// 仅当已有记录但未打签到时间时允许补签;已有签到时间则禁止重复签到
vo.setCanSignIn(c.getSignInTime() == null);
vo.setCanSignOut(c.getSignInTime() != null && c.getSignOutTime() == null);
vo.setCanAdjust(true);
}
else
{
vo.setCheckinId(null);
vo.setCheckinWay(null);
vo.setAttendanceStatus(null);
vo.setSignInTime(null);
vo.setSignOutTime(null);
vo.setCanSignIn(true);
vo.setCanSignOut(false);
vo.setCanAdjust(false);
}
roster.add(vo);
}
}
return roster;
}
private String maskPhone(String phone)
{
if (phone == null || phone.length() < 7)
{
return phone;
}
if (phone.length() == 11)
{
return phone.substring(0, 3) + "****" + phone.substring(7);
}
return phone.substring(0, 2) + "****" + phone.substring(phone.length() - 2);
}
}