feat: 切换管理端聊天记录至智能体会话
- 管理端固定筛选 Agent 会话并拒绝旧 Bot 历史访问 - 筛选候选及页面语义切换为智能体并补充前后端回归测试
This commit is contained in:
@@ -8,6 +8,7 @@ import org.springframework.beans.factory.support.StaticListableBeanFactory;
|
||||
import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatDashboardSummary;
|
||||
import tech.easyflow.chatlog.domain.query.ChatSessionFilterQuery;
|
||||
import tech.easyflow.chatlog.support.ChatJsonSupport;
|
||||
import tech.easyflow.common.analyticaldb.config.AnalyticalDBFlywayProperties;
|
||||
import tech.easyflow.common.analyticaldb.core.AnalyticalDBOperations;
|
||||
@@ -26,6 +27,27 @@ import java.util.List;
|
||||
*/
|
||||
public class ChatAnalyticalDBRepositoryTest {
|
||||
|
||||
/**
|
||||
* 验证会话分页的统计 SQL 与数据 SQL 使用相同的 Agent 类型和 ID 条件。
|
||||
*/
|
||||
@Test
|
||||
public void shouldApplyAssistantCodeAndIdToSessionPageQueries() {
|
||||
RecordingAnalyticalDBOperations operations = new RecordingAnalyticalDBOperations();
|
||||
ChatSessionFilterQuery query = new ChatSessionFilterQuery();
|
||||
query.setAssistantCode("AGENT");
|
||||
query.setAssistantId(BigInteger.TEN);
|
||||
|
||||
ChatAnalyticalDBRepository repository = newRepository(operations);
|
||||
repository.pageSessions(query);
|
||||
|
||||
Assert.assertTrue(operations.lastPageCountSql.contains("assistant_code=?"));
|
||||
Assert.assertTrue(operations.lastPageCountSql.contains("assistant_id=?"));
|
||||
Assert.assertTrue(operations.lastPageDataSql.contains("assistant_code=?"));
|
||||
Assert.assertTrue(operations.lastPageDataSql.contains("assistant_id=?"));
|
||||
Assert.assertArrayEquals(new Object[]{"AGENT", BigInteger.TEN}, operations.lastPageCountArgs);
|
||||
Assert.assertArrayEquals(new Object[]{"AGENT", BigInteger.TEN}, operations.lastPageDataArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证工作台汇总使用跨天去重的 session 口径。
|
||||
*/
|
||||
@@ -149,6 +171,10 @@ public class ChatAnalyticalDBRepositoryTest {
|
||||
|
||||
private String lastQueryOneSql;
|
||||
private String lastQuerySql;
|
||||
private String lastPageCountSql;
|
||||
private String lastPageDataSql;
|
||||
private Object[] lastPageCountArgs;
|
||||
private Object[] lastPageDataArgs;
|
||||
private ChatDashboardSummary queryOneResult;
|
||||
|
||||
@Override
|
||||
@@ -204,7 +230,11 @@ public class ChatAnalyticalDBRepositoryTest {
|
||||
Object[] dataArgs,
|
||||
AnalyticalDBPageRequest pageRequest,
|
||||
RowMapper<T> rowMapper) {
|
||||
return null;
|
||||
this.lastPageCountSql = countSql;
|
||||
this.lastPageCountArgs = countArgs;
|
||||
this.lastPageDataSql = dataSql;
|
||||
this.lastPageDataArgs = dataArgs;
|
||||
return new AnalyticalDBPageResult<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
package tech.easyflow.chatlog.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.support.StaticListableBeanFactory;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatHistoryPage;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatSessionPage;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatSessionSummary;
|
||||
import tech.easyflow.chatlog.domain.query.ChatPageQuery;
|
||||
import tech.easyflow.chatlog.domain.query.ChatSessionFilterQuery;
|
||||
import tech.easyflow.chatlog.repository.analyticaldb.ChatAnalyticalDBRepository;
|
||||
import tech.easyflow.chatlog.service.ChatHistoryQueryService;
|
||||
import tech.easyflow.chatlog.service.ChatRoundOperateService;
|
||||
import tech.easyflow.chatlog.service.ChatSessionCommandService;
|
||||
import tech.easyflow.chatlog.service.ChatSessionQueryService;
|
||||
import tech.easyflow.chatlog.support.ChatJsonSupport;
|
||||
import tech.easyflow.common.analyticaldb.core.AnalyticalDBOperations;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* {@link ChatHistoryManageServiceImpl} 管理端 Agent 会话边界测试。
|
||||
*/
|
||||
public class ChatHistoryManageServiceImplTest {
|
||||
|
||||
private StubChatAnalyticalDBRepository chatAnalyticalDBRepository;
|
||||
private ChatHistoryManageServiceImpl service;
|
||||
|
||||
/**
|
||||
* 初始化管理端聊天历史服务及其依赖。
|
||||
*/
|
||||
@Before
|
||||
public void setUp() {
|
||||
ChatSessionQueryService chatSessionQueryService = unusedDependency(ChatSessionQueryService.class);
|
||||
ChatSessionCommandService chatSessionCommandService = unusedDependency(ChatSessionCommandService.class);
|
||||
ChatHistoryQueryService chatHistoryQueryService = unusedDependency(ChatHistoryQueryService.class);
|
||||
ChatRoundOperateService chatRoundOperateService = unusedDependency(ChatRoundOperateService.class);
|
||||
chatAnalyticalDBRepository = new StubChatAnalyticalDBRepository();
|
||||
service = new ChatHistoryManageServiceImpl(
|
||||
chatSessionQueryService,
|
||||
chatSessionCommandService,
|
||||
chatHistoryQueryService,
|
||||
chatRoundOperateService,
|
||||
chatAnalyticalDBRepository
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证管理端列表始终覆盖客户端传入的会话类型为 Agent。
|
||||
*/
|
||||
@Test
|
||||
public void queryAdminSessionsShouldForceAgentAssistantCode() {
|
||||
ChatSessionFilterQuery query = new ChatSessionFilterQuery();
|
||||
query.setAssistantCode("BOT");
|
||||
|
||||
service.queryAdminSessions(query);
|
||||
|
||||
Assert.assertEquals("AGENT", query.getAssistantCode());
|
||||
Assert.assertSame(query, chatAnalyticalDBRepository.lastPageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证管理端可以读取正式 Agent 会话。
|
||||
*/
|
||||
@Test
|
||||
public void getAdminSessionShouldReturnAgentSession() {
|
||||
BigInteger sessionId = BigInteger.valueOf(1001);
|
||||
ChatSessionSummary summary = session(sessionId, "AGENT", 0);
|
||||
chatAnalyticalDBRepository.sessionResult = summary;
|
||||
|
||||
ChatSessionSummary result = service.getAdminSession(sessionId);
|
||||
|
||||
Assert.assertSame(summary, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证正式 Agent 会话可以继续读取历史消息。
|
||||
*/
|
||||
@Test
|
||||
public void queryAdminMessagesShouldReturnAgentHistory() {
|
||||
BigInteger sessionId = BigInteger.valueOf(1004);
|
||||
ChatHistoryPage expectedPage = new ChatHistoryPage();
|
||||
chatAnalyticalDBRepository.sessionResult = session(sessionId, "AGENT", 0);
|
||||
service = new ChatHistoryManageServiceImpl(
|
||||
unusedDependency(ChatSessionQueryService.class),
|
||||
unusedDependency(ChatSessionCommandService.class),
|
||||
(requestedSessionId, query) -> expectedPage,
|
||||
unusedDependency(ChatRoundOperateService.class),
|
||||
chatAnalyticalDBRepository
|
||||
);
|
||||
ChatPageQuery query = new ChatPageQuery();
|
||||
query.setPageNumber(2);
|
||||
|
||||
ChatHistoryPage result = service.queryAdminMessages(sessionId, query);
|
||||
|
||||
Assert.assertSame(expectedPage, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证管理端拒绝旧 Bot 会话详情。
|
||||
*/
|
||||
@Test
|
||||
public void getAdminSessionShouldRejectBotSession() {
|
||||
BigInteger sessionId = BigInteger.valueOf(1002);
|
||||
chatAnalyticalDBRepository.sessionResult = session(sessionId, "BOT", 0);
|
||||
|
||||
BusinessException exception = Assert.assertThrows(
|
||||
BusinessException.class,
|
||||
() -> service.getAdminSession(sessionId)
|
||||
);
|
||||
|
||||
Assert.assertEquals("Agent 会话不存在", exception.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证旧 Bot 会话在消息查询前即被拒绝,不会继续读取历史消息。
|
||||
*/
|
||||
@Test
|
||||
public void queryAdminMessagesShouldRejectBotBeforeHistoryLookup() {
|
||||
BigInteger sessionId = BigInteger.valueOf(1003);
|
||||
chatAnalyticalDBRepository.sessionResult = session(sessionId, "BOT", 0);
|
||||
|
||||
Assert.assertThrows(
|
||||
BusinessException.class,
|
||||
() -> service.queryAdminMessages(sessionId, new ChatPageQuery())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证旧 Bot 会话在答案版本列表查询前即被拒绝。
|
||||
*/
|
||||
@Test
|
||||
public void listAdminRoundVariantsShouldRejectBotBeforeRoundLookup() {
|
||||
BigInteger sessionId = BigInteger.valueOf(1005);
|
||||
chatAnalyticalDBRepository.sessionResult = session(sessionId, "BOT", 0);
|
||||
|
||||
Assert.assertThrows(
|
||||
BusinessException.class,
|
||||
() -> service.listAdminRoundVariants(sessionId, BigInteger.ONE)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证旧 Bot 会话在答案版本选择前即被拒绝。
|
||||
*/
|
||||
@Test
|
||||
public void selectAdminRoundVariantShouldRejectBotBeforeRoundUpdate() {
|
||||
BigInteger sessionId = BigInteger.valueOf(1006);
|
||||
chatAnalyticalDBRepository.sessionResult = session(sessionId, "BOT", 0);
|
||||
|
||||
Assert.assertThrows(
|
||||
BusinessException.class,
|
||||
() -> service.selectAdminRoundVariant(
|
||||
sessionId,
|
||||
BigInteger.ONE,
|
||||
1,
|
||||
BigInteger.TEN
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建不应在当前测试路径中被调用的接口依赖。
|
||||
*
|
||||
* @param dependencyType 依赖接口类型
|
||||
* @param <T> 依赖接口类型
|
||||
* @return 调用任意方法即失败的代理对象
|
||||
*/
|
||||
private <T> T unusedDependency(Class<T> dependencyType) {
|
||||
Object proxy = Proxy.newProxyInstance(
|
||||
dependencyType.getClassLoader(),
|
||||
new Class<?>[]{dependencyType},
|
||||
(instance, method, args) -> {
|
||||
throw new AssertionError("测试路径不应调用依赖方法: " + method.getName());
|
||||
}
|
||||
);
|
||||
return dependencyType.cast(proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造会话摘要。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @param assistantCode 助手类型编码
|
||||
* @param isDeleted 删除标识
|
||||
* @return 会话摘要
|
||||
*/
|
||||
private ChatSessionSummary session(BigInteger sessionId, String assistantCode, int isDeleted) {
|
||||
ChatSessionSummary summary = new ChatSessionSummary();
|
||||
summary.setId(sessionId);
|
||||
summary.setAssistantCode(assistantCode);
|
||||
summary.setIsDeleted(isDeleted);
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅记录管理端会话查询参数和返回值的分析库仓储桩。
|
||||
*/
|
||||
private static class StubChatAnalyticalDBRepository extends ChatAnalyticalDBRepository {
|
||||
|
||||
private ChatSessionFilterQuery lastPageQuery;
|
||||
private ChatSessionSummary sessionResult;
|
||||
|
||||
/**
|
||||
* 创建不连接真实分析库的仓储桩。
|
||||
*/
|
||||
private StubChatAnalyticalDBRepository() {
|
||||
super(
|
||||
new StaticListableBeanFactory().getBeanProvider(AnalyticalDBOperations.class),
|
||||
null,
|
||||
new ChatJsonSupport(new ObjectMapper())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录分页筛选参数。
|
||||
*
|
||||
* @param query 会话筛选条件
|
||||
* @return 空分页结果
|
||||
*/
|
||||
@Override
|
||||
public ChatSessionPage pageSessions(ChatSessionFilterQuery query) {
|
||||
lastPageQuery = query;
|
||||
return new ChatSessionPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回预设的会话摘要。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @return 预设会话摘要
|
||||
*/
|
||||
@Override
|
||||
public ChatSessionSummary getSession(BigInteger sessionId) {
|
||||
return sessionResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user