Forráskód Böngészése

docs(环评): 按 Ponytail 精简实现计划

wangkangyjy 2 napja
szülő
commit
5d6b3bb51b
1 módosított fájl, 37 hozzáadás és 54 törlés
  1. 37 54
      docs/superpowers/plans/2026-07-16-360-peer-review.md

+ 37 - 54
docs/superpowers/plans/2026-07-16-360-peer-review.md

@@ -4,7 +4,7 @@
 
 **目标:** 在现有 `ASSESSING` 绩效流程中加入员工邀请、Leader 审核、同事评价和两份有效反馈门禁,同时保证原始环评内容只对周期快照 Leader 可见。
 
-**架构:** 新建独立 `PeerReviewService` 深模块管理请求、邀请、响应、角色 DTO 和门禁;现有 `PerformanceService` 只调用 `requireCanFinalize(periodId, userId, leaderId)`。前端复用“我的绩效”“绩效考评”“周期管理”和通知中心,仅新增评价人填写路由,不新增侧边栏模块
+**架构:** 新建独立 `PeerReviewService` 深模块管理请求、邀请/评价、角色 DTO 和门禁;现有 `PerformanceService` 只调用 `requireCanFinalize(periodId, userId, leaderId)`。评价草稿与正式内容直接保存在邀请表,沿用项目字符串状态模式;前端复用“我的绩效”“绩效考评”“周期管理”和通知中心,仅新增评价人填写路由。
 
 **技术栈:** Java 17、Spring Boot 3.2、MyBatis-Plus、SQLite、JUnit 5、Mockito、Vue 3、Element Plus、Vite、Vitest。
 
@@ -14,20 +14,11 @@
 
 ### 后端新增文件
 
-- `backend/src/main/java/com/emoon/okr/enums/PeerReviewRequestStatus.java`:请求流程状态。
-- `backend/src/main/java/com/emoon/okr/enums/PeerReviewInvitationStatus.java`:邀请接受、拒绝、失效和提交状态。
-- `backend/src/main/java/com/emoon/okr/enums/PeerReviewResponseStatus.java`:草稿与正式提交状态。
 - `backend/src/main/java/com/emoon/okr/entity/PeerReviewRequest.java`:每位员工每周期唯一的 360 请求。
-- `backend/src/main/java/com/emoon/okr/entity/PeerReviewInvitation.java`:累计邀请记录和拒绝原因。
-- `backend/src/main/java/com/emoon/okr/entity/PeerReviewResponse.java`:三项评分与两项简评。
+- `backend/src/main/java/com/emoon/okr/entity/PeerReviewInvitation.java`:累计邀请、拒绝原因、评价草稿和正式内容。
 - `backend/src/main/java/com/emoon/okr/mapper/PeerReviewRequestMapper.java`:请求持久化。
-- `backend/src/main/java/com/emoon/okr/mapper/PeerReviewInvitationMapper.java`:邀请持久化。
-- `backend/src/main/java/com/emoon/okr/mapper/PeerReviewResponseMapper.java`:响应持久化。
-- `backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java`:员工、评价人、Leader、管理员的最小响应 DTO。
-- `backend/src/main/java/com/emoon/okr/dto/request/SubmitPeerReviewListRequest.java`:员工提交候选人 ID。
-- `backend/src/main/java/com/emoon/okr/dto/request/ReviewPeerReviewListRequest.java`:Leader 批准或驳回整份名单。
-- `backend/src/main/java/com/emoon/okr/dto/request/DeclinePeerReviewInvitationRequest.java`:拒绝原因。
-- `backend/src/main/java/com/emoon/okr/dto/request/SavePeerReviewResponseRequest.java`:草稿/提交字段。
+- `backend/src/main/java/com/emoon/okr/mapper/PeerReviewInvitationMapper.java`:邀请与评价持久化。
+- `backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java`:四类角色的最小响应 DTO 和四个嵌套请求类型。
 - `backend/src/main/java/com/emoon/okr/controller/PeerReviewController.java`:四类角色 API。
 - `backend/src/main/java/com/emoon/okr/service/PeerReviewService.java`:资格、状态、隐私、聚合与终评门禁。
 - `backend/src/main/java/com/emoon/okr/schedule/PeerReviewReminderScheduler.java`:截止前 24 小时去重提醒。
@@ -86,7 +77,6 @@ void migrationAddsPeerReviewTablesAndDeadlines() {
     assertColumnExists(jdbcTemplate, "assessment_period", "peer_review_deadline");
     assertTableExists(jdbcTemplate, "peer_review_request");
     assertTableExists(jdbcTemplate, "peer_review_invitation");
-    assertTableExists(jdbcTemplate, "peer_review_response");
     assertEquals(1, jdbcTemplate.queryForObject(
             "SELECT COUNT(*) FROM schema_migration WHERE version = 13", Integer.class));
 }
@@ -136,47 +126,44 @@ CREATE TABLE IF NOT EXISTS peer_review_invitation (
     relation_type_snapshot VARCHAR(24) NOT NULL,
     status VARCHAR(24) NOT NULL DEFAULT 'PROPOSED',
     decline_reason TEXT,
-    responded_at TEXT,
-    created_at TEXT NOT NULL,
-    updated_at TEXT NOT NULL,
-    deleted INTEGER NOT NULL DEFAULT 0
-);
-CREATE UNIQUE INDEX IF NOT EXISTS uk_peer_review_invitation_reviewer
-    ON peer_review_invitation(request_id, reviewer_id);
-CREATE INDEX IF NOT EXISTS idx_peer_review_invitation_reviewer
-    ON peer_review_invitation(reviewer_id, status);
-
-CREATE TABLE IF NOT EXISTS peer_review_response (
-    id INTEGER PRIMARY KEY AUTOINCREMENT,
-    invitation_id INTEGER NOT NULL,
     goal_contribution_score INTEGER,
     collaboration_score INTEGER,
     accountability_score INTEGER,
     strengths TEXT,
     improvement_suggestion TEXT,
-    status VARCHAR(16) NOT NULL DEFAULT 'DRAFT',
     submitted_at TEXT,
+    responded_at TEXT,
     created_at TEXT NOT NULL,
     updated_at TEXT NOT NULL,
     deleted INTEGER NOT NULL DEFAULT 0
 );
-CREATE UNIQUE INDEX IF NOT EXISTS uk_peer_review_response_invitation
-    ON peer_review_response(invitation_id);
+CREATE UNIQUE INDEX IF NOT EXISTS uk_peer_review_invitation_reviewer
+    ON peer_review_invitation(request_id, reviewer_id);
+CREATE INDEX IF NOT EXISTS idx_peer_review_invitation_reviewer
+    ON peer_review_invitation(reviewer_id, status);
+
 ```
 
 迁移必须分别调用 `context.addColumnIfMissing("assessment_period", "peer_invitation_deadline", "TEXT")` 和 `context.addColumnIfMissing("assessment_period", "peer_review_deadline", "TEXT")`,再用 `context.execute` 建表和索引,保证旧数据库可升级。
 
-- [ ] **步骤 4:创建类型安全的 entity 与 mapper**
+- [ ] **步骤 4:创建最小 entity 与 mapper**
 
-枚举值固定为
+状态沿用现有服务字符串常量,不新增 enum
 
 ```java
-public enum PeerReviewRequestStatus { DRAFT, PENDING_APPROVAL, REJECTED, IN_PROGRESS, READY_FOR_FINAL, ARCHIVED }
-public enum PeerReviewInvitationStatus { PROPOSED, PENDING, ACCEPTED, DECLINED, SUBMITTED, INVALIDATED }
-public enum PeerReviewResponseStatus { DRAFT, SUBMITTED }
+private static final String REQUEST_PENDING_APPROVAL = "PENDING_APPROVAL";
+private static final String REQUEST_REJECTED = "REJECTED";
+private static final String REQUEST_IN_PROGRESS = "IN_PROGRESS";
+private static final String REQUEST_READY_FOR_FINAL = "READY_FOR_FINAL";
+private static final String INVITATION_PROPOSED = "PROPOSED";
+private static final String INVITATION_PENDING = "PENDING";
+private static final String INVITATION_ACCEPTED = "ACCEPTED";
+private static final String INVITATION_DECLINED = "DECLINED";
+private static final String INVITATION_SUBMITTED = "SUBMITTED";
+private static final String INVITATION_INVALIDATED = "INVALIDATED";
 ```
 
-三个实体使用 `@TableName`、`@TableId(type = IdType.AUTO)`、`@TableLogic` 和现有时间自动填充模式;`AssessmentPeriod` 增加:
+个实体使用 `@TableName`、`@TableId(type = IdType.AUTO)`、`@TableLogic` 和现有时间自动填充模式;`AssessmentPeriod` 增加:
 
 ```java
 private LocalDateTime peerInvitationDeadline;
@@ -198,7 +185,7 @@ mvn test
 - [ ] **步骤 6:Commit**
 
 ```bash
-git add backend/src/main/resources/schema.sql backend/src/main/java/com/emoon/okr/config/DatabaseMigrationRunner.java backend/src/test/java/com/emoon/okr/config/DatabaseMigrationRunnerTest.java backend/src/main/java/com/emoon/okr/entity/AssessmentPeriod.java backend/src/main/java/com/emoon/okr/enums/PeerReviewRequestStatus.java backend/src/main/java/com/emoon/okr/enums/PeerReviewInvitationStatus.java backend/src/main/java/com/emoon/okr/enums/PeerReviewResponseStatus.java backend/src/main/java/com/emoon/okr/entity/PeerReviewRequest.java backend/src/main/java/com/emoon/okr/entity/PeerReviewInvitation.java backend/src/main/java/com/emoon/okr/entity/PeerReviewResponse.java backend/src/main/java/com/emoon/okr/mapper/PeerReviewRequestMapper.java backend/src/main/java/com/emoon/okr/mapper/PeerReviewInvitationMapper.java backend/src/main/java/com/emoon/okr/mapper/PeerReviewResponseMapper.java
+git add backend/src/main/resources/schema.sql backend/src/main/java/com/emoon/okr/config/DatabaseMigrationRunner.java backend/src/test/java/com/emoon/okr/config/DatabaseMigrationRunnerTest.java backend/src/main/java/com/emoon/okr/entity/AssessmentPeriod.java backend/src/main/java/com/emoon/okr/entity/PeerReviewRequest.java backend/src/main/java/com/emoon/okr/entity/PeerReviewInvitation.java backend/src/main/java/com/emoon/okr/mapper/PeerReviewRequestMapper.java backend/src/main/java/com/emoon/okr/mapper/PeerReviewInvitationMapper.java
 git commit -m "feat(环评): 添加 360 环评持久化模型"
 ```
 
@@ -206,7 +193,6 @@ git commit -m "feat(环评): 添加 360 环评持久化模型"
 
 **文件:**
 - 创建:`backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java`
-- 创建:`backend/src/main/java/com/emoon/okr/dto/request/SubmitPeerReviewListRequest.java`
 - 创建:`backend/src/main/java/com/emoon/okr/service/PeerReviewService.java`
 - 创建:`backend/src/test/java/com/emoon/okr/service/PeerReviewServiceTest.java`
 
@@ -258,11 +244,11 @@ mvn -Dtest=PeerReviewServiceTest test
 
 - [ ] **步骤 3:定义最小 DTO 与请求**
 
-`SubmitPeerReviewListRequest`:
+`PeerReviewDto.SubmitListRequest`:
 
 ```java
 @Data
-public class SubmitPeerReviewListRequest {
+public static class SubmitListRequest {
     @NotNull
     private Long periodId;
 
@@ -298,15 +284,13 @@ mvn -Dtest=PeerReviewServiceTest test
 - [ ] **步骤 6:Commit**
 
 ```bash
-git add backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java backend/src/main/java/com/emoon/okr/dto/request/SubmitPeerReviewListRequest.java backend/src/main/java/com/emoon/okr/service/PeerReviewService.java backend/src/test/java/com/emoon/okr/service/PeerReviewServiceTest.java
+git add backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java backend/src/main/java/com/emoon/okr/service/PeerReviewService.java backend/src/test/java/com/emoon/okr/service/PeerReviewServiceTest.java
 git commit -m "feat(环评): 实现评价人资格与邀请名单"
 ```
 
 ## 任务 3:Leader 审核、邀请接受与拒绝补邀
 
 **文件:**
-- 创建:`backend/src/main/java/com/emoon/okr/dto/request/ReviewPeerReviewListRequest.java`
-- 创建:`backend/src/main/java/com/emoon/okr/dto/request/DeclinePeerReviewInvitationRequest.java`
 - 创建:`backend/src/main/java/com/emoon/okr/controller/PeerReviewController.java`
 - 修改:`backend/src/main/java/com/emoon/okr/service/PeerReviewService.java`
 - 修改:`backend/src/test/java/com/emoon/okr/service/PeerReviewServiceTest.java`
@@ -323,7 +307,7 @@ void approveReviewerListRequiresFrozenLeaderAndCreatesPendingInvitations() {
     service.reviewList(20L, 9L, true, "");
 
     verify(invitationMapper, times(3)).updateById(argThat(invitation ->
-            invitation.getStatus() == PeerReviewInvitationStatus.PENDING));
+            "PENDING".equals(invitation.getStatus())));
 }
 
 @Test
@@ -334,7 +318,7 @@ void declineRequiresReasonAndLeavesReviewerIdentityVisibleOnlyToSubjectAndLeader
     service.declineInvitation(30L, 3L, "合作接触不足");
 
     verify(invitationMapper).updateById(argThat(invitation ->
-            invitation.getStatus() == PeerReviewInvitationStatus.DECLINED
+            "DECLINED".equals(invitation.getStatus())
                     && "合作接触不足".equals(invitation.getDeclineReason())));
 }
 ```
@@ -382,7 +366,7 @@ public class PeerReviewController {
 
     @PutMapping("/leader/requests/{id}/review")
     public ApiResult<PeerReviewDto.LeaderSummary> review(@PathVariable Long id,
-            @Valid @RequestBody ReviewPeerReviewListRequest request) {
+            @Valid @RequestBody PeerReviewDto.ReviewListRequest request) {
         return ApiResult.success(peerReviewService.reviewList(
                 id, SecurityUtils.getCurrentUserId(), request.getApproved(), request.getReason()));
     }
@@ -404,14 +388,13 @@ mvn -DskipTests package
 - [ ] **步骤 6:Commit**
 
 ```bash
-git add backend/src/main/java/com/emoon/okr/dto/request/ReviewPeerReviewListRequest.java backend/src/main/java/com/emoon/okr/dto/request/DeclinePeerReviewInvitationRequest.java backend/src/main/java/com/emoon/okr/controller/PeerReviewController.java backend/src/main/java/com/emoon/okr/service/PeerReviewService.java backend/src/test/java/com/emoon/okr/service/PeerReviewServiceTest.java
+git add backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java backend/src/main/java/com/emoon/okr/controller/PeerReviewController.java backend/src/main/java/com/emoon/okr/service/PeerReviewService.java backend/src/test/java/com/emoon/okr/service/PeerReviewServiceTest.java
 git commit -m "feat(环评): 实现名单审核与邀请响应"
 ```
 
 ## 任务 4:限定材料、草稿与正式评价
 
 **文件:**
-- 创建:`backend/src/main/java/com/emoon/okr/dto/request/SavePeerReviewResponseRequest.java`
 - 修改:`backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java`
 - 修改:`backend/src/main/java/com/emoon/okr/service/PeerReviewService.java`
 - 修改:`backend/src/main/java/com/emoon/okr/controller/PeerReviewController.java`
@@ -439,7 +422,7 @@ void reviewerMaterialContainsOkrAndDescriptionsButNoPerformanceScores() {
 @Test
 void submitResponseRequiresThreeScoresAndTwoTenCharacterComments() {
     givenAcceptedInvitation(30L, 20L, 3L);
-    SavePeerReviewResponseRequest request = validResponse();
+    PeerReviewDto.SaveResponseRequest request = validResponse();
     request.setStrengths("太短");
 
     BusinessException error = assertThrows(BusinessException.class,
@@ -461,11 +444,11 @@ mvn -Dtest=PeerReviewServiceTest test
 
 - [ ] **步骤 3:实现请求验证和最小 Reviewer DTO**
 
-`SavePeerReviewResponseRequest`:
+`PeerReviewDto.SaveResponseRequest`:
 
 ```java
 @Data
-public class SavePeerReviewResponseRequest {
+public static class SaveResponseRequest {
     @Min(1) @Max(5)
     private Integer goalContributionScore;
     @Min(1) @Max(5)
@@ -488,10 +471,10 @@ public class SavePeerReviewResponseRequest {
 ```java
 public PeerReviewDto.ReviewerWorkspace getReviewerWorkspace(Long invitationId, Long reviewerId)
 public PeerReviewDto.InvitationSummary saveResponse(Long invitationId, Long reviewerId,
-        SavePeerReviewResponseRequest body, boolean submit)
+        PeerReviewDto.SaveResponseRequest body, boolean submit)
 ```
 
-材料 DTO 仅复制 `OkrDetailDto` 的目标、KR 标题、当前值、目标值、状态和进度,以及 SELF `PerformanceScoreItem` 的 `name`、`description`;禁止复用 `ScoringDto` 或实体作为响应。正式提交后 `response.status=SUBMITTED`、`invitation.status=SUBMITTED`,再次提交返回业务错误。
+材料 DTO 仅复制 `OkrDetailDto` 的目标、KR 标题、当前值、目标值、状态和进度,以及 SELF `PerformanceScoreItem` 的 `name`、`description`;禁止复用 `ScoringDto` 或实体作为响应。草稿直接更新邀请表的可空评价字段;正式提交后 `invitation.status=SUBMITTED` 并写入 `submittedAt`,再次提交返回业务错误。
 
 - [ ] **步骤 5:运行测试**
 
@@ -505,7 +488,7 @@ mvn -Dtest=PeerReviewServiceTest test
 - [ ] **步骤 6:Commit**
 
 ```bash
-git add backend/src/main/java/com/emoon/okr/dto/request/SavePeerReviewResponseRequest.java backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java backend/src/main/java/com/emoon/okr/service/PeerReviewService.java backend/src/main/java/com/emoon/okr/controller/PeerReviewController.java backend/src/test/java/com/emoon/okr/service/PeerReviewServiceTest.java
+git add backend/src/main/java/com/emoon/okr/dto/PeerReviewDto.java backend/src/main/java/com/emoon/okr/service/PeerReviewService.java backend/src/main/java/com/emoon/okr/controller/PeerReviewController.java backend/src/test/java/com/emoon/okr/service/PeerReviewServiceTest.java
 git commit -m "feat(环评): 实现同事评价与隐私材料"
 ```