diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java index d5760dc7..c1d1973a 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java @@ -231,6 +231,7 @@ public class AihrSopSeedService { throw new ServiceException("缺少片段ID"); } validateFeedbackFragmentOwnership(fragmentIds); + Long reviewId = normalizeFeedbackReviewId(request.reviewId()); ensureAnswerFeedbackTable(); String reviewStatus = "down".equals(verdict) ? "待复核" : "已复核"; Timestamp now = Timestamp.valueOf(java.time.LocalDateTime.now()); @@ -258,7 +259,7 @@ public class AihrSopSeedService { firstNonBlank(request.category(), "sop"), firstNonBlank(request.position(), "生活顾问"), normalizeSearchSource(request.source()), - request.reviewId(), + reviewId, reviewStatus, now, now @@ -2109,6 +2110,19 @@ public class AihrSopSeedService { } } + private Long normalizeFeedbackReviewId(Long reviewId) { + if (reviewId == null || reviewId <= 0) { + return null; + } + ensureSopReviewTable(); + Integer count = jdbcTemplate.queryForObject(""" + SELECT COUNT(*) + FROM aihr_sop_answer_review + WHERE tenant_id = ? AND id = ? + """, Integer.class, tenantId(), reviewId); + return count != null && count > 0 ? reviewId : null; + } + private static String formatTimestamp(Timestamp timestamp) { return timestamp == null ? "" : timestamp.toLocalDateTime().format(TIME_FORMATTER); } diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrSopSeedServiceTest.java b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrSopSeedServiceTest.java index b4347662..235f6ad4 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrSopSeedServiceTest.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrSopSeedServiceTest.java @@ -142,6 +142,19 @@ public class AihrSopSeedServiceTest { assertTrue(code.contains("WHERE tenant_id = ? AND id IN (%s)")); } + @Test + @Tag("dev") + public void answerFeedbackNormalizesReviewOwnership() throws Exception { + Path source = Path.of("src/main/java/org/dromara/aihr/service/AihrSopSeedService.java"); + if (!Files.exists(source)) { + source = Path.of("ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java"); + } + String code = Files.readString(source); + + assertTrue(code.contains("Long reviewId = normalizeFeedbackReviewId(request.reviewId())")); + assertTrue(code.contains("WHERE tenant_id = ? AND id = ?")); + } + private static AihrSopSeedService.KnowledgeHit hit(Long fragmentId, String title) { return new AihrSopSeedService.KnowledgeHit(fragmentId, title, "sop", "", "doc-" + fragmentId, "片段内容", 1, 1.0); } diff --git a/docs/BRD_IMPLEMENTATION_AUDIT.md b/docs/BRD_IMPLEMENTATION_AUDIT.md index 52899b0a..58c6c5ae 100644 --- a/docs/BRD_IMPLEMENTATION_AUDIT.md +++ b/docs/BRD_IMPLEMENTATION_AUDIT.md @@ -181,3 +181,4 @@ - 2026-07-14 BRD SOP 审核留痕复核:SOP 问答评审接口此前接受请求体中的 `reviewer` 并直接入库,登录用户可伪造审核人;现由控制器从当前登录用户传入服务端操作人,请求体仅保留评审结论和备注,保留旧服务调用的默认“人工评审”兼容。 - 2026-07-14 BRD 知识答案反馈复核:原先只更新 `review_status`,没有复核人和复核时间;现由服务端记录登录操作人到 `reviewer/reviewed_time`,管理端待复核表同步展示留痕。 - 2026-07-14 BRD 知识反馈归属复核:答案反馈原先信任客户端传入的片段 ID,未校验知识片段是否属于当前租户;现入库前按当前租户校验片段归属并限制单次最多 20 个片段。 +- 2026-07-14 BRD 知识反馈关联复核:答案反馈原先直接接受客户端传入的 `reviewId`;现仅保留当前租户存在的 SOP 评审关联,非法或跨租户 ID 归一为空。 diff --git a/scripts/demo-check.sh b/scripts/demo-check.sh index 43c29444..70149223 100755 --- a/scripts/demo-check.sh +++ b/scripts/demo-check.sh @@ -496,6 +496,7 @@ contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "/embeddings" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "aihr_knowledge_gap" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "validateFeedbackFragmentOwnership" +contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "normalizeFeedbackReviewId" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrVideoService.java "处理输出已隐藏" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrUploadQueueService.java "资料加工失败,请重试或联系管理员" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "目录导入失败,请重试或联系管理员"