21 changed files with 1254 additions and 132 deletions
-
252client/dist/chatbot-sdk.js
-
2client/dist/chatbot-sdk.min.js
-
24client/src/api.ts
-
56client/src/chat.ts
-
87client/src/dom.ts
-
14client/src/i18n.ts
-
95client/src/styles.ts
-
4client/src/types.ts
-
2frontend/auto-imports.d.ts
-
2frontend/src/router/index.ts
-
1frontend/src/stores/navigation.ts
-
9frontend/src/views/ChatPanel.vue
-
18frontend/src/views/DashboardPanel.vue
-
20src/main/java/com/wok/supportbot/app/ChatPipeline.java
-
33src/main/java/com/wok/supportbot/rag/RagPipeline.java
-
4src/main/java/com/wok/supportbot/security/SdkAuthFilter.java
-
202src/main/java/com/wok/supportbot/service/DashboardService.java
-
22src/main/java/com/wok/supportbot/service/MessageFeedbackService.java
-
252src/main/resources/static/sdk/chatbot-sdk.js
-
2src/main/resources/static/sdk/chatbot-sdk.min.js
-
227src/test/java/com/wok/supportbot/MessageFeedbackTests.java
2
client/dist/chatbot-sdk.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
src/main/resources/static/sdk/chatbot-sdk.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,227 @@ |
|||
package com.wok.supportbot; |
|||
|
|||
import com.wok.supportbot.dao.MessageFeedbackMapper; |
|||
import com.wok.supportbot.entity.MessageFeedback; |
|||
import com.wok.supportbot.service.MessageFeedbackService; |
|||
import jakarta.annotation.Resource; |
|||
import org.junit.jupiter.api.*; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
|
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
|
|||
/** |
|||
* 消息反馈功能集成测试 |
|||
* 覆盖:提交/更新/查询/统计/取消 完整链路 |
|||
* <p> |
|||
* 需要运行中的 PostgreSQL。 |
|||
*/ |
|||
@SpringBootTest |
|||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) |
|||
@DisplayName("消息反馈功能集成测试") |
|||
class MessageFeedbackTests { |
|||
|
|||
@Resource |
|||
private MessageFeedbackService messageFeedbackService; |
|||
|
|||
@Resource |
|||
private MessageFeedbackMapper messageFeedbackMapper; |
|||
|
|||
private static final String TEST_MESSAGE_ID = "test_feedback_" + UUID.randomUUID().toString().substring(0, 8); |
|||
private static final String TEST_CONVERSATION_ID = "test_conv_" + UUID.randomUUID().toString().substring(0, 8); |
|||
|
|||
// ==================== 提交与查询 ==================== |
|||
|
|||
@Test |
|||
@Order(1) |
|||
@DisplayName("F-01 新增反馈(点赞)应持久化到数据库") |
|||
void submitThumbsUp() { |
|||
MessageFeedback fb = MessageFeedback.builder() |
|||
.messageId(TEST_MESSAGE_ID) |
|||
.conversationId(TEST_CONVERSATION_ID) |
|||
.feedbackType("THUMBS_UP") |
|||
.build(); |
|||
MessageFeedback saved = messageFeedbackService.submitFeedback(fb); |
|||
assertNotNull(saved); |
|||
assertNotNull(saved.getId()); |
|||
assertEquals(TEST_MESSAGE_ID, saved.getMessageId()); |
|||
assertEquals("THUMBS_UP", saved.getFeedbackType()); |
|||
} |
|||
|
|||
@Test |
|||
@Order(2) |
|||
@DisplayName("F-02 按 messageId 查询反馈") |
|||
void getByMessageId() { |
|||
MessageFeedback fb = messageFeedbackService.getByMessageId(TEST_MESSAGE_ID); |
|||
assertNotNull(fb); |
|||
assertEquals(TEST_MESSAGE_ID, fb.getMessageId()); |
|||
assertEquals("THUMBS_UP", fb.getFeedbackType()); |
|||
} |
|||
|
|||
@Test |
|||
@Order(3) |
|||
@DisplayName("F-03 覆盖提交(upsert):点赞改为点踩") |
|||
void upsertFeedback() { |
|||
MessageFeedback fb = MessageFeedback.builder() |
|||
.messageId(TEST_MESSAGE_ID) |
|||
.conversationId(TEST_CONVERSATION_ID) |
|||
.feedbackType("THUMBS_DOWN") |
|||
.reasonCategory("inaccurate") |
|||
.reasonComment("答案有误") |
|||
.build(); |
|||
MessageFeedback saved = messageFeedbackService.submitFeedback(fb); |
|||
|
|||
// 验证覆盖后只有一条记录 |
|||
MessageFeedback queried = messageFeedbackService.getByMessageId(TEST_MESSAGE_ID); |
|||
assertNotNull(queried); |
|||
assertEquals("THUMBS_DOWN", queried.getFeedbackType()); |
|||
assertEquals("inaccurate", queried.getReasonCategory()); |
|||
assertEquals("答案有误", queried.getReasonComment()); |
|||
|
|||
// 验证 upsert 逻辑:id 应与原始相同 (不是新插入) |
|||
assertEquals(queried.getId(), saved.getId()); |
|||
} |
|||
|
|||
// ==================== 批量查询 ==================== |
|||
|
|||
@Test |
|||
@Order(4) |
|||
@DisplayName("F-04 批量查询反馈(SDK 回显场景)") |
|||
void getBatchByMessageIds() { |
|||
// 插入一条额外反馈 |
|||
String msg2 = "test_batch_" + UUID.randomUUID().toString().substring(0, 8); |
|||
messageFeedbackService.submitFeedback(MessageFeedback.builder() |
|||
.messageId(msg2) |
|||
.conversationId(TEST_CONVERSATION_ID) |
|||
.feedbackType("THUMBS_UP") |
|||
.build()); |
|||
|
|||
List<MessageFeedback> list = messageFeedbackService.getBatchByMessageIds( |
|||
List.of(TEST_MESSAGE_ID, msg2, "non_existent_id")); |
|||
assertEquals(2, list.size()); |
|||
} |
|||
|
|||
// ==================== 按会话查询 ==================== |
|||
|
|||
@Test |
|||
@Order(5) |
|||
@DisplayName("F-05 按会话 ID 查询所有反馈") |
|||
void getByConversationId() { |
|||
List<MessageFeedback> list = messageFeedbackService.getByConversationId(TEST_CONVERSATION_ID); |
|||
assertFalse(list.isEmpty()); |
|||
assertTrue(list.stream().allMatch(fb -> TEST_CONVERSATION_ID.equals(fb.getConversationId()))); |
|||
} |
|||
|
|||
// ==================== 统计 ==================== |
|||
|
|||
@Test |
|||
@Order(6) |
|||
@DisplayName("F-06 获取全局反馈统计") |
|||
void getStats() { |
|||
Map<String, Object> stats = messageFeedbackService.getStats(null, null); |
|||
assertNotNull(stats); |
|||
assertTrue(stats.containsKey("totalFeedbacks")); |
|||
assertTrue(stats.containsKey("thumbsUpCount")); |
|||
assertTrue(stats.containsKey("thumbsDownCount")); |
|||
assertTrue(stats.containsKey("satisfactionRate")); |
|||
assertTrue(stats.containsKey("reasonDistribution")); |
|||
assertTrue(stats.containsKey("dailyTrends")); |
|||
|
|||
// 满意率应在 0.0 ~ 1.0 之间 |
|||
double rate = (Double) stats.get("satisfactionRate"); |
|||
assertTrue(rate >= 0.0 && rate <= 1.0, "满意率应在 0~1 之间,实际: " + rate); |
|||
} |
|||
|
|||
@Test |
|||
@Order(7) |
|||
@DisplayName("F-07 统计按日期过滤") |
|||
void getStatsWithDateFilter() { |
|||
// 用近期日期范围过滤 |
|||
Map<String, Object> stats = messageFeedbackService.getStats("2020-01-01", "2099-12-31"); |
|||
assertNotNull(stats); |
|||
long total = ((Number) stats.get("totalFeedbacks")).longValue(); |
|||
assertTrue(total > 0, "宽日期范围应有数据"); |
|||
|
|||
// 用不可能有数据的日期范围过滤 |
|||
stats = messageFeedbackService.getStats("2000-01-01", "2000-01-02"); |
|||
long zero = ((Number) stats.get("totalFeedbacks")).longValue(); |
|||
assertEquals(0L, zero, "远古日期范围应无数反馈"); |
|||
} |
|||
|
|||
// ==================== 边界条件 ==================== |
|||
|
|||
@Test |
|||
@Order(8) |
|||
@DisplayName("F-08 查询不存在的 messageId 返回 null") |
|||
void getByNonExistentMessageId() { |
|||
MessageFeedback fb = messageFeedbackService.getByMessageId("non_existent_" + UUID.randomUUID()); |
|||
assertNull(fb); |
|||
} |
|||
|
|||
@Test |
|||
@Order(9) |
|||
@DisplayName("F-09 批量查询传入空列表返回空结果") |
|||
void getBatchByEmptyIds() { |
|||
List<MessageFeedback> list = messageFeedbackService.getBatchByMessageIds(List.of()); |
|||
assertTrue(list.isEmpty()); |
|||
} |
|||
|
|||
@Test |
|||
@Order(10) |
|||
@DisplayName("F-10 提交反馈后 createTime 和 updateTime 不为空") |
|||
void verifyTimestamps() { |
|||
String msgId = "test_ts_" + UUID.randomUUID().toString().substring(0, 8); |
|||
MessageFeedback fb = MessageFeedback.builder() |
|||
.messageId(msgId) |
|||
.conversationId(TEST_CONVERSATION_ID) |
|||
.feedbackType("THUMBS_UP") |
|||
.build(); |
|||
MessageFeedback saved = messageFeedbackService.submitFeedback(fb); |
|||
assertNotNull(saved.getCreateTime()); |
|||
assertNotNull(saved.getUpdateTime()); |
|||
} |
|||
|
|||
@Test |
|||
@Order(11) |
|||
@DisplayName("F-11 更新反馈时 updateTime 应更新") |
|||
void verifyUpdateTimeChanges() throws InterruptedException { |
|||
String msgId = "test_uptime_" + UUID.randomUUID().toString().substring(0, 8); |
|||
MessageFeedback fb = MessageFeedback.builder() |
|||
.messageId(msgId) |
|||
.conversationId(TEST_CONVERSATION_ID) |
|||
.feedbackType("THUMBS_UP") |
|||
.build(); |
|||
MessageFeedback first = messageFeedbackService.submitFeedback(fb); |
|||
|
|||
Thread.sleep(10); // 确保时间戳有差异 |
|||
|
|||
MessageFeedback update = MessageFeedback.builder() |
|||
.messageId(msgId) |
|||
.conversationId(TEST_CONVERSATION_ID) |
|||
.feedbackType("THUMBS_DOWN") |
|||
.build(); |
|||
MessageFeedback second = messageFeedbackService.submitFeedback(update); |
|||
assertTrue(second.getUpdateTime().after(first.getUpdateTime()), |
|||
"更新后 updateTime 应晚于初始值"); |
|||
} |
|||
|
|||
// ==================== 点踩原因分布 ==================== |
|||
|
|||
@Test |
|||
@Order(12) |
|||
@DisplayName("F-12 点踩原因包含四种分类") |
|||
void reasonCategoryValues() { |
|||
// 验证统计中的 reasonDistribution 返回正确 |
|||
Map<String, Object> stats = messageFeedbackService.getStats(null, null); |
|||
@SuppressWarnings("unchecked") |
|||
Map<String, Long> dist = (Map<String, Long>) stats.get("reasonDistribution"); |
|||
assertNotNull(dist); |
|||
// 键应为四种原因之一 |
|||
dist.keySet().forEach(key -> assertTrue( |
|||
List.of("inaccurate", "irrelevant", "incomplete", "other").contains(key), |
|||
"未知原因分类: " + key)); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue