Browse Source

feat: wire notification events into business services

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
wangkangyjy 2 months ago
parent
commit
aa9b7e289a

+ 41 - 0
backend/src/main/java/com/yimeng/okr/schedule/PeriodScheduler.java

@@ -1,21 +1,62 @@
 package com.yimeng.okr.schedule;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.yimeng.okr.entity.AssessmentPeriod;
+import com.yimeng.okr.entity.SysUser;
+import com.yimeng.okr.enums.PeriodStatus;
+import com.yimeng.okr.event.NotificationEvent;
+import com.yimeng.okr.mapper.AssessmentPeriodMapper;
+import com.yimeng.okr.mapper.SysUserMapper;
 import com.yimeng.okr.service.AssessmentPeriodService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+
 @Slf4j
 @Component
 @RequiredArgsConstructor
 public class PeriodScheduler {
 
     private final AssessmentPeriodService periodService;
+    private final AssessmentPeriodMapper periodMapper;
+    private final SysUserMapper userMapper;
+    private final ApplicationEventPublisher publisher;
 
     @Scheduled(cron = "0 0 2 * * ?")
     public void autoAdvancePeriods() {
         log.info("Running period auto-advance check...");
         periodService.checkAndAutoAdvance();
     }
+
+    @Scheduled(cron = "0 0 10 * * ?")
+    public void checkPeriodDeadlines() {
+        log.info("Checking period deadlines...");
+        List<AssessmentPeriod> periods = periodMapper.selectList(
+                new LambdaQueryWrapper<AssessmentPeriod>()
+                        .eq(AssessmentPeriod::getStatus, PeriodStatus.EXECUTING));
+        LocalDate today = LocalDate.now();
+        for (AssessmentPeriod p : periods) {
+            try {
+                LocalDate end = LocalDate.parse(p.getEndDate().substring(0, 10));
+                if (ChronoUnit.DAYS.between(today, end) == 3) {
+                    List<SysUser> users = userMapper.selectList(null);
+                    for (SysUser u : users) {
+                        publisher.publishEvent(new NotificationEvent(
+                                this, u.getId(), "PERIOD_DEADLINE",
+                                "周期即将结束", "考核周期「" + p.getName() + "」将于3天后结束",
+                                "period", p.getId()));
+                    }
+                }
+            } catch (Exception e) {
+                log.warn("Failed to check deadline for period {}: {}", p.getId(), e.getMessage());
+            }
+        }
+    }
 }

+ 11 - 0
backend/src/main/java/com/yimeng/okr/service/FeedbackService.java

@@ -6,10 +6,12 @@ import com.yimeng.okr.dto.ScoringDto;
 import com.yimeng.okr.entity.PerformanceFeedback;
 import com.yimeng.okr.entity.PerformanceScore;
 import com.yimeng.okr.enums.ScoreType;
+import com.yimeng.okr.event.NotificationEvent;
 import com.yimeng.okr.exception.BusinessException;
 import com.yimeng.okr.mapper.PerformanceFeedbackMapper;
 import com.yimeng.okr.mapper.PerformanceScoreMapper;
 import lombok.RequiredArgsConstructor;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -23,6 +25,7 @@ public class FeedbackService {
     private final PerformanceScoreMapper scoreMapper;
     private final OkrService okrService;
     private final PerformanceService performanceService;
+    private final ApplicationEventPublisher publisher;
 
     @Transactional
     public PerformanceFeedback saveInterviewNotes(Long periodId, Long userId, Long superiorId,
@@ -38,6 +41,10 @@ public class FeedbackService {
             existing.setIssues(issues);
             existing.setImprovements(improvements);
             feedbackMapper.updateById(existing);
+            publisher.publishEvent(new NotificationEvent(
+                    this, userId, "FEEDBACK_NOTIFY",
+                    "绩效面谈通知", "你的上级发起了绩效面谈,请查看并回复",
+                    "feedback", existing.getId()));
             return existing;
         }
 
@@ -50,6 +57,10 @@ public class FeedbackService {
         fb.setIssues(issues);
         fb.setImprovements(improvements);
         feedbackMapper.insert(fb);
+        publisher.publishEvent(new NotificationEvent(
+                this, userId, "FEEDBACK_NOTIFY",
+                "绩效面谈通知", "你的上级发起了绩效面谈,请查看并回复",
+                "feedback", fb.getId()));
         return fb;
     }
 

+ 17 - 0
backend/src/main/java/com/yimeng/okr/service/OkrService.java

@@ -6,9 +6,11 @@ import com.yimeng.okr.dto.OkrDetailDto.KrWithProgress;
 import com.yimeng.okr.entity.*;
 import com.yimeng.okr.enums.KrStatus;
 import com.yimeng.okr.enums.PeriodStatus;
+import com.yimeng.okr.event.NotificationEvent;
 import com.yimeng.okr.exception.BusinessException;
 import com.yimeng.okr.mapper.*;
 import lombok.RequiredArgsConstructor;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -26,6 +28,7 @@ public class OkrService {
     private final AssessmentPeriodMapper periodMapper;
     private final SysUserMapper userMapper;
     private final SysDepartmentMapper departmentMapper;
+    private final ApplicationEventPublisher publisher;
 
     @Transactional
     public OkrObjective submitOkr(Long periodId, Long userId, String title,
@@ -114,6 +117,14 @@ public class OkrService {
             kr.setStatus(KrStatus.NOT_STARTED);
             krMapper.insert(kr);
         }
+
+        if (!isTopLevel && user.getSuperiorId() != null) {
+            publisher.publishEvent(new NotificationEvent(
+                    this, user.getSuperiorId(), "OKR_REVIEW",
+                    "新的OKR待审核", user.getRealName() + " 提交了OKR,请审核",
+                    "objective", obj.getId()));
+        }
+
         return obj;
     }
 
@@ -185,6 +196,12 @@ public class OkrService {
         obj.setReviewedBy(reviewerId);
         obj.setReviewedAt(LocalDateTime.now());
         objectiveMapper.updateById(obj);
+
+        String result = approved ? "已通过" : "已驳回";
+        publisher.publishEvent(new NotificationEvent(
+                this, obj.getUserId(), "OKR_REVIEWED",
+                "OKR审核" + result, "你的OKR已被" + result + (remark != null && !remark.isEmpty() ? "。备注:" + remark : ""),
+                "objective", obj.getId()));
     }
 
     @Transactional

+ 16 - 0
backend/src/main/java/com/yimeng/okr/service/PerformanceService.java

@@ -7,11 +7,13 @@ import com.yimeng.okr.dto.UserInfo;
 import com.yimeng.okr.entity.*;
 import com.yimeng.okr.enums.Grade;
 import com.yimeng.okr.enums.ScoreType;
+import com.yimeng.okr.event.NotificationEvent;
 import com.yimeng.okr.exception.BusinessException;
 import com.yimeng.okr.mapper.*;
 import java.time.LocalDateTime;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -27,6 +29,7 @@ public class PerformanceService {
     private final AssessmentPeriodMapper periodMapper;
     private final SysUserMapper userMapper;
     private final OkrService okrService;
+    private final ApplicationEventPublisher publisher;
 
     // Self evaluation
 
@@ -108,6 +111,11 @@ public class PerformanceService {
         finalScore.setCommentsJson(commentsJson);
         scoreMapper.insert(finalScore);
 
+        publisher.publishEvent(new NotificationEvent(
+                this, userId, "SCORE_CONFIRM",
+                "绩效评分已完成", "你的上级已完成本周期绩效评分,请确认",
+                "score", score.getId()));
+
         return score;
     }
 
@@ -168,6 +176,14 @@ public class PerformanceService {
         if (score.getConfirmedAt() != null) throw new BusinessException("已确认过,无需重复确认");
         score.setConfirmedAt(LocalDateTime.now());
         scoreMapper.updateById(score);
+
+        SysUser user = userMapper.selectById(userId);
+        if (user != null && user.getSuperiorId() != null) {
+            publisher.publishEvent(new NotificationEvent(
+                    this, user.getSuperiorId(), "SCORE_CONFIRMED",
+                    "评分已确认", user.getRealName() + " 已确认绩效评分",
+                    "score", score.getId()));
+        }
     }
 
     // Helpers