|
|
@@ -3,6 +3,7 @@ package com.yimeng.okr.service;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.yimeng.okr.dto.PeriodReadinessDto;
|
|
|
import com.yimeng.okr.entity.AssessmentPeriod;
|
|
|
import com.yimeng.okr.entity.KpiConfig;
|
|
|
import com.yimeng.okr.entity.KrKeyResult;
|
|
|
@@ -10,12 +11,14 @@ import com.yimeng.okr.entity.OkrObjective;
|
|
|
import com.yimeng.okr.entity.SysUser;
|
|
|
import com.yimeng.okr.enums.PeriodStatus;
|
|
|
import com.yimeng.okr.enums.PeriodType;
|
|
|
+import com.yimeng.okr.enums.ScoreType;
|
|
|
import com.yimeng.okr.event.NotificationEvent;
|
|
|
import com.yimeng.okr.exception.BusinessException;
|
|
|
import com.yimeng.okr.mapper.AssessmentPeriodMapper;
|
|
|
import com.yimeng.okr.mapper.KpiConfigMapper;
|
|
|
import com.yimeng.okr.mapper.KrKeyResultMapper;
|
|
|
import com.yimeng.okr.mapper.OkrObjectiveMapper;
|
|
|
+import com.yimeng.okr.mapper.PerformanceScoreMapper;
|
|
|
import com.yimeng.okr.mapper.SysUserMapper;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
@@ -38,6 +41,7 @@ public class AssessmentPeriodService {
|
|
|
private final OkrObjectiveMapper objectiveMapper;
|
|
|
private final KrKeyResultMapper krMapper;
|
|
|
private final KpiConfigMapper kpiConfigMapper;
|
|
|
+ private final PerformanceScoreMapper scoreMapper;
|
|
|
private final ApplicationEventPublisher publisher;
|
|
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
|
|
@@ -94,6 +98,8 @@ public class AssessmentPeriodService {
|
|
|
current.getLabel(), targetStatus.getLabel(), hint));
|
|
|
}
|
|
|
|
|
|
+ requireReadyForTransition(period, targetStatus);
|
|
|
+
|
|
|
period.setStatus(targetStatus);
|
|
|
periodMapper.updateById(period);
|
|
|
if (targetStatus == PeriodStatus.ARCHIVED) {
|
|
|
@@ -104,6 +110,71 @@ public class AssessmentPeriodService {
|
|
|
notifyPeriodTransition(period, targetStatus);
|
|
|
}
|
|
|
|
|
|
+ public PeriodReadinessDto checkReadiness(Long id, PeriodStatus targetStatus) {
|
|
|
+ AssessmentPeriod period = getById(id);
|
|
|
+ PeriodReadinessDto readiness = new PeriodReadinessDto();
|
|
|
+ readiness.setPeriodId(period.getId());
|
|
|
+ readiness.setCurrentStatus(period.getStatus());
|
|
|
+ readiness.setTargetStatus(targetStatus);
|
|
|
+
|
|
|
+ List<String> blockers = readiness.getBlockers();
|
|
|
+ if (!isValidTransition(period.getStatus(), targetStatus) && period.getStatus() != targetStatus) {
|
|
|
+ blockers.add("当前状态不能直接转为" + targetStatus.getLabel());
|
|
|
+ } else {
|
|
|
+ collectTransitionBlockers(period.getId(), targetStatus, blockers);
|
|
|
+ }
|
|
|
+ readiness.setReady(blockers.isEmpty());
|
|
|
+ return readiness;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void requireReadyForTransition(AssessmentPeriod period, PeriodStatus targetStatus) {
|
|
|
+ List<String> blockers = new ArrayList<>();
|
|
|
+ collectTransitionBlockers(period.getId(), targetStatus, blockers);
|
|
|
+ if (!blockers.isEmpty()) {
|
|
|
+ throw new BusinessException("周期尚未满足进入【" + targetStatus.getLabel() + "】条件:" + String.join(";", blockers));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void collectTransitionBlockers(Long periodId, PeriodStatus targetStatus, List<String> blockers) {
|
|
|
+ if (targetStatus == PeriodStatus.EXECUTING || targetStatus == PeriodStatus.ASSESSING) {
|
|
|
+ long pendingOkrs = objectiveMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<OkrObjective>()
|
|
|
+ .eq(OkrObjective::getPeriodId, periodId)
|
|
|
+ .in(OkrObjective::getStatus, List.of("DRAFT", "SUBMITTED")));
|
|
|
+ if (pendingOkrs > 0) {
|
|
|
+ blockers.add("存在 " + pendingOkrs + " 条待审核OKR");
|
|
|
+ }
|
|
|
+
|
|
|
+ long unlockedKpis = kpiConfigMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<KpiConfig>()
|
|
|
+ .eq(KpiConfig::getPeriodId, periodId)
|
|
|
+ .isNull(KpiConfig::getLockedAt));
|
|
|
+ if (unlockedKpis > 0) {
|
|
|
+ blockers.add("存在 " + unlockedKpis + " 份未锁定KPI");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (targetStatus == PeriodStatus.ARCHIVED) {
|
|
|
+ long finalScores = scoreMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<com.yimeng.okr.entity.PerformanceScore>()
|
|
|
+ .eq(com.yimeng.okr.entity.PerformanceScore::getPeriodId, periodId)
|
|
|
+ .eq(com.yimeng.okr.entity.PerformanceScore::getType, ScoreType.FINAL));
|
|
|
+ if (finalScores == 0) {
|
|
|
+ blockers.add("暂无最终评分");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ long unconfirmedScores = scoreMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<com.yimeng.okr.entity.PerformanceScore>()
|
|
|
+ .eq(com.yimeng.okr.entity.PerformanceScore::getPeriodId, periodId)
|
|
|
+ .eq(com.yimeng.okr.entity.PerformanceScore::getType, ScoreType.FINAL)
|
|
|
+ .isNull(com.yimeng.okr.entity.PerformanceScore::getConfirmedAt));
|
|
|
+ if (unconfirmedScores > 0) {
|
|
|
+ blockers.add("存在 " + unconfirmedScores + " 条未确认最终评分");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private void notifyPeriodTransition(AssessmentPeriod period, PeriodStatus target) {
|
|
|
String title;
|
|
|
String content;
|