fix(aihr): audit knowledge feedback reviews

This commit is contained in:
2026-07-14 10:52:46 +08:00
parent 19c59a07ab
commit 816bf0e6f5
9 changed files with 44 additions and 7 deletions
@@ -106,7 +106,9 @@ public class AihrSopController {
@SaCheckRole(value = {TenantConstants.SUPER_ADMIN_ROLE_KEY, HR_OPERATOR_ROLE}, mode = SaMode.OR)
@PostMapping("/answer-feedback/{id}/review")
public R<AnswerFeedbackReviewResponse> reviewAnswerFeedback(@PathVariable Long id) {
AnswerFeedbackReviewResponse response = sopSeedService.reviewAnswerFeedback(id);
LoginUser loginUser = LoginHelper.getLoginUser();
String operator = loginUser == null ? "unknown" : loginUser.getUsername();
AnswerFeedbackReviewResponse response = sopSeedService.reviewAnswerFeedback(id, operator);
return response == null ? R.fail("答案反馈记录不存在") : R.ok(response);
}
@@ -44,10 +44,10 @@ public final class AihrSopDto {
public record AnswerFeedbackResponse(Integer saved, String verdict, String reviewStatus) {
}
public record AnswerFeedbackItemResponse(Long id, String queryText, String queryNorm, Long fragmentId, String verdict, String category, String position, String source, Long reviewId, String reviewStatus, String createTime, String updateTime) {
public record AnswerFeedbackItemResponse(Long id, String queryText, String queryNorm, Long fragmentId, String verdict, String category, String position, String source, Long reviewId, String reviewStatus, String reviewer, String reviewedTime, String createTime, String updateTime) {
}
public record AnswerFeedbackReviewResponse(Long id, String reviewStatus) {
public record AnswerFeedbackReviewResponse(Long id, String reviewStatus, String reviewer, String reviewedTime) {
}
public record SummaryCardResponse(String title, List<CardStep> steps, List<CardObjection> objections, List<String> scripts, List<String> reminders) {
@@ -271,7 +271,7 @@ public class AihrSopSeedService {
String reviewStatus = firstNonBlank(status, "待复核");
int limitValue = Math.max(1, Math.min(limit, 200));
return jdbcTemplate.query("""
SELECT id, query_text, query_norm, fragment_id, verdict, category, position, source, review_id, review_status, create_time, update_time
SELECT id, query_text, query_norm, fragment_id, verdict, category, position, source, review_id, review_status, reviewer, reviewed_time, create_time, update_time
FROM aihr_knowledge_answer_feedback
WHERE tenant_id = ?
AND (? = '' OR review_status = ?)
@@ -288,26 +288,35 @@ public class AihrSopSeedService {
rs.getString("source"),
rs.getObject("review_id") == null ? null : rs.getLong("review_id"),
rs.getString("review_status"),
rs.getString("reviewer"),
formatTimestamp(rs.getTimestamp("reviewed_time")),
formatTimestamp(rs.getTimestamp("create_time")),
formatTimestamp(rs.getTimestamp("update_time"))
), tenantId(), reviewStatus, reviewStatus, limitValue);
}
public AnswerFeedbackReviewResponse reviewAnswerFeedback(Long id) {
return reviewAnswerFeedback(id, null);
}
public AnswerFeedbackReviewResponse reviewAnswerFeedback(Long id, String operator) {
if (id == null) {
return null;
}
ensureAnswerFeedbackTable();
Timestamp reviewedTime = Timestamp.valueOf(java.time.LocalDateTime.now());
int updated = jdbcTemplate.update("""
UPDATE aihr_knowledge_answer_feedback
SET review_status = '已复核', update_time = ?
SET review_status = '已复核', reviewer = ?, reviewed_time = ?, update_time = ?
WHERE tenant_id = ? AND id = ?
""",
Timestamp.valueOf(java.time.LocalDateTime.now()),
firstNonBlank(operator, "人工复核"),
reviewedTime,
reviewedTime,
tenantId(),
id
);
return updated == 0 ? null : new AnswerFeedbackReviewResponse(id, "已复核");
return updated == 0 ? null : new AnswerFeedbackReviewResponse(id, "已复核", firstNonBlank(operator, "人工复核"), formatTimestamp(reviewedTime));
}
public SummaryCardResponse summaryCard(SummaryCardRequest request) {
@@ -1990,6 +1999,8 @@ public class AihrSopSeedService {
`source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源',
`review_id` bigint DEFAULT NULL COMMENT '对应SOP问答评审ID',
`review_status` varchar(30) DEFAULT '待复核' COMMENT '复核状态',
`reviewer` varchar(100) DEFAULT NULL COMMENT '复核人',
`reviewed_time` datetime DEFAULT NULL COMMENT '复核时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
@@ -1999,6 +2010,8 @@ public class AihrSopSeedService {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI HR 知识答案反馈';
""");
ensureColumn("aihr_knowledge_answer_feedback", "review_id", "ALTER TABLE aihr_knowledge_answer_feedback ADD COLUMN `review_id` bigint DEFAULT NULL COMMENT '对应SOP问答评审ID' AFTER `source`");
ensureColumn("aihr_knowledge_answer_feedback", "reviewer", "ALTER TABLE aihr_knowledge_answer_feedback ADD COLUMN `reviewer` varchar(100) DEFAULT NULL COMMENT '复核人' AFTER `review_status`");
ensureColumn("aihr_knowledge_answer_feedback", "reviewed_time", "ALTER TABLE aihr_knowledge_answer_feedback ADD COLUMN `reviewed_time` datetime DEFAULT NULL COMMENT '复核时间' AFTER `reviewer`");
answerFeedbackTableReady = true;
}
}
@@ -117,6 +117,18 @@ public class AihrSopSeedServiceTest {
assertTrue(code.contains("reviewSearch(id, request, operator)"));
}
@Test
@Tag("dev")
public void answerFeedbackReviewUsesAuthenticatedOperatorForAuditTrail() throws Exception {
Path source = Path.of("src/main/java/org/dromara/aihr/controller/AihrSopController.java");
if (!Files.exists(source)) {
source = Path.of("ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java");
}
String code = Files.readString(source);
assertTrue(code.contains("reviewAnswerFeedback(id, operator)"));
}
private static AihrSopSeedService.KnowledgeHit hit(Long fragmentId, String title) {
return new AihrSopSeedService.KnowledgeHit(fragmentId, title, "sop", "", "doc-" + fragmentId, "片段内容", 1, 1.0);
}
@@ -257,6 +257,8 @@ CREATE TABLE IF NOT EXISTS `aihr_knowledge_answer_feedback` (
`source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源',
`review_id` bigint DEFAULT NULL COMMENT '对应SOP问答评审ID',
`review_status` varchar(30) DEFAULT '待复核' COMMENT '复核状态',
`reviewer` varchar(100) DEFAULT NULL COMMENT '复核人',
`reviewed_time` datetime DEFAULT NULL COMMENT '复核时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),