fix(aihr): audit knowledge feedback reviews
This commit is contained in:
+3
-1
@@ -106,7 +106,9 @@ public class AihrSopController {
|
|||||||
@SaCheckRole(value = {TenantConstants.SUPER_ADMIN_ROLE_KEY, HR_OPERATOR_ROLE}, mode = SaMode.OR)
|
@SaCheckRole(value = {TenantConstants.SUPER_ADMIN_ROLE_KEY, HR_OPERATOR_ROLE}, mode = SaMode.OR)
|
||||||
@PostMapping("/answer-feedback/{id}/review")
|
@PostMapping("/answer-feedback/{id}/review")
|
||||||
public R<AnswerFeedbackReviewResponse> reviewAnswerFeedback(@PathVariable Long id) {
|
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);
|
return response == null ? R.fail("答案反馈记录不存在") : R.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -44,10 +44,10 @@ public final class AihrSopDto {
|
|||||||
public record AnswerFeedbackResponse(Integer saved, String verdict, String reviewStatus) {
|
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) {
|
public record SummaryCardResponse(String title, List<CardStep> steps, List<CardObjection> objections, List<String> scripts, List<String> reminders) {
|
||||||
|
|||||||
+17
-4
@@ -271,7 +271,7 @@ public class AihrSopSeedService {
|
|||||||
String reviewStatus = firstNonBlank(status, "待复核");
|
String reviewStatus = firstNonBlank(status, "待复核");
|
||||||
int limitValue = Math.max(1, Math.min(limit, 200));
|
int limitValue = Math.max(1, Math.min(limit, 200));
|
||||||
return jdbcTemplate.query("""
|
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
|
FROM aihr_knowledge_answer_feedback
|
||||||
WHERE tenant_id = ?
|
WHERE tenant_id = ?
|
||||||
AND (? = '' OR review_status = ?)
|
AND (? = '' OR review_status = ?)
|
||||||
@@ -288,26 +288,35 @@ public class AihrSopSeedService {
|
|||||||
rs.getString("source"),
|
rs.getString("source"),
|
||||||
rs.getObject("review_id") == null ? null : rs.getLong("review_id"),
|
rs.getObject("review_id") == null ? null : rs.getLong("review_id"),
|
||||||
rs.getString("review_status"),
|
rs.getString("review_status"),
|
||||||
|
rs.getString("reviewer"),
|
||||||
|
formatTimestamp(rs.getTimestamp("reviewed_time")),
|
||||||
formatTimestamp(rs.getTimestamp("create_time")),
|
formatTimestamp(rs.getTimestamp("create_time")),
|
||||||
formatTimestamp(rs.getTimestamp("update_time"))
|
formatTimestamp(rs.getTimestamp("update_time"))
|
||||||
), tenantId(), reviewStatus, reviewStatus, limitValue);
|
), tenantId(), reviewStatus, reviewStatus, limitValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnswerFeedbackReviewResponse reviewAnswerFeedback(Long id) {
|
public AnswerFeedbackReviewResponse reviewAnswerFeedback(Long id) {
|
||||||
|
return reviewAnswerFeedback(id, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnswerFeedbackReviewResponse reviewAnswerFeedback(Long id, String operator) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ensureAnswerFeedbackTable();
|
ensureAnswerFeedbackTable();
|
||||||
|
Timestamp reviewedTime = Timestamp.valueOf(java.time.LocalDateTime.now());
|
||||||
int updated = jdbcTemplate.update("""
|
int updated = jdbcTemplate.update("""
|
||||||
UPDATE aihr_knowledge_answer_feedback
|
UPDATE aihr_knowledge_answer_feedback
|
||||||
SET review_status = '已复核', update_time = ?
|
SET review_status = '已复核', reviewer = ?, reviewed_time = ?, update_time = ?
|
||||||
WHERE tenant_id = ? AND id = ?
|
WHERE tenant_id = ? AND id = ?
|
||||||
""",
|
""",
|
||||||
Timestamp.valueOf(java.time.LocalDateTime.now()),
|
firstNonBlank(operator, "人工复核"),
|
||||||
|
reviewedTime,
|
||||||
|
reviewedTime,
|
||||||
tenantId(),
|
tenantId(),
|
||||||
id
|
id
|
||||||
);
|
);
|
||||||
return updated == 0 ? null : new AnswerFeedbackReviewResponse(id, "已复核");
|
return updated == 0 ? null : new AnswerFeedbackReviewResponse(id, "已复核", firstNonBlank(operator, "人工复核"), formatTimestamp(reviewedTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SummaryCardResponse summaryCard(SummaryCardRequest request) {
|
public SummaryCardResponse summaryCard(SummaryCardRequest request) {
|
||||||
@@ -1990,6 +1999,8 @@ public class AihrSopSeedService {
|
|||||||
`source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源',
|
`source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源',
|
||||||
`review_id` bigint DEFAULT NULL COMMENT '对应SOP问答评审ID',
|
`review_id` bigint DEFAULT NULL COMMENT '对应SOP问答评审ID',
|
||||||
`review_status` varchar(30) DEFAULT '待复核' COMMENT '复核状态',
|
`review_status` varchar(30) DEFAULT '待复核' COMMENT '复核状态',
|
||||||
|
`reviewer` varchar(100) DEFAULT NULL COMMENT '复核人',
|
||||||
|
`reviewed_time` datetime DEFAULT NULL COMMENT '复核时间',
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
@@ -1999,6 +2010,8 @@ public class AihrSopSeedService {
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI HR 知识答案反馈';
|
) 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", "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;
|
answerFeedbackTableReady = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -117,6 +117,18 @@ public class AihrSopSeedServiceTest {
|
|||||||
assertTrue(code.contains("reviewSearch(id, request, operator)"));
|
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) {
|
private static AihrSopSeedService.KnowledgeHit hit(Long fragmentId, String title) {
|
||||||
return new AihrSopSeedService.KnowledgeHit(fragmentId, title, "sop", "", "doc-" + fragmentId, "片段内容", 1, 1.0);
|
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 '来源',
|
`source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源',
|
||||||
`review_id` bigint DEFAULT NULL COMMENT '对应SOP问答评审ID',
|
`review_id` bigint DEFAULT NULL COMMENT '对应SOP问答评审ID',
|
||||||
`review_status` varchar(30) DEFAULT '待复核' COMMENT '复核状态',
|
`review_status` varchar(30) DEFAULT '待复核' COMMENT '复核状态',
|
||||||
|
`reviewer` varchar(100) DEFAULT NULL COMMENT '复核人',
|
||||||
|
`reviewed_time` datetime DEFAULT NULL COMMENT '复核时间',
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
|
|||||||
@@ -179,3 +179,4 @@
|
|||||||
- 2026-07-14 BRD 案例署名复核:案例库已有负责人字段,但此前上传、整理和入库记录都硬编码为“培训组”,无法追溯案例贡献者;现复用既有 `owner` 字段保存首次上传身份,整理和入库阶段保留原署名,手机号身份仅显示员工末四位,避免把原始手机号写入案例展示。
|
- 2026-07-14 BRD 案例署名复核:案例库已有负责人字段,但此前上传、整理和入库记录都硬编码为“培训组”,无法追溯案例贡献者;现复用既有 `owner` 字段保存首次上传身份,整理和入库阶段保留原署名,手机号身份仅显示员工末四位,避免把原始手机号写入案例展示。
|
||||||
- 2026-07-14 BRD 候选人关联完整性复核:候选人—员工关联接口此前只校验员工主体在职,允许给不存在的任意 `candidateId` 建立入职关联;现要求候选人必须来自内置候选档案、活动面试会话或当前租户已持久化面试记录,保留已有幂等关联和历史数据兼容。
|
- 2026-07-14 BRD 候选人关联完整性复核:候选人—员工关联接口此前只校验员工主体在职,允许给不存在的任意 `candidateId` 建立入职关联;现要求候选人必须来自内置候选档案、活动面试会话或当前租户已持久化面试记录,保留已有幂等关联和历史数据兼容。
|
||||||
- 2026-07-14 BRD SOP 审核留痕复核:SOP 问答评审接口此前接受请求体中的 `reviewer` 并直接入库,登录用户可伪造审核人;现由控制器从当前登录用户传入服务端操作人,请求体仅保留评审结论和备注,保留旧服务调用的默认“人工评审”兼容。
|
- 2026-07-14 BRD SOP 审核留痕复核:SOP 问答评审接口此前接受请求体中的 `reviewer` 并直接入库,登录用户可伪造审核人;现由控制器从当前登录用户传入服务端操作人,请求体仅保留评审结论和备注,保留旧服务调用的默认“人工评审”兼容。
|
||||||
|
- 2026-07-14 BRD 知识答案反馈复核:原先只更新 `review_status`,没有复核人和复核时间;现由服务端记录登录操作人到 `reviewer/reviewed_time`,管理端待复核表同步展示留痕。
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ export type AnswerFeedbackItem = {
|
|||||||
source: string;
|
source: string;
|
||||||
reviewId?: number | null;
|
reviewId?: number | null;
|
||||||
reviewStatus: string;
|
reviewStatus: string;
|
||||||
|
reviewer?: string | null;
|
||||||
|
reviewedTime?: string | null;
|
||||||
createTime: string;
|
createTime: string;
|
||||||
updateTime: string;
|
updateTime: string;
|
||||||
};
|
};
|
||||||
@@ -68,6 +70,8 @@ export type AnswerFeedbackItem = {
|
|||||||
export type AnswerFeedbackReviewResponse = {
|
export type AnswerFeedbackReviewResponse = {
|
||||||
id: number;
|
id: number;
|
||||||
reviewStatus: string;
|
reviewStatus: string;
|
||||||
|
reviewer?: string | null;
|
||||||
|
reviewedTime?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SopUploadResponse = {
|
export type SopUploadResponse = {
|
||||||
|
|||||||
@@ -115,6 +115,8 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="position" label="岗位" width="120" />
|
<el-table-column prop="position" label="岗位" width="120" />
|
||||||
<el-table-column prop="createTime" label="提交时间" width="170" />
|
<el-table-column prop="createTime" label="提交时间" width="170" />
|
||||||
|
<el-table-column prop="reviewer" label="复核人" width="120" />
|
||||||
|
<el-table-column prop="reviewedTime" label="复核时间" width="170" />
|
||||||
<el-table-column label="操作" width="110" fixed="right">
|
<el-table-column label="操作" width="110" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button
|
<el-button
|
||||||
|
|||||||
@@ -487,6 +487,7 @@ contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/control
|
|||||||
contains frontend/src/api/aihr/sop.ts "/api/knowledge/doc/upload"
|
contains frontend/src/api/aihr/sop.ts "/api/knowledge/doc/upload"
|
||||||
contains frontend/src/api/aihr/sop.ts "/api/knowledge/reviews"
|
contains frontend/src/api/aihr/sop.ts "/api/knowledge/reviews"
|
||||||
contains frontend/src/api/aihr/sop.ts "/api/knowledge/answer-feedback"
|
contains frontend/src/api/aihr/sop.ts "/api/knowledge/answer-feedback"
|
||||||
|
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "reviewAnswerFeedback(id, operator)"
|
||||||
contains frontend/src/views/knowledge/sop.vue "SOP 问答评审已记录"
|
contains frontend/src/views/knowledge/sop.vue "SOP 问答评审已记录"
|
||||||
contains frontend/src/views/knowledge/sop.vue "答案待复核"
|
contains frontend/src/views/knowledge/sop.vue "答案待复核"
|
||||||
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "/api/knowledge"
|
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "/api/knowledge"
|
||||||
|
|||||||
Reference in New Issue
Block a user