feat: 完成管理端聊天工作台收口

- 新增管理端聊天工作台与会话级额外知识库持久化

- 补齐发布态聊天、历史会话只读判断与答案版本切换

- 新增 chat_round 热数据与主线消息读取支撑
This commit is contained in:
2026-05-14 20:22:46 +08:00
parent 2ad8935a61
commit 47c2bad839
63 changed files with 8609 additions and 136 deletions

View File

@@ -1,18 +1,29 @@
package tech.easyflow.chatlog.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.chatlog.domain.command.ChatAppendMessageCommand;
import tech.easyflow.chatlog.domain.command.ChatSessionSummaryCommand;
import tech.easyflow.chatlog.domain.command.ChatSessionUpsertCommand;
import tech.easyflow.chatlog.domain.event.ChatPersistEvent;
import tech.easyflow.chatlog.domain.event.ChatPersistEventType;
import tech.easyflow.chatlog.repository.mysql.MySqlChatLogRepository;
import tech.easyflow.chatlog.repository.mysql.MySqlChatLogTableManager;
import tech.easyflow.chatlog.repository.mysql.MySqlChatRoundRepository;
import tech.easyflow.chatlog.repository.mysql.MySqlChatSessionRepository;
import tech.easyflow.chatlog.support.ChatJsonSupport;
import java.math.BigInteger;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class ChatPersistMySqlApplyServiceTest {
private final ChatPersistMySqlApplyService service =
new ChatPersistMySqlApplyService(null, null, null, null);
new ChatPersistMySqlApplyService(null, null, null, null, new ChatJsonSupport(new ObjectMapper()));
@Test
public void shouldBuildMissingSessionUpsertFromMessageMetadata() {
@@ -69,4 +80,101 @@ public class ChatPersistMySqlApplyServiceTest {
Assert.assertEquals("会话-202", upsert.getTitle());
Assert.assertEquals(BigInteger.valueOf(7), upsert.getOperatorId());
}
@Test
public void shouldNotDoubleCountSummaryWhenMessageEventReplayed() {
ChatJsonSupport jsonSupport = new ChatJsonSupport(new ObjectMapper());
FakeSessionRepository sessionRepository = new FakeSessionRepository();
FakeLogRepository logRepository = new FakeLogRepository(jsonSupport);
ChatPersistMySqlApplyService applyService = new ChatPersistMySqlApplyService(
sessionRepository,
logRepository,
new FakeRoundRepository(),
new FakeTableManager(),
jsonSupport
);
ChatAppendMessageCommand command = new ChatAppendMessageCommand();
command.setMessageId(BigInteger.valueOf(301));
command.setSessionId(BigInteger.valueOf(401));
command.setTenantId(BigInteger.ONE);
command.setDeptId(BigInteger.ONE);
command.setUserId(BigInteger.valueOf(7));
command.setAssistantId(BigInteger.valueOf(8));
command.setSenderId(BigInteger.valueOf(7));
command.setSenderName("admin");
command.setSenderRole("user");
command.setContentText("第一条消息");
command.setCreatedBy(BigInteger.valueOf(7));
command.setCreated(new Date(4_000L));
ChatPersistEvent event = new ChatPersistEvent();
event.setEventId("message-301");
event.setEventType(ChatPersistEventType.USER_MESSAGE_APPENDED);
event.setSessionId(command.getSessionId());
event.setPayload(jsonSupport.toJson(command));
applyService.apply(List.of(event));
applyService.apply(List.of(event));
Assert.assertEquals(1, sessionRepository.summaryCommands.size());
ChatSessionSummaryCommand summaryCommand = sessionRepository.summaryCommands.get(0);
Assert.assertEquals(1, summaryCommand.getMessageIncrement());
Assert.assertEquals("第一条消息", summaryCommand.getLastMessagePreview());
Assert.assertEquals(new Date(4_000L), summaryCommand.getLastMessageAt());
Assert.assertEquals(new Date(4_000L), summaryCommand.getAccessAt());
}
private static final class FakeSessionRepository extends MySqlChatSessionRepository {
private final List<ChatSessionSummaryCommand> summaryCommands = new ArrayList<>();
private FakeSessionRepository() {
super(null, null);
}
@Override
public void createOrTouchBatch(List<ChatSessionUpsertCommand> commands) {
}
@Override
public void updateSummaries(List<ChatSessionSummaryCommand> commands) {
summaryCommands.addAll(commands);
}
}
private static final class FakeLogRepository extends MySqlChatLogRepository {
private boolean inserted;
private FakeLogRepository(ChatJsonSupport jsonSupport) {
super(null, null, jsonSupport);
}
@Override
public List<ChatAppendMessageCommand> appendMessages(List<ChatAppendMessageCommand> commands) {
if (inserted) {
return List.of();
}
inserted = true;
return commands;
}
}
private static final class FakeRoundRepository extends MySqlChatRoundRepository {
private FakeRoundRepository() {
super(null);
}
}
private static final class FakeTableManager extends MySqlChatLogTableManager {
private FakeTableManager() {
super(null, null);
}
@Override
public void ensureMonthTable(YearMonth month) {
}
}
}

View File

@@ -0,0 +1,206 @@
package tech.easyflow.chatlog.service.impl;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.chatlog.domain.command.ChatRoundSelectCommand;
import tech.easyflow.chatlog.domain.command.ChatRoundUpsertCommand;
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
import tech.easyflow.chatlog.service.ChatRoundCommandService;
import tech.easyflow.chatlog.service.ChatRoundQueryService;
import tech.easyflow.chatlog.support.ChatConstants;
import tech.easyflow.common.web.exceptions.BusinessException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
/**
* {@link ChatRoundOperateServiceImpl} 单元测试。
*/
public class ChatRoundOperateServiceImplTest {
/**
* 切换答案版本时应精准查询目标版本,避免先加载全部版本再过滤。
*/
@Test
public void selectVariantShouldReadTargetVariantDirectly() {
FakeRoundQueryService queryService = new FakeRoundQueryService();
queryService.round = round(BigInteger.valueOf(1001), BigInteger.valueOf(2001), 2, ChatConstants.ROUND_STATUS_READY);
queryService.latestRound = queryService.round;
queryService.targetVariant = message(BigInteger.valueOf(3002), 2);
FakeRoundCommandService commandService = new FakeRoundCommandService();
ChatRoundOperateServiceImpl service = new ChatRoundOperateServiceImpl(queryService, commandService);
ChatMessageRecord selected = service.selectVariant(
BigInteger.valueOf(1001),
BigInteger.valueOf(2001),
2,
BigInteger.valueOf(7)
);
Assert.assertEquals(BigInteger.valueOf(3002), selected.getId());
Assert.assertEquals(Integer.valueOf(2), selected.getSelectedVariantIndex());
Assert.assertEquals(Integer.valueOf(2), selected.getVariantCount());
Assert.assertEquals(Boolean.TRUE, selected.getSwitchable());
Assert.assertEquals(1, queryService.getRoundVariantCalls);
Assert.assertEquals(0, queryService.listRoundVariantsCalls);
Assert.assertNotNull(commandService.selectedCommand);
Assert.assertEquals(BigInteger.valueOf(3002), commandService.selectedCommand.getSelectedAssistantMessageId());
}
/**
* 列出答案版本时应由业务层统一补齐当前选中态和可切换状态。
*/
@Test
public void listVariantsShouldFillVariantMetadata() {
FakeRoundQueryService queryService = new FakeRoundQueryService();
queryService.round = round(BigInteger.valueOf(1001), BigInteger.valueOf(2001), 2, ChatConstants.ROUND_STATUS_READY);
queryService.latestRound = queryService.round;
queryService.variants = List.of(message(BigInteger.valueOf(3001), 1), message(BigInteger.valueOf(3002), 2));
ChatRoundOperateServiceImpl service = new ChatRoundOperateServiceImpl(queryService, new FakeRoundCommandService());
List<ChatMessageRecord> variants = service.listVariants(BigInteger.valueOf(1001), BigInteger.valueOf(2001));
Assert.assertEquals(2, variants.size());
for (ChatMessageRecord variant : variants) {
Assert.assertEquals(Integer.valueOf(2), variant.getVariantCount());
Assert.assertEquals(Integer.valueOf(2), variant.getSelectedVariantIndex());
Assert.assertEquals(Boolean.TRUE, variant.getSwitchable());
}
}
/**
* 已锁定轮次禁止切换,避免改变已有后续上下文。
*/
@Test(expected = BusinessException.class)
public void selectVariantShouldRejectLockedRound() {
FakeRoundQueryService queryService = new FakeRoundQueryService();
queryService.round = round(BigInteger.valueOf(1001), BigInteger.valueOf(2001), 2, ChatConstants.ROUND_STATUS_LOCKED);
queryService.latestRound = queryService.round;
ChatRoundOperateServiceImpl service = new ChatRoundOperateServiceImpl(queryService, new FakeRoundCommandService());
service.selectVariant(BigInteger.valueOf(1001), BigInteger.valueOf(2001), 1, BigInteger.valueOf(7));
}
/**
* 新增迁移必须为热表版本切换查询补齐索引。
*
* @throws Exception 读取迁移文件失败时抛出
*/
@Test
public void migrationShouldCreateRoundVariantIndex() throws Exception {
String sql = Files.readString(
resolveMigrationPath("V18__mysql_chat_round_variant_index.sql"),
StandardCharsets.UTF_8
);
Assert.assertTrue(sql.contains("idx_chat_log_round_variant"));
Assert.assertTrue(sql.contains("`session_id`, `round_id`, `message_kind`, `variant_index`, `created`, `id`"));
Assert.assertFalse(sql.contains("V16__mysql_chat_round_variant"));
}
/**
* 从当前测试工作目录向上查找迁移文件,兼容根工程与模块工程两种运行方式。
*
* @param fileName 迁移文件名
* @return 迁移文件路径
* @throws Exception 未找到迁移文件时抛出
*/
private static Path resolveMigrationPath(String fileName) throws Exception {
Path current = Path.of("").toAbsolutePath();
while (current != null) {
Path candidate = current.resolve(
"easyflow-starter/easyflow-starter-all/src/main/resources/db/migration/mysql/" + fileName
);
if (Files.exists(candidate)) {
return candidate;
}
current = current.getParent();
}
throw new java.nio.file.NoSuchFileException(fileName);
}
private static ChatRoundRecord round(BigInteger sessionId, BigInteger roundId, int selectedVariantIndex, String status) {
ChatRoundRecord round = new ChatRoundRecord();
round.setId(roundId);
round.setSessionId(sessionId);
round.setRoundNo(1);
round.setSelectedVariantIndex(selectedVariantIndex);
round.setVariantCount(2);
round.setStatus(status);
return round;
}
private static ChatMessageRecord message(BigInteger id, int variantIndex) {
ChatMessageRecord record = new ChatMessageRecord();
record.setId(id);
record.setSessionId(BigInteger.valueOf(1001));
record.setRoundId(BigInteger.valueOf(2001));
record.setVariantIndex(variantIndex);
record.setSenderRole("assistant");
record.setMessageKind(ChatConstants.MESSAGE_KIND_ASSISTANT_VARIANT);
record.setContentText("答案 " + variantIndex);
return record;
}
/**
* 轮次读服务测试替身。
*/
private static final class FakeRoundQueryService implements ChatRoundQueryService {
private ChatRoundRecord round;
private ChatRoundRecord latestRound;
private ChatMessageRecord targetVariant;
private List<ChatMessageRecord> variants = List.of();
private int getRoundVariantCalls;
private int listRoundVariantsCalls;
@Override
public ChatRoundRecord getLatestRound(BigInteger sessionId) {
return latestRound;
}
@Override
public ChatRoundRecord getRound(BigInteger sessionId, BigInteger roundId) {
return round;
}
@Override
public List<ChatMessageRecord> listRoundVariants(BigInteger sessionId, BigInteger roundId) {
listRoundVariantsCalls += 1;
return variants;
}
@Override
public ChatMessageRecord getRoundVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex) {
getRoundVariantCalls += 1;
return targetVariant;
}
@Override
public boolean hasRounds(BigInteger sessionId) {
return round != null;
}
}
/**
* 轮次写服务测试替身。
*/
private static final class FakeRoundCommandService implements ChatRoundCommandService {
private ChatRoundSelectCommand selectedCommand;
@Override
public ChatRoundRecord createOrTouchRound(ChatRoundUpsertCommand command) {
return null;
}
@Override
public void selectVariant(ChatRoundSelectCommand command) {
selectedCommand = command;
}
}
}

View File

@@ -0,0 +1,222 @@
package tech.easyflow.chatlog.service.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.chatlog.cache.ChatHotStateService;
import tech.easyflow.chatlog.config.ChatCacheProperties;
import tech.easyflow.chatlog.domain.dto.ChatHistoryPage;
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
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.repository.mysql.MySqlChatLogRepository;
import tech.easyflow.chatlog.repository.mysql.MySqlChatLogTableManager;
import tech.easyflow.chatlog.repository.mysql.MySqlChatSessionRepository;
import tech.easyflow.chatlog.support.ChatJsonSupport;
import java.math.BigInteger;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;
/**
* {@link ChatSessionQueryServiceImpl} 单元测试。
*/
public class ChatSessionQueryServiceImplTest {
/**
* 会话列表必须以 MySQL 会话表为唯一权威来源,不再使用 Redis 列表索引。
*/
@Test
public void pageSessionsShouldUseMysqlRepositoryAsAuthority() {
FakeSessionRepository sessionRepository = new FakeSessionRepository();
sessionRepository.sessions = List.of(session(BigInteger.valueOf(1001), 4));
sessionRepository.count = 1;
ChatSessionQueryServiceImpl service = new ChatSessionQueryServiceImpl(
sessionRepository,
new FakeLogRepository(),
new FakeTableManager(List.of()),
new FakeHotStateService()
);
ChatSessionPage page = service.pageSessions(BigInteger.valueOf(7), null, new ChatPageQuery());
Assert.assertEquals(1, page.getTotal());
Assert.assertEquals(1, page.getRecords().size());
Assert.assertEquals(1, sessionRepository.countSessionsCalls);
Assert.assertEquals(1, sessionRepository.listSessionsCalls);
}
/**
* 工作台消息分页必须走 MySQL 热表主线查询,并保持分页参数语义。
*/
@Test
public void pageMainlineMessagesShouldReadMysqlHotTables() {
FakeSessionRepository sessionRepository = new FakeSessionRepository();
sessionRepository.summary = session(BigInteger.valueOf(2001), 6);
FakeLogRepository logRepository = new FakeLogRepository();
logRepository.records = List.of(message(3001), message(3002));
List<YearMonth> months = List.of(YearMonth.of(2026, 5));
ChatSessionQueryServiceImpl service = new ChatSessionQueryServiceImpl(
sessionRepository,
logRepository,
new FakeTableManager(months),
new FakeHotStateService()
);
ChatPageQuery query = new ChatPageQuery();
query.setPageNumber(2);
query.setPageSize(2);
ChatHistoryPage page = service.pageMainlineMessages(BigInteger.valueOf(2001), query);
Assert.assertEquals(6, page.getTotal());
Assert.assertEquals(2, page.getRecords().size());
Assert.assertEquals(BigInteger.valueOf(2001), logRepository.capturedSessionId);
Assert.assertEquals(months, logRepository.capturedMonths);
Assert.assertEquals(2, logRepository.capturedOffset);
Assert.assertEquals(2, logRepository.capturedLimit);
}
/**
* 当 MySQL 摘要计数滞后时,分页 total 至少覆盖当前已返回的数据范围。
*/
@Test
public void pageMainlineMessagesShouldNotReturnTotalSmallerThanCurrentPage() {
FakeSessionRepository sessionRepository = new FakeSessionRepository();
sessionRepository.summary = session(BigInteger.valueOf(2002), 1);
FakeLogRepository logRepository = new FakeLogRepository();
logRepository.records = List.of(message(4001), message(4002));
ChatSessionQueryServiceImpl service = new ChatSessionQueryServiceImpl(
sessionRepository,
logRepository,
new FakeTableManager(List.of(YearMonth.of(2026, 5))),
new FakeHotStateService()
);
ChatPageQuery query = new ChatPageQuery();
query.setPageNumber(2);
query.setPageSize(2);
ChatHistoryPage page = service.pageMainlineMessages(BigInteger.valueOf(2002), query);
Assert.assertEquals(4, page.getTotal());
}
private static ChatSessionSummary session(BigInteger id, int messageCount) {
ChatSessionSummary summary = new ChatSessionSummary();
summary.setId(id);
summary.setUserId(BigInteger.valueOf(7));
summary.setMessageCount(messageCount);
return summary;
}
private static ChatMessageRecord message(long id) {
ChatMessageRecord record = new ChatMessageRecord();
record.setId(BigInteger.valueOf(id));
return record;
}
/**
* MySQL 会话仓储测试替身。
*/
private static final class FakeSessionRepository extends MySqlChatSessionRepository {
private long count;
private int countSessionsCalls;
private int listSessionsCalls;
private ChatSessionSummary summary;
private List<ChatSessionSummary> sessions = new ArrayList<>();
private FakeSessionRepository() {
super(null, null);
}
@Override
public List<ChatSessionSummary> listSessions(BigInteger userId, BigInteger assistantId, ChatPageQuery query) {
listSessionsCalls += 1;
return sessions;
}
@Override
public long countSessions(BigInteger userId, BigInteger assistantId) {
countSessionsCalls += 1;
return count;
}
@Override
public ChatSessionSummary findBySessionId(BigInteger sessionId) {
return summary;
}
}
/**
* MySQL 消息仓储测试替身。
*/
private static final class FakeLogRepository extends MySqlChatLogRepository {
private BigInteger capturedSessionId;
private List<YearMonth> capturedMonths;
private long capturedOffset;
private int capturedLimit;
private List<ChatMessageRecord> records = new ArrayList<>();
private FakeLogRepository() {
super(null, null, new ChatJsonSupport(new ObjectMapper()));
}
@Override
public List<ChatMessageRecord> listMainlineMessages(BigInteger sessionId, List<YearMonth> months, long offset, int limit) {
capturedSessionId = sessionId;
capturedMonths = months;
capturedOffset = offset;
capturedLimit = limit;
return records;
}
}
/**
* MySQL 热表管理器测试替身。
*/
private static final class FakeTableManager extends MySqlChatLogTableManager {
private final List<YearMonth> months;
private FakeTableManager(List<YearMonth> months) {
super(null, null);
this.months = months;
}
@Override
public List<YearMonth> listRecentExistingMonths(int retentionMonths) {
return months;
}
}
/**
* Redis 热态测试替身,避免单测依赖真实 Redis。
*/
private static final class FakeHotStateService extends ChatHotStateService {
private FakeHotStateService() {
super(null, new ObjectMapper(), new ChatCacheProperties());
}
@Override
public ChatSessionSummary getSessionSummary(BigInteger sessionId) {
return null;
}
@Override
public void cacheSessionSummary(ChatSessionSummary summary) {
}
@Override
public List<ChatMessageRecord> getSessionTail(BigInteger sessionId) {
return null;
}
@Override
public void setSessionTail(BigInteger sessionId, List<ChatMessageRecord> records) {
}
}
}

View File

@@ -0,0 +1,219 @@
package tech.easyflow.chatlog.service.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
import tech.easyflow.chatlog.domain.command.ChatSessionUpsertCommand;
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
import tech.easyflow.chatlog.domain.dto.ChatSessionExtPayload;
import tech.easyflow.chatlog.domain.dto.ChatSessionSummary;
import tech.easyflow.chatlog.service.ChatPersistDispatcher;
import tech.easyflow.chatlog.service.ChatRoundOperateService;
import tech.easyflow.chatlog.service.ChatRoundQueryService;
import tech.easyflow.chatlog.service.ChatSessionQueryService;
import tech.easyflow.chatlog.support.ChatJsonSupport;
import tech.easyflow.core.runtime.ChatRuntimeContext;
import tech.easyflow.core.runtime.ChatRuntimeExtKeys;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
/**
* {@link ChatlogRuntimeListener} 单元测试。
*/
public class ChatlogRuntimeListenerTest {
/**
* 会话准备阶段应把额外知识库选择写入 ext_json。
*/
@Test
public void onSessionPreparedShouldWriteExtraKnowledgeIdsToExtJson() {
CapturingChatPersistDispatcher dispatcher = new CapturingChatPersistDispatcher();
ChatlogRuntimeListener listener = new ChatlogRuntimeListener(
dispatcher,
new NoopChatRoundOperateService(),
new NoopChatRoundQueryService(),
new NoopChatSessionQueryService(),
new ChatJsonSupport(new ObjectMapper())
);
ChatRuntimeContext context = new ChatRuntimeContext();
context.setSessionId(BigInteger.valueOf(1001));
context.setTenantId(BigInteger.ONE);
context.setDeptId(BigInteger.TEN);
context.setUserId(BigInteger.valueOf(7));
context.setUserAccount("admin");
context.setAssistantId(BigInteger.valueOf(88));
context.setAssistantCode("bot-88");
context.setAssistantName("测试助手");
context.setSessionTitle("你好");
context.getExt().put(
ChatRuntimeExtKeys.EXTRA_KNOWLEDGE_IDS,
List.of(BigInteger.valueOf(11), BigInteger.valueOf(22))
);
listener.onSessionPrepared(context);
Assert.assertNotNull(dispatcher.captured);
ChatSessionExtPayload payload = new ChatJsonSupport(new ObjectMapper())
.fromJson(dispatcher.captured.getExtJson(), ChatSessionExtPayload.class);
Assert.assertEquals(
List.of(BigInteger.valueOf(11), BigInteger.valueOf(22)),
payload.getExtraKnowledgeIds()
);
}
/**
* 重新生成时历史上下文应排除当前轮旧问题和旧答案。
*/
@Test
public void loadMessagesShouldExcludeRegenerateRoundHistory() {
ChatlogRuntimeListener listener = new ChatlogRuntimeListener(
null,
new NoopChatRoundOperateService(),
new NoopChatRoundQueryService(),
new TailChatSessionQueryService(List.of(
record(4, 2, "assistant", "旧答案"),
record(3, 2, "user", "当前问题"),
record(2, 1, "assistant", "上一轮答案"),
record(1, 1, "user", "上一轮问题")
)),
new ChatJsonSupport(new ObjectMapper())
);
ChatRuntimeContext context = new ChatRuntimeContext();
context.setSessionId(BigInteger.valueOf(1001));
context.getExt().put(ChatRuntimeExtKeys.REGENERATE_ROUND_ID, BigInteger.valueOf(2));
List<tech.easyflow.core.runtime.ChatRuntimeMessage> messages = listener.loadMessages(context, 10);
Assert.assertEquals(2, messages.size());
Assert.assertEquals("上一轮问题", messages.get(0).getContentText());
Assert.assertEquals("上一轮答案", messages.get(1).getContentText());
}
private static ChatMessageRecord record(long id, int roundId, String role, String text) {
ChatMessageRecord record = new ChatMessageRecord();
record.setId(BigInteger.valueOf(id));
record.setSessionId(BigInteger.valueOf(1001));
record.setRoundId(BigInteger.valueOf(roundId));
record.setSenderRole(role);
record.setContentType("TEXT");
record.setContentText(text);
record.setCreated(new Date(id));
return record;
}
private static class CapturingChatPersistDispatcher extends ChatPersistDispatcher {
private ChatSessionUpsertCommand captured;
private CapturingChatPersistDispatcher() {
super(null, null, null, null);
}
@Override
public ChatSessionSummary createOrTouchSession(ChatSessionUpsertCommand command) {
this.captured = command;
return new ChatSessionSummary();
}
}
private static class NoopChatRoundOperateService implements ChatRoundOperateService {
@Override
public ChatRoundRecord requireRegeneratableRound(BigInteger sessionId, BigInteger roundId) {
return null;
}
@Override
public List<tech.easyflow.chatlog.domain.dto.ChatMessageRecord> listVariants(BigInteger sessionId, BigInteger roundId) {
return List.of();
}
@Override
public tech.easyflow.chatlog.domain.dto.ChatMessageRecord selectVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId) {
return null;
}
}
private static class NoopChatRoundQueryService implements ChatRoundQueryService {
@Override
public ChatRoundRecord getLatestRound(BigInteger sessionId) {
return null;
}
@Override
public ChatRoundRecord getRound(BigInteger sessionId, BigInteger roundId) {
return null;
}
@Override
public List<tech.easyflow.chatlog.domain.dto.ChatMessageRecord> listRoundVariants(BigInteger sessionId, BigInteger roundId) {
return List.of();
}
@Override
public tech.easyflow.chatlog.domain.dto.ChatMessageRecord getRoundVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex) {
return null;
}
@Override
public boolean hasRounds(BigInteger sessionId) {
return false;
}
}
private static class NoopChatSessionQueryService implements ChatSessionQueryService {
@Override
public List<ChatSessionSummary> listSessions(BigInteger userId, BigInteger assistantId, tech.easyflow.chatlog.domain.query.ChatPageQuery query) {
return List.of();
}
@Override
public long countSessions(BigInteger userId, BigInteger assistantId) {
return 0;
}
@Override
public tech.easyflow.chatlog.domain.dto.ChatSessionPage pageSessions(BigInteger userId, BigInteger assistantId, tech.easyflow.chatlog.domain.query.ChatPageQuery query) {
return new tech.easyflow.chatlog.domain.dto.ChatSessionPage();
}
@Override
public ChatSessionSummary getSessionSummary(BigInteger sessionId) {
return null;
}
@Override
public tech.easyflow.chatlog.domain.dto.ChatHistoryPage pageMainlineMessages(BigInteger sessionId, tech.easyflow.chatlog.domain.query.ChatPageQuery query) {
return new tech.easyflow.chatlog.domain.dto.ChatHistoryPage();
}
@Override
public List<tech.easyflow.chatlog.domain.dto.ChatMessageRecord> listMainlineMessages(BigInteger sessionId) {
return List.of();
}
@Override
public List<tech.easyflow.chatlog.domain.dto.ChatMessageRecord> getRecentTail(BigInteger sessionId, int limit) {
return List.of();
}
}
private static class TailChatSessionQueryService extends NoopChatSessionQueryService {
private final List<ChatMessageRecord> records;
private TailChatSessionQueryService(List<ChatMessageRecord> records) {
this.records = records;
}
@Override
public List<ChatMessageRecord> getRecentTail(BigInteger sessionId, int limit) {
return records.subList(0, Math.min(records.size(), limit));
}
}
}