|
|
@@ -292,6 +292,61 @@ public class PeerReviewService {
|
|
|
return reviewerInvitationSummary(invitation);
|
|
|
}
|
|
|
|
|
|
+ public void requireCanFinalize(Long periodId, Long subjectUserId, Long leaderId) {
|
|
|
+ PeerReviewRequest request = requireLeaderRequest(periodId, subjectUserId, leaderId);
|
|
|
+ List<PeerReviewInvitation> invitations = requestInvitations(request.getId());
|
|
|
+ long submitted = invitations.stream()
|
|
|
+ .filter(item -> INVITATION_SUBMITTED.equals(item.getStatus())).count();
|
|
|
+ if (submitted < 2) {
|
|
|
+ throw new BusinessException("至少需要 2 份有效环评才能终评");
|
|
|
+ }
|
|
|
+ AssessmentPeriod period = periodMapper.selectById(periodId);
|
|
|
+ boolean deadlinePassed = period != null && period.getPeerReviewDeadline() != null
|
|
|
+ && LocalDateTime.now().isAfter(period.getPeerReviewDeadline());
|
|
|
+ if (!deadlinePassed && invitations.stream().anyMatch(this::isOutstandingEffectiveInvitation)) {
|
|
|
+ throw new BusinessException("评价截止前须等待全部有效邀请提交后才能终评");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public PeerReviewDto.LeaderSummary getLeaderSummary(Long periodId, Long subjectUserId, Long leaderId) {
|
|
|
+ PeerReviewRequest request = requireLeaderRequest(periodId, subjectUserId, leaderId);
|
|
|
+ List<PeerReviewInvitation> invitations = requestInvitations(request.getId());
|
|
|
+ Map<Long, SysUser> users = usersById(invitations.stream()
|
|
|
+ .filter(item -> INVITATION_SUBMITTED.equals(item.getStatus()))
|
|
|
+ .map(PeerReviewInvitation::getReviewerId).toList());
|
|
|
+
|
|
|
+ PeerReviewDto.LeaderSummary summary = leaderSummary(request);
|
|
|
+ summary.setInvitedCount(invitations.size());
|
|
|
+ summary.setSubmittedCount((int) invitations.stream()
|
|
|
+ .filter(item -> INVITATION_SUBMITTED.equals(item.getStatus())).count());
|
|
|
+ summary.setCanFinalize(canFinalize(periodId, invitations));
|
|
|
+ List<PeerReviewInvitation> submitted = invitations.stream()
|
|
|
+ .filter(item -> INVITATION_SUBMITTED.equals(item.getStatus())).toList();
|
|
|
+ summary.setResponses(submitted.stream().map(item -> leaderResponse(item, users.get(item.getReviewerId())))
|
|
|
+ .toList());
|
|
|
+ summary.setAverageGoalContributionScore(average(submitted, PeerReviewInvitation::getGoalContributionScore));
|
|
|
+ summary.setAverageCollaborationScore(average(submitted, PeerReviewInvitation::getCollaborationScore));
|
|
|
+ summary.setAverageAccountabilityScore(average(submitted, PeerReviewInvitation::getAccountabilityScore));
|
|
|
+ return summary;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<PeerReviewDto.LeaderQueueItem> getLeaderQueue(Long periodId, Long leaderId) {
|
|
|
+ return requestMapper.selectList(new LambdaQueryWrapper<PeerReviewRequest>()
|
|
|
+ .eq(PeerReviewRequest::getPeriodId, periodId)
|
|
|
+ .eq(PeerReviewRequest::getLeaderId, leaderId))
|
|
|
+ .stream().map(request -> {
|
|
|
+ PeerReviewDto.LeaderSummary summary = getLeaderSummary(
|
|
|
+ periodId, request.getSubjectUserId(), leaderId);
|
|
|
+ PeerReviewDto.LeaderQueueItem item = new PeerReviewDto.LeaderQueueItem();
|
|
|
+ item.setRequestId(summary.getRequestId());
|
|
|
+ item.setSubjectUserId(summary.getSubjectUserId());
|
|
|
+ item.setStatus(summary.getStatus());
|
|
|
+ item.setSubmittedCount(summary.getSubmittedCount());
|
|
|
+ item.setCanFinalize(summary.isCanFinalize());
|
|
|
+ return item;
|
|
|
+ }).toList();
|
|
|
+ }
|
|
|
+
|
|
|
@Transactional
|
|
|
public PeerReviewDto.LeaderSummary reviewList(Long requestId, Long leaderId,
|
|
|
boolean approved, String reason) {
|
|
|
@@ -398,6 +453,42 @@ public class PeerReviewService {
|
|
|
return request;
|
|
|
}
|
|
|
|
|
|
+ private PeerReviewRequest requireLeaderRequest(Long periodId, Long subjectUserId, Long leaderId) {
|
|
|
+ PeerReviewRequest request = requestMapper.selectOne(new LambdaQueryWrapper<PeerReviewRequest>()
|
|
|
+ .eq(PeerReviewRequest::getPeriodId, periodId)
|
|
|
+ .eq(PeerReviewRequest::getSubjectUserId, subjectUserId));
|
|
|
+ if (request == null) {
|
|
|
+ throw new BusinessException("该员工尚未发起 360 环评");
|
|
|
+ }
|
|
|
+ if (!Objects.equals(request.getLeaderId(), leaderId)) {
|
|
|
+ throw new BusinessException(403, "仅周期冻结 Leader 可查看环评结果");
|
|
|
+ }
|
|
|
+ return request;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<PeerReviewInvitation> requestInvitations(Long requestId) {
|
|
|
+ return invitationMapper.selectList(new LambdaQueryWrapper<PeerReviewInvitation>()
|
|
|
+ .eq(PeerReviewInvitation::getRequestId, requestId));
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isOutstandingEffectiveInvitation(PeerReviewInvitation invitation) {
|
|
|
+ return !INVITATION_SUBMITTED.equals(invitation.getStatus())
|
|
|
+ && !INVITATION_DECLINED.equals(invitation.getStatus())
|
|
|
+ && !INVITATION_INVALIDATED.equals(invitation.getStatus());
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean canFinalize(Long periodId, List<PeerReviewInvitation> invitations) {
|
|
|
+ long submitted = invitations.stream()
|
|
|
+ .filter(item -> INVITATION_SUBMITTED.equals(item.getStatus())).count();
|
|
|
+ if (submitted < 2) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ AssessmentPeriod period = periodMapper.selectById(periodId);
|
|
|
+ boolean deadlinePassed = period != null && period.getPeerReviewDeadline() != null
|
|
|
+ && LocalDateTime.now().isAfter(period.getPeerReviewDeadline());
|
|
|
+ return deadlinePassed || invitations.stream().noneMatch(this::isOutstandingEffectiveInvitation);
|
|
|
+ }
|
|
|
+
|
|
|
private PeerReviewInvitation requireReviewerInvitation(Long invitationId, Long reviewerId,
|
|
|
boolean allowSubmitted) {
|
|
|
PeerReviewInvitation invitation = invitationMapper.selectById(invitationId);
|
|
|
@@ -513,6 +604,24 @@ public class PeerReviewService {
|
|
|
return summary;
|
|
|
}
|
|
|
|
|
|
+ private PeerReviewDto.LeaderResponse leaderResponse(PeerReviewInvitation invitation, SysUser reviewer) {
|
|
|
+ PeerReviewDto.LeaderResponse response = new PeerReviewDto.LeaderResponse();
|
|
|
+ response.setReviewerId(invitation.getReviewerId());
|
|
|
+ response.setReviewerName(reviewer == null ? null : reviewer.getRealName());
|
|
|
+ response.setGoalContributionScore(invitation.getGoalContributionScore());
|
|
|
+ response.setCollaborationScore(invitation.getCollaborationScore());
|
|
|
+ response.setAccountabilityScore(invitation.getAccountabilityScore());
|
|
|
+ response.setStrengths(invitation.getStrengths());
|
|
|
+ response.setImprovementSuggestion(invitation.getImprovementSuggestion());
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Double average(List<PeerReviewInvitation> invitations,
|
|
|
+ Function<PeerReviewInvitation, Integer> score) {
|
|
|
+ return invitations.stream().map(score).filter(Objects::nonNull)
|
|
|
+ .mapToInt(Integer::intValue).average().orElse(0);
|
|
|
+ }
|
|
|
+
|
|
|
private PeerReviewDto.InvitationSummary reviewerInvitationSummary(PeerReviewInvitation invitation) {
|
|
|
PeerReviewDto.InvitationSummary summary = new PeerReviewDto.InvitationSummary();
|
|
|
summary.setInvitationId(invitation.getId());
|