feat: 完成管理端聊天工作台收口
- 新增管理端聊天工作台与会话级额外知识库持久化 - 补齐发布态聊天、历史会话只读判断与答案版本切换 - 新增 chat_round 热数据与主线消息读取支撑
This commit is contained in:
@@ -1,16 +1,22 @@
|
||||
package tech.easyflow.chatlog.service;
|
||||
|
||||
import org.slf4j.MDC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.chatlog.cache.ChatHotStateService;
|
||||
import tech.easyflow.chatlog.domain.command.ChatAppendMessageCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundSelectCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundUpsertCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatSessionUpsertCommand;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatSessionSummary;
|
||||
import tech.easyflow.chatlog.domain.event.ChatPersistEvent;
|
||||
import tech.easyflow.chatlog.domain.event.ChatPersistEventType;
|
||||
import tech.easyflow.chatlog.domain.event.payload.ChatSessionDeletePayload;
|
||||
import tech.easyflow.chatlog.domain.event.payload.ChatSessionRenamePayload;
|
||||
import tech.easyflow.chatlog.support.ChatJsonSupport;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
@@ -19,21 +25,26 @@ import java.util.UUID;
|
||||
@Service
|
||||
public class ChatPersistDispatcher {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ChatPersistDispatcher.class);
|
||||
|
||||
private final ChatHotStateService chatHotStateService;
|
||||
private final ChatPersistEventProducer eventProducer;
|
||||
private final ChatPersistMySqlApplyService mySqlApplyService;
|
||||
private final ChatJsonSupport chatJsonSupport;
|
||||
|
||||
public ChatPersistDispatcher(ChatHotStateService chatHotStateService,
|
||||
ChatPersistEventProducer eventProducer,
|
||||
ChatPersistMySqlApplyService mySqlApplyService,
|
||||
ChatJsonSupport chatJsonSupport) {
|
||||
this.chatHotStateService = chatHotStateService;
|
||||
this.eventProducer = eventProducer;
|
||||
this.mySqlApplyService = mySqlApplyService;
|
||||
this.chatJsonSupport = chatJsonSupport;
|
||||
}
|
||||
|
||||
public ChatSessionSummary createOrTouchSession(ChatSessionUpsertCommand command) {
|
||||
ChatSessionSummary summary = chatHotStateService.touchSession(command);
|
||||
eventProducer.send(buildEvent(
|
||||
ChatPersistEvent event = buildEvent(
|
||||
UUID.randomUUID().toString(),
|
||||
ChatPersistEventType.SESSION_PREPARED,
|
||||
command.getSessionId(),
|
||||
@@ -41,10 +52,43 @@ public class ChatPersistDispatcher {
|
||||
command.getAssistantId(),
|
||||
command.getOperateAt(),
|
||||
chatJsonSupport.toJson(command)
|
||||
));
|
||||
);
|
||||
persistImmediately(event);
|
||||
eventProducer.send(event);
|
||||
return summary;
|
||||
}
|
||||
|
||||
public ChatRoundRecord createOrTouchRound(ChatRoundUpsertCommand command) {
|
||||
ChatRoundRecord record = chatHotStateService.createOrTouchRound(command);
|
||||
ChatPersistEvent event = buildEvent(
|
||||
UUID.randomUUID().toString(),
|
||||
ChatPersistEventType.ROUND_UPSERTED,
|
||||
command.getSessionId(),
|
||||
BigInteger.ZERO,
|
||||
BigInteger.ZERO,
|
||||
command.getOperateAt(),
|
||||
chatJsonSupport.toJson(command)
|
||||
);
|
||||
persistImmediately(event);
|
||||
eventProducer.send(event);
|
||||
return record;
|
||||
}
|
||||
|
||||
public void selectRoundVariant(ChatRoundSelectCommand command) {
|
||||
chatHotStateService.selectVariant(command);
|
||||
ChatPersistEvent event = buildEvent(
|
||||
UUID.randomUUID().toString(),
|
||||
ChatPersistEventType.ROUND_VARIANT_SELECTED,
|
||||
command.getSessionId(),
|
||||
BigInteger.ZERO,
|
||||
BigInteger.ZERO,
|
||||
command.getOperateAt(),
|
||||
chatJsonSupport.toJson(command)
|
||||
);
|
||||
persistImmediately(event);
|
||||
eventProducer.send(event);
|
||||
}
|
||||
|
||||
public void appendUserMessage(ChatAppendMessageCommand command) {
|
||||
appendMessage(command, ChatPersistEventType.USER_MESSAGE_APPENDED);
|
||||
}
|
||||
@@ -96,7 +140,7 @@ public class ChatPersistDispatcher {
|
||||
|
||||
private void appendMessage(ChatAppendMessageCommand command, ChatPersistEventType eventType) {
|
||||
chatHotStateService.appendMessage(command);
|
||||
eventProducer.send(buildEvent(
|
||||
ChatPersistEvent event = buildEvent(
|
||||
eventId("message", command.getMessageId()),
|
||||
eventType,
|
||||
command.getSessionId(),
|
||||
@@ -104,7 +148,27 @@ public class ChatPersistDispatcher {
|
||||
command.getAssistantId(),
|
||||
command.getCreated(),
|
||||
chatJsonSupport.toJson(command)
|
||||
));
|
||||
);
|
||||
persistImmediately(event);
|
||||
eventProducer.send(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 先同步写入 MySQL,再发送异步事件,保证会话列表和版本切换读取有确定来源。
|
||||
*
|
||||
* @param event 持久化事件
|
||||
*/
|
||||
private void persistImmediately(ChatPersistEvent event) {
|
||||
try {
|
||||
mySqlApplyService.apply(java.util.List.of(event));
|
||||
} catch (RuntimeException ex) {
|
||||
log.error("聊天记录同步写入 MySQL 失败,eventId={}, eventType={}, sessionId={}",
|
||||
event == null ? null : event.getEventId(),
|
||||
event == null ? null : event.getEventType(),
|
||||
event == null ? null : event.getSessionId(),
|
||||
ex);
|
||||
throw new BusinessException("聊天记录持久化失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
private ChatPersistEvent buildEvent(String eventId,
|
||||
|
||||
@@ -3,6 +3,8 @@ package tech.easyflow.chatlog.service;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.chatlog.domain.command.ChatAppendMessageCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundSelectCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundUpsertCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatSessionSummaryCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatSessionUpsertCommand;
|
||||
import tech.easyflow.chatlog.domain.event.ChatPersistEvent;
|
||||
@@ -11,7 +13,9 @@ import tech.easyflow.chatlog.domain.event.payload.ChatSessionDeletePayload;
|
||||
import tech.easyflow.chatlog.domain.event.payload.ChatSessionRenamePayload;
|
||||
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.ChatConstants;
|
||||
import tech.easyflow.chatlog.support.ChatJsonSupport;
|
||||
|
||||
import java.math.BigInteger;
|
||||
@@ -30,15 +34,18 @@ public class ChatPersistMySqlApplyService {
|
||||
|
||||
private final MySqlChatSessionRepository sessionRepository;
|
||||
private final MySqlChatLogRepository logRepository;
|
||||
private final MySqlChatRoundRepository roundRepository;
|
||||
private final MySqlChatLogTableManager tableManager;
|
||||
private final ChatJsonSupport chatJsonSupport;
|
||||
|
||||
public ChatPersistMySqlApplyService(MySqlChatSessionRepository sessionRepository,
|
||||
MySqlChatLogRepository logRepository,
|
||||
MySqlChatRoundRepository roundRepository,
|
||||
MySqlChatLogTableManager tableManager,
|
||||
ChatJsonSupport chatJsonSupport) {
|
||||
this.sessionRepository = sessionRepository;
|
||||
this.logRepository = logRepository;
|
||||
this.roundRepository = roundRepository;
|
||||
this.tableManager = tableManager;
|
||||
this.chatJsonSupport = chatJsonSupport;
|
||||
}
|
||||
@@ -50,6 +57,8 @@ public class ChatPersistMySqlApplyService {
|
||||
}
|
||||
|
||||
Map<BigInteger, ChatSessionUpsertCommand> sessionUpserts = new LinkedHashMap<>();
|
||||
Map<BigInteger, ChatRoundUpsertCommand> roundUpserts = new LinkedHashMap<>();
|
||||
List<ChatRoundSelectCommand> roundSelections = new ArrayList<>();
|
||||
List<ChatAppendMessageCommand> appendCommands = new ArrayList<>();
|
||||
Map<BigInteger, ChatSessionSummaryCommand> summaryCommands = new LinkedHashMap<>();
|
||||
List<ChatSessionRenamePayload> renamePayloads = new ArrayList<>();
|
||||
@@ -67,6 +76,18 @@ public class ChatPersistMySqlApplyService {
|
||||
sessionUpserts.put(command.getSessionId(), command);
|
||||
}
|
||||
}
|
||||
case ROUND_UPSERTED -> {
|
||||
ChatRoundUpsertCommand command = chatJsonSupport.fromJson(event.getPayload(), ChatRoundUpsertCommand.class);
|
||||
if (command != null && command.getRoundId() != null) {
|
||||
roundUpserts.put(command.getRoundId(), command);
|
||||
}
|
||||
}
|
||||
case ROUND_VARIANT_SELECTED -> {
|
||||
ChatRoundSelectCommand command = chatJsonSupport.fromJson(event.getPayload(), ChatRoundSelectCommand.class);
|
||||
if (command != null && command.getRoundId() != null) {
|
||||
roundSelections.add(command);
|
||||
}
|
||||
}
|
||||
case USER_MESSAGE_APPENDED, ASSISTANT_MESSAGE_APPENDED -> {
|
||||
ChatAppendMessageCommand command = chatJsonSupport.fromJson(event.getPayload(), ChatAppendMessageCommand.class);
|
||||
if (command == null || command.getSessionId() == null || command.getMessageId() == null) {
|
||||
@@ -96,6 +117,9 @@ public class ChatPersistMySqlApplyService {
|
||||
if (!sessionUpserts.isEmpty()) {
|
||||
sessionRepository.createOrTouchBatch(new ArrayList<>(sessionUpserts.values()));
|
||||
}
|
||||
if (!roundUpserts.isEmpty()) {
|
||||
roundRepository.createOrTouchBatch(new ArrayList<>(roundUpserts.values()));
|
||||
}
|
||||
if (!months.isEmpty()) {
|
||||
for (YearMonth month : months) {
|
||||
tableManager.ensureMonthTable(month);
|
||||
@@ -113,6 +137,9 @@ public class ChatPersistMySqlApplyService {
|
||||
}
|
||||
sessionRepository.updateSummaries(new ArrayList<>(summaryCommands.values()));
|
||||
}
|
||||
if (!roundSelections.isEmpty()) {
|
||||
roundRepository.selectVariants(roundSelections);
|
||||
}
|
||||
if (!renamePayloads.isEmpty()) {
|
||||
sessionRepository.renameSessions(renamePayloads);
|
||||
}
|
||||
@@ -127,15 +154,26 @@ public class ChatPersistMySqlApplyService {
|
||||
ChatSessionSummaryCommand created = new ChatSessionSummaryCommand();
|
||||
created.setSessionId(command.getSessionId());
|
||||
created.setUserId(command.getUserId());
|
||||
created.setLastMessageAt(null);
|
||||
created.setAccessAt(null);
|
||||
created.setModifiedAt(null);
|
||||
created.setMessageIncrement(0);
|
||||
return created;
|
||||
});
|
||||
summary.setMessageIncrement(summary.getMessageIncrement() + 1);
|
||||
if (summary.getLastMessageAt() == null || !command.getCreated().before(summary.getLastMessageAt())) {
|
||||
if (ChatConstants.MESSAGE_KIND_ASSISTANT_VARIANT.equals(command.getMessageKind())
|
||||
&& command.getVariantIndex() != null
|
||||
&& command.getVariantIndex() > 1) {
|
||||
summary.setMessageIncrement(Math.max(summary.getMessageIncrement() - 1, 0));
|
||||
}
|
||||
Date commandCreated = defaultDate(command.getCreated());
|
||||
if (summary.getLastMessageAt() == null || !commandCreated.before(summary.getLastMessageAt())) {
|
||||
summary.setLastSenderId(command.getSenderId());
|
||||
summary.setLastSenderName(command.getSenderName());
|
||||
summary.setLastMessagePreview(trimPreview(command.getContentText()));
|
||||
summary.setLastMessageAt(command.getCreated());
|
||||
summary.setLastMessageAt(commandCreated);
|
||||
summary.setAccessAt(commandCreated);
|
||||
summary.setModifiedAt(commandCreated);
|
||||
summary.setOperatorId(command.getCreatedBy());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package tech.easyflow.chatlog.service;
|
||||
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundSelectCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundUpsertCommand;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
|
||||
|
||||
/**
|
||||
* 聊天轮次写服务。
|
||||
*/
|
||||
public interface ChatRoundCommandService {
|
||||
|
||||
/**
|
||||
* 创建或更新轮次聚合。
|
||||
*
|
||||
* @param command 轮次命令
|
||||
* @return 最新轮次记录
|
||||
*/
|
||||
ChatRoundRecord createOrTouchRound(ChatRoundUpsertCommand command);
|
||||
|
||||
/**
|
||||
* 切换轮次当前选中的答案版本。
|
||||
*
|
||||
* @param command 切换命令
|
||||
*/
|
||||
void selectVariant(ChatRoundSelectCommand command);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package tech.easyflow.chatlog.service;
|
||||
|
||||
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 聊天轮次业务操作服务。
|
||||
*/
|
||||
public interface ChatRoundOperateService {
|
||||
|
||||
/**
|
||||
* 校验并返回允许重答的轮次。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @param roundId 轮次 ID
|
||||
* @return 轮次记录
|
||||
*/
|
||||
ChatRoundRecord requireRegeneratableRound(BigInteger sessionId, BigInteger roundId);
|
||||
|
||||
/**
|
||||
* 查询轮次下所有答案版本。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @param roundId 轮次 ID
|
||||
* @return 答案版本列表
|
||||
*/
|
||||
List<ChatMessageRecord> listVariants(BigInteger sessionId, BigInteger roundId);
|
||||
|
||||
/**
|
||||
* 切换指定轮次当前选中的答案版本。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @param roundId 轮次 ID
|
||||
* @param variantIndex 目标版本序号
|
||||
* @param operatorId 操作人
|
||||
* @return 选中的答案消息
|
||||
*/
|
||||
ChatMessageRecord selectVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package tech.easyflow.chatlog.service;
|
||||
|
||||
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 聊天轮次读服务。
|
||||
*/
|
||||
public interface ChatRoundQueryService {
|
||||
|
||||
/**
|
||||
* 查询会话最新轮次。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @return 最新轮次
|
||||
*/
|
||||
ChatRoundRecord getLatestRound(BigInteger sessionId);
|
||||
|
||||
/**
|
||||
* 查询指定轮次。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @param roundId 轮次 ID
|
||||
* @return 轮次记录
|
||||
*/
|
||||
ChatRoundRecord getRound(BigInteger sessionId, BigInteger roundId);
|
||||
|
||||
/**
|
||||
* 查询轮次下所有助手答案版本。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @param roundId 轮次 ID
|
||||
* @return 答案版本列表
|
||||
*/
|
||||
List<ChatMessageRecord> listRoundVariants(BigInteger sessionId, BigInteger roundId);
|
||||
|
||||
/**
|
||||
* 查询轮次下指定答案版本。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @param roundId 轮次 ID
|
||||
* @param variantIndex 答案版本序号
|
||||
* @return 答案版本记录
|
||||
*/
|
||||
ChatMessageRecord getRoundVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex);
|
||||
|
||||
/**
|
||||
* 判断会话是否已经启用轮次模型。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @return 是否存在轮次
|
||||
*/
|
||||
boolean hasRounds(BigInteger sessionId);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package tech.easyflow.chatlog.service;
|
||||
|
||||
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
|
||||
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;
|
||||
@@ -18,5 +19,22 @@ public interface ChatSessionQueryService {
|
||||
|
||||
ChatSessionSummary getSessionSummary(BigInteger sessionId);
|
||||
|
||||
/**
|
||||
* 分页查询当前会话的主线可见消息。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @param query 分页参数
|
||||
* @return 主线消息分页
|
||||
*/
|
||||
ChatHistoryPage pageMainlineMessages(BigInteger sessionId, ChatPageQuery query);
|
||||
|
||||
/**
|
||||
* 查询当前会话的全部主线可见消息。
|
||||
*
|
||||
* @param sessionId 会话 ID
|
||||
* @return 主线消息列表
|
||||
*/
|
||||
List<ChatMessageRecord> listMainlineMessages(BigInteger sessionId);
|
||||
|
||||
List<ChatMessageRecord> getRecentTail(BigInteger sessionId, int limit);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package tech.easyflow.chatlog.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundSelectCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundUpsertCommand;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
|
||||
import tech.easyflow.chatlog.service.ChatPersistDispatcher;
|
||||
import tech.easyflow.chatlog.service.ChatRoundCommandService;
|
||||
|
||||
/**
|
||||
* 聊天轮次写服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class ChatRoundCommandServiceImpl implements ChatRoundCommandService {
|
||||
|
||||
private final ChatPersistDispatcher chatPersistDispatcher;
|
||||
|
||||
public ChatRoundCommandServiceImpl(ChatPersistDispatcher chatPersistDispatcher) {
|
||||
this.chatPersistDispatcher = chatPersistDispatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatRoundRecord createOrTouchRound(ChatRoundUpsertCommand command) {
|
||||
return chatPersistDispatcher.createOrTouchRound(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectVariant(ChatRoundSelectCommand command) {
|
||||
chatPersistDispatcher.selectRoundVariant(command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package tech.easyflow.chatlog.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundSelectCommand;
|
||||
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.ChatRoundOperateService;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 聊天轮次业务操作服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class ChatRoundOperateServiceImpl implements ChatRoundOperateService {
|
||||
|
||||
private final ChatRoundQueryService chatRoundQueryService;
|
||||
private final ChatRoundCommandService chatRoundCommandService;
|
||||
|
||||
public ChatRoundOperateServiceImpl(ChatRoundQueryService chatRoundQueryService,
|
||||
ChatRoundCommandService chatRoundCommandService) {
|
||||
this.chatRoundQueryService = chatRoundQueryService;
|
||||
this.chatRoundCommandService = chatRoundCommandService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatRoundRecord requireRegeneratableRound(BigInteger sessionId, BigInteger roundId) {
|
||||
ChatRoundRecord round = requireLatestRound(sessionId, roundId);
|
||||
if (round.getSelectedAssistantMessageId() == null || round.getSelectedVariantIndex() == null
|
||||
|| round.getSelectedVariantIndex() <= 0) {
|
||||
throw new BusinessException("当前轮次暂无可重答的回答");
|
||||
}
|
||||
return round;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChatMessageRecord> listVariants(BigInteger sessionId, BigInteger roundId) {
|
||||
ChatRoundRecord round = chatRoundQueryService.getRound(sessionId, roundId);
|
||||
if (round == null) {
|
||||
throw new BusinessException("轮次不存在");
|
||||
}
|
||||
ChatRoundRecord latestRound = chatRoundQueryService.getLatestRound(sessionId);
|
||||
boolean switchable = latestRound != null
|
||||
&& Objects.equals(latestRound.getId(), round.getId())
|
||||
&& !ChatConstants.ROUND_STATUS_LOCKED.equalsIgnoreCase(round.getStatus());
|
||||
List<ChatMessageRecord> variants = new ArrayList<>(chatRoundQueryService.listRoundVariants(sessionId, roundId));
|
||||
for (ChatMessageRecord variant : variants) {
|
||||
variant.setVariantCount(round.getVariantCount());
|
||||
variant.setSelectedVariantIndex(round.getSelectedVariantIndex());
|
||||
variant.setSwitchable(switchable);
|
||||
}
|
||||
return variants;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatMessageRecord selectVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId) {
|
||||
ChatRoundRecord round = requireLatestRound(sessionId, roundId);
|
||||
if (variantIndex == null || variantIndex <= 0) {
|
||||
throw new BusinessException("目标答案版本无效");
|
||||
}
|
||||
ChatMessageRecord selected = chatRoundQueryService.getRoundVariant(sessionId, roundId, variantIndex);
|
||||
if (selected == null) {
|
||||
throw new BusinessException("目标答案版本不存在");
|
||||
}
|
||||
|
||||
ChatRoundSelectCommand command = new ChatRoundSelectCommand();
|
||||
command.setSessionId(sessionId);
|
||||
command.setRoundId(roundId);
|
||||
command.setSelectedVariantIndex(variantIndex);
|
||||
command.setSelectedAssistantMessageId(selected.getId());
|
||||
command.setSelectedAssistantMessage(selected);
|
||||
command.setOperatorId(operatorId);
|
||||
chatRoundCommandService.selectVariant(command);
|
||||
selected.setSelectedVariantIndex(variantIndex);
|
||||
selected.setVariantCount(round.getVariantCount());
|
||||
selected.setSwitchable(true);
|
||||
return selected;
|
||||
}
|
||||
|
||||
private ChatRoundRecord requireLatestRound(BigInteger sessionId, BigInteger roundId) {
|
||||
ChatRoundRecord round = chatRoundQueryService.getRound(sessionId, roundId);
|
||||
if (round == null) {
|
||||
throw new BusinessException("轮次不存在");
|
||||
}
|
||||
ChatRoundRecord latestRound = chatRoundQueryService.getLatestRound(sessionId);
|
||||
if (latestRound == null || !Objects.equals(latestRound.getId(), round.getId())) {
|
||||
throw new BusinessException("当前轮次已有后续对话,不支持切换答案版本");
|
||||
}
|
||||
if (ChatConstants.ROUND_STATUS_LOCKED.equalsIgnoreCase(round.getStatus())) {
|
||||
throw new BusinessException("当前轮次已有后续对话,不支持切换答案版本");
|
||||
}
|
||||
return round;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package tech.easyflow.chatlog.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.chatlog.cache.ChatHotStateService;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
|
||||
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.service.ChatRoundQueryService;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 聊天轮次读服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class ChatRoundQueryServiceImpl implements ChatRoundQueryService {
|
||||
|
||||
private final MySqlChatRoundRepository roundRepository;
|
||||
private final MySqlChatLogRepository logRepository;
|
||||
private final MySqlChatLogTableManager tableManager;
|
||||
private final ChatHotStateService chatHotStateService;
|
||||
|
||||
public ChatRoundQueryServiceImpl(MySqlChatRoundRepository roundRepository,
|
||||
MySqlChatLogRepository logRepository,
|
||||
MySqlChatLogTableManager tableManager,
|
||||
ChatHotStateService chatHotStateService) {
|
||||
this.roundRepository = roundRepository;
|
||||
this.logRepository = logRepository;
|
||||
this.tableManager = tableManager;
|
||||
this.chatHotStateService = chatHotStateService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatRoundRecord getLatestRound(BigInteger sessionId) {
|
||||
ChatRoundRecord cached = chatHotStateService.getLatestRound(sessionId);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
ChatRoundRecord record = roundRepository.findLatestRound(sessionId);
|
||||
if (record != null) {
|
||||
chatHotStateService.cacheRound(record);
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatRoundRecord getRound(BigInteger sessionId, BigInteger roundId) {
|
||||
ChatRoundRecord cached = chatHotStateService.getRound(sessionId, roundId);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
ChatRoundRecord record = roundRepository.findRound(sessionId, roundId);
|
||||
if (record != null) {
|
||||
chatHotStateService.cacheRound(record);
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChatMessageRecord> listRoundVariants(BigInteger sessionId, BigInteger roundId) {
|
||||
return logRepository.listRoundVariants(sessionId, roundId, tableManager.listRecentExistingMonths(3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatMessageRecord getRoundVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex) {
|
||||
return logRepository.findRoundVariant(sessionId, roundId, variantIndex, tableManager.listRecentExistingMonths(3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRounds(BigInteger sessionId) {
|
||||
return roundRepository.existsRounds(sessionId);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package tech.easyflow.chatlog.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.chatlog.cache.ChatHotStateService;
|
||||
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;
|
||||
@@ -12,7 +13,12 @@ import tech.easyflow.chatlog.repository.mysql.MySqlChatSessionRepository;
|
||||
import tech.easyflow.chatlog.service.ChatSessionQueryService;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class ChatSessionQueryServiceImpl implements ChatSessionQueryService {
|
||||
@@ -34,21 +40,7 @@ public class ChatSessionQueryServiceImpl implements ChatSessionQueryService {
|
||||
|
||||
@Override
|
||||
public List<ChatSessionSummary> listSessions(BigInteger userId, BigInteger assistantId, ChatPageQuery query) {
|
||||
if (assistantId == null) {
|
||||
List<BigInteger> sessionIds = chatHotStateService.listSessionIds(userId, query.getOffset(), query.getPageSize());
|
||||
if (!sessionIds.isEmpty()) {
|
||||
List<ChatSessionSummary> cached = chatHotStateService.getSessionSummaries(sessionIds);
|
||||
if (cached.size() == sessionIds.size()) {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
List<ChatSessionSummary> sessions = sessionRepository.listSessions(userId, null, query);
|
||||
chatHotStateService.cacheSessionSummaries(sessions);
|
||||
return sessions;
|
||||
}
|
||||
List<ChatSessionSummary> sessions = sessionRepository.listSessions(userId, assistantId, query);
|
||||
chatHotStateService.cacheSessionSummaries(sessions);
|
||||
return sessions;
|
||||
return sessionRepository.listSessions(userId, assistantId, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,21 +54,6 @@ public class ChatSessionQueryServiceImpl implements ChatSessionQueryService {
|
||||
page.setPageNumber(query.getPageNumber());
|
||||
page.setPageSize(query.getPageSize());
|
||||
|
||||
if (assistantId == null && chatHotStateService.hasSessionIndex(userId)) {
|
||||
List<BigInteger> sessionIds = chatHotStateService.listSessionIds(userId, query.getOffset(), query.getPageSize());
|
||||
if (sessionIds.isEmpty()) {
|
||||
page.setTotal(chatHotStateService.countSessions(userId));
|
||||
page.setRecords(List.of());
|
||||
return page;
|
||||
}
|
||||
List<ChatSessionSummary> cached = chatHotStateService.getSessionSummaries(sessionIds);
|
||||
if (cached.size() == sessionIds.size()) {
|
||||
page.setTotal(chatHotStateService.countSessions(userId));
|
||||
page.setRecords(cached);
|
||||
return page;
|
||||
}
|
||||
}
|
||||
|
||||
page.setTotal(sessionRepository.countSessions(userId, assistantId));
|
||||
page.setRecords(listSessions(userId, assistantId, query));
|
||||
return page;
|
||||
@@ -95,14 +72,74 @@ public class ChatSessionQueryServiceImpl implements ChatSessionQueryService {
|
||||
return summary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatHistoryPage pageMainlineMessages(BigInteger sessionId, ChatPageQuery query) {
|
||||
ChatHistoryPage page = new ChatHistoryPage();
|
||||
page.setPageNumber(query.getPageNumber());
|
||||
page.setPageSize(query.getPageSize());
|
||||
ChatSessionSummary summary = getSessionSummary(sessionId);
|
||||
long total = summary == null || summary.getMessageCount() == null ? 0L : summary.getMessageCount();
|
||||
List<ChatMessageRecord> records = logRepository.listMainlineMessages(
|
||||
sessionId,
|
||||
tableManager.listRecentExistingMonths(3),
|
||||
query.getOffset(),
|
||||
Math.toIntExact(query.getPageSize())
|
||||
);
|
||||
page.setRecords(records);
|
||||
page.setTotal(Math.max(total, query.getOffset() + records.size()));
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChatMessageRecord> listMainlineMessages(BigInteger sessionId) {
|
||||
return logRepository.listMainlineMessages(sessionId, tableManager.listRecentExistingMonths(3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChatMessageRecord> getRecentTail(BigInteger sessionId, int limit) {
|
||||
List<ChatMessageRecord> cached = chatHotStateService.getSessionTail(sessionId);
|
||||
if (cached != null) {
|
||||
if (cached != null && isTailReliable(cached)) {
|
||||
return cached.subList(0, Math.min(limit, cached.size()));
|
||||
}
|
||||
List<ChatMessageRecord> records = logRepository.listRecentTail(sessionId, tableManager.listRecentExistingMonths(3), limit);
|
||||
chatHotStateService.setSessionTail(sessionId, records);
|
||||
return records;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 Redis tail 是否符合当前主线版本语义,防止过期选中版本把可见回答过滤掉。
|
||||
*
|
||||
* @param records Redis tail 消息
|
||||
* @return true 表示可直接使用缓存
|
||||
*/
|
||||
private boolean isTailReliable(List<ChatMessageRecord> records) {
|
||||
Map<BigInteger, Integer> selectedVariantByRound = new LinkedHashMap<>();
|
||||
Map<BigInteger, Set<Integer>> assistantVariantsByRound = new LinkedHashMap<>();
|
||||
for (ChatMessageRecord record : records) {
|
||||
if (record == null || record.getRoundId() == null) {
|
||||
continue;
|
||||
}
|
||||
Integer selectedVariantIndex = record.getSelectedVariantIndex();
|
||||
if (selectedVariantIndex != null && selectedVariantIndex > 0) {
|
||||
Integer previous = selectedVariantByRound.putIfAbsent(record.getRoundId(), selectedVariantIndex);
|
||||
if (previous != null && !Objects.equals(previous, selectedVariantIndex)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ("assistant".equalsIgnoreCase(record.getSenderRole())
|
||||
&& record.getVariantIndex() != null
|
||||
&& record.getVariantIndex() > 0) {
|
||||
assistantVariantsByRound
|
||||
.computeIfAbsent(record.getRoundId(), key -> new LinkedHashSet<>())
|
||||
.add(record.getVariantIndex());
|
||||
}
|
||||
}
|
||||
for (Map.Entry<BigInteger, Integer> entry : selectedVariantByRound.entrySet()) {
|
||||
Set<Integer> visibleVariants = assistantVariantsByRound.get(entry.getKey());
|
||||
if (visibleVariants != null && !visibleVariants.isEmpty() && !visibleVariants.contains(entry.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,19 @@ import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.easyflow.chatlog.domain.command.ChatAppendMessageCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatRoundUpsertCommand;
|
||||
import tech.easyflow.chatlog.domain.command.ChatSessionUpsertCommand;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatSessionExtPayload;
|
||||
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
|
||||
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.ChatConstants;
|
||||
import tech.easyflow.chatlog.support.ChatJsonSupport;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.core.runtime.ChatRuntimeExtKeys;
|
||||
import tech.easyflow.core.runtime.ChatRuntimeHistoryPayloadHelper;
|
||||
import tech.easyflow.core.runtime.ChatRuntimeContext;
|
||||
import tech.easyflow.core.runtime.ChatRuntimeListener;
|
||||
@@ -27,12 +35,21 @@ public class ChatlogRuntimeListener implements ChatRuntimeListener {
|
||||
private final SnowFlakeIDKeyGenerator idGenerator = new SnowFlakeIDKeyGenerator();
|
||||
|
||||
private final ChatPersistDispatcher chatPersistDispatcher;
|
||||
private final ChatRoundOperateService chatRoundOperateService;
|
||||
private final ChatRoundQueryService chatRoundQueryService;
|
||||
private final ChatSessionQueryService chatSessionQueryService;
|
||||
private final ChatJsonSupport chatJsonSupport;
|
||||
|
||||
public ChatlogRuntimeListener(ChatPersistDispatcher chatPersistDispatcher,
|
||||
ChatSessionQueryService chatSessionQueryService) {
|
||||
ChatRoundOperateService chatRoundOperateService,
|
||||
ChatRoundQueryService chatRoundQueryService,
|
||||
ChatSessionQueryService chatSessionQueryService,
|
||||
ChatJsonSupport chatJsonSupport) {
|
||||
this.chatPersistDispatcher = chatPersistDispatcher;
|
||||
this.chatRoundOperateService = chatRoundOperateService;
|
||||
this.chatRoundQueryService = chatRoundQueryService;
|
||||
this.chatSessionQueryService = chatSessionQueryService;
|
||||
this.chatJsonSupport = chatJsonSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,6 +65,7 @@ public class ChatlogRuntimeListener implements ChatRuntimeListener {
|
||||
command.setAssistantCode(context.getAssistantCode());
|
||||
command.setAssistantName(context.getAssistantName());
|
||||
command.setTitle(context.getSessionTitle());
|
||||
command.setExtJson(resolveExtJson(context));
|
||||
command.setOperatorId(defaultNumber(context.getUserId()));
|
||||
chatPersistDispatcher.createOrTouchSession(command);
|
||||
} catch (RuntimeException ex) {
|
||||
@@ -58,6 +76,9 @@ public class ChatlogRuntimeListener implements ChatRuntimeListener {
|
||||
@Override
|
||||
public void onUserMessage(ChatRuntimeContext context, ChatRuntimeMessage message) {
|
||||
try {
|
||||
if (prepareRoundContext(context, message)) {
|
||||
return;
|
||||
}
|
||||
chatPersistDispatcher.appendUserMessage(toAppendCommand(context, message));
|
||||
} catch (RuntimeException ex) {
|
||||
throw persistFailed(ex);
|
||||
@@ -67,7 +88,36 @@ public class ChatlogRuntimeListener implements ChatRuntimeListener {
|
||||
@Override
|
||||
public void onAssistantCompleted(ChatRuntimeContext context, ChatRuntimeMessage message) {
|
||||
try {
|
||||
applyAssistantRoundMetadata(context, message);
|
||||
chatPersistDispatcher.appendAssistantMessage(toAppendCommand(context, message));
|
||||
chatPersistDispatcher.createOrTouchRound(buildAssistantCompletedRoundCommand(context, message));
|
||||
} catch (RuntimeException ex) {
|
||||
throw persistFailed(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChatFailed(ChatRuntimeContext context, Throwable throwable) {
|
||||
try {
|
||||
BigInteger roundId = resolveNumber(context, ChatRuntimeExtKeys.CURRENT_ROUND_ID);
|
||||
if (context == null || context.getSessionId() == null || roundId == null) {
|
||||
return;
|
||||
}
|
||||
ChatRoundRecord currentRound = chatRoundQueryService.getRound(context.getSessionId(), roundId);
|
||||
if (currentRound == null) {
|
||||
return;
|
||||
}
|
||||
ChatRoundUpsertCommand command = new ChatRoundUpsertCommand();
|
||||
command.setRoundId(currentRound.getId());
|
||||
command.setSessionId(currentRound.getSessionId());
|
||||
command.setRoundNo(currentRound.getRoundNo());
|
||||
command.setUserMessageId(currentRound.getUserMessageId());
|
||||
command.setSelectedAssistantMessageId(currentRound.getSelectedAssistantMessageId());
|
||||
command.setSelectedVariantIndex(currentRound.getSelectedVariantIndex());
|
||||
command.setVariantCount(currentRound.getVariantCount());
|
||||
command.setStatus(ChatConstants.ROUND_STATUS_READY);
|
||||
command.setOperatorId(defaultNumber(context.getUserId()));
|
||||
chatPersistDispatcher.createOrTouchRound(command);
|
||||
} catch (RuntimeException ex) {
|
||||
throw persistFailed(ex);
|
||||
}
|
||||
@@ -78,7 +128,15 @@ public class ChatlogRuntimeListener implements ChatRuntimeListener {
|
||||
if (context == null || context.getSessionId() == null || limit <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ChatMessageRecord> records = new ArrayList<>(chatSessionQueryService.getRecentTail(context.getSessionId(), limit));
|
||||
BigInteger regenerateRoundId = resolveNumber(context, ChatRuntimeExtKeys.REGENERATE_ROUND_ID);
|
||||
int queryLimit = regenerateRoundId == null ? limit : limit + 4;
|
||||
List<ChatMessageRecord> records = new ArrayList<>(chatSessionQueryService.getRecentTail(context.getSessionId(), queryLimit));
|
||||
if (regenerateRoundId != null) {
|
||||
records.removeIf(record -> regenerateRoundId.equals(record.getRoundId()));
|
||||
if (records.size() > limit) {
|
||||
records = new ArrayList<>(records.subList(0, limit));
|
||||
}
|
||||
}
|
||||
Collections.reverse(records);
|
||||
List<ChatRuntimeMessage> messages = new ArrayList<>(records.size());
|
||||
for (ChatMessageRecord record : records) {
|
||||
@@ -118,11 +176,127 @@ public class ChatlogRuntimeListener implements ChatRuntimeListener {
|
||||
command.setContentType(message.getContentType());
|
||||
command.setContentText(message.getContentText());
|
||||
command.setContentPayload(message.getContentPayload());
|
||||
command.setRoundId(message.getRoundId());
|
||||
command.setRoundNo(message.getRoundNo());
|
||||
command.setMessageKind(message.getMessageKind());
|
||||
command.setVariantIndex(message.getVariantIndex());
|
||||
command.setCreatedBy(defaultNumber(context.getUserId()));
|
||||
command.setCreated(message.getCreatedAt());
|
||||
return command;
|
||||
}
|
||||
|
||||
private boolean prepareRoundContext(ChatRuntimeContext context, ChatRuntimeMessage message) {
|
||||
if (context == null || message == null || context.getSessionId() == null) {
|
||||
return false;
|
||||
}
|
||||
BigInteger regenerateRoundId = resolveNumber(context, ChatRuntimeExtKeys.REGENERATE_ROUND_ID);
|
||||
if (regenerateRoundId != null) {
|
||||
ChatRoundRecord round = chatRoundOperateService.requireRegeneratableRound(context.getSessionId(), regenerateRoundId);
|
||||
context.getExt().put(ChatRuntimeExtKeys.CURRENT_ROUND_ID, round.getId());
|
||||
context.getExt().put(ChatRuntimeExtKeys.CURRENT_ROUND_NO, round.getRoundNo());
|
||||
context.getExt().put(ChatRuntimeExtKeys.CURRENT_VARIANT_INDEX, Math.max(round.getVariantCount() + 1, 1));
|
||||
ChatRoundUpsertCommand command = new ChatRoundUpsertCommand();
|
||||
command.setRoundId(round.getId());
|
||||
command.setSessionId(round.getSessionId());
|
||||
command.setRoundNo(round.getRoundNo());
|
||||
command.setUserMessageId(round.getUserMessageId());
|
||||
command.setSelectedAssistantMessageId(round.getSelectedAssistantMessageId());
|
||||
command.setSelectedVariantIndex(round.getSelectedVariantIndex());
|
||||
command.setVariantCount(round.getVariantCount());
|
||||
command.setStatus(ChatConstants.ROUND_STATUS_ANSWERING);
|
||||
command.setOperatorId(defaultNumber(context.getUserId()));
|
||||
chatPersistDispatcher.createOrTouchRound(command);
|
||||
return true;
|
||||
}
|
||||
ChatRoundRecord latestRound = chatRoundQueryService.getLatestRound(context.getSessionId());
|
||||
if (latestRound != null && latestRound.getId() != null
|
||||
&& !ChatConstants.ROUND_STATUS_LOCKED.equalsIgnoreCase(latestRound.getStatus())) {
|
||||
ChatRoundUpsertCommand lockCommand = new ChatRoundUpsertCommand();
|
||||
lockCommand.setRoundId(latestRound.getId());
|
||||
lockCommand.setSessionId(latestRound.getSessionId());
|
||||
lockCommand.setRoundNo(latestRound.getRoundNo());
|
||||
lockCommand.setUserMessageId(latestRound.getUserMessageId());
|
||||
lockCommand.setSelectedAssistantMessageId(latestRound.getSelectedAssistantMessageId());
|
||||
lockCommand.setSelectedVariantIndex(latestRound.getSelectedVariantIndex());
|
||||
lockCommand.setVariantCount(latestRound.getVariantCount());
|
||||
lockCommand.setStatus(ChatConstants.ROUND_STATUS_LOCKED);
|
||||
lockCommand.setOperatorId(defaultNumber(context.getUserId()));
|
||||
chatPersistDispatcher.createOrTouchRound(lockCommand);
|
||||
}
|
||||
BigInteger roundId = BigInteger.valueOf(idGenerator.nextId());
|
||||
int roundNo = latestRound == null || latestRound.getRoundNo() == null ? 1 : latestRound.getRoundNo() + 1;
|
||||
if (message.getMessageId() == null) {
|
||||
message.setMessageId(BigInteger.valueOf(idGenerator.nextId()));
|
||||
}
|
||||
context.getExt().put(ChatRuntimeExtKeys.CURRENT_ROUND_ID, roundId);
|
||||
context.getExt().put(ChatRuntimeExtKeys.CURRENT_ROUND_NO, roundNo);
|
||||
context.getExt().put(ChatRuntimeExtKeys.CURRENT_VARIANT_INDEX, 1);
|
||||
message.setRoundId(roundId);
|
||||
message.setRoundNo(roundNo);
|
||||
message.setMessageKind(ChatConstants.MESSAGE_KIND_USER_PROMPT);
|
||||
message.setVariantIndex(null);
|
||||
ChatRoundUpsertCommand command = new ChatRoundUpsertCommand();
|
||||
command.setRoundId(roundId);
|
||||
command.setSessionId(context.getSessionId());
|
||||
command.setRoundNo(roundNo);
|
||||
command.setUserMessageId(message.getMessageId());
|
||||
command.setSelectedVariantIndex(0);
|
||||
command.setVariantCount(0);
|
||||
command.setStatus(ChatConstants.ROUND_STATUS_ANSWERING);
|
||||
command.setOperatorId(defaultNumber(context.getUserId()));
|
||||
chatPersistDispatcher.createOrTouchRound(command);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void applyAssistantRoundMetadata(ChatRuntimeContext context, ChatRuntimeMessage message) {
|
||||
if (message.getMessageId() == null) {
|
||||
message.setMessageId(BigInteger.valueOf(idGenerator.nextId()));
|
||||
}
|
||||
message.setRoundId(resolveNumber(context, ChatRuntimeExtKeys.CURRENT_ROUND_ID));
|
||||
message.setRoundNo(resolveInteger(context, ChatRuntimeExtKeys.CURRENT_ROUND_NO));
|
||||
message.setVariantIndex(resolveInteger(context, ChatRuntimeExtKeys.CURRENT_VARIANT_INDEX));
|
||||
message.setMessageKind(ChatConstants.MESSAGE_KIND_ASSISTANT_VARIANT);
|
||||
}
|
||||
|
||||
private ChatRoundUpsertCommand buildAssistantCompletedRoundCommand(ChatRuntimeContext context, ChatRuntimeMessage message) {
|
||||
ChatRoundUpsertCommand command = new ChatRoundUpsertCommand();
|
||||
command.setRoundId(message.getRoundId());
|
||||
command.setSessionId(context.getSessionId());
|
||||
command.setRoundNo(message.getRoundNo());
|
||||
ChatRoundRecord existing = chatRoundQueryService.getRound(context.getSessionId(), message.getRoundId());
|
||||
if (existing != null) {
|
||||
command.setUserMessageId(existing.getUserMessageId());
|
||||
}
|
||||
command.setSelectedAssistantMessageId(message.getMessageId());
|
||||
command.setSelectedVariantIndex(message.getVariantIndex());
|
||||
command.setVariantCount(message.getVariantIndex());
|
||||
command.setStatus(ChatConstants.ROUND_STATUS_READY);
|
||||
command.setOperatorId(defaultNumber(context.getUserId()));
|
||||
return command;
|
||||
}
|
||||
|
||||
private BigInteger resolveNumber(ChatRuntimeContext context, String key) {
|
||||
if (context == null || context.getExt() == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = context.getExt().get(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return new BigInteger(String.valueOf(value));
|
||||
}
|
||||
|
||||
private Integer resolveInteger(ChatRuntimeContext context, String key) {
|
||||
if (context == null || context.getExt() == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = context.getExt().get(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return Integer.parseInt(String.valueOf(value));
|
||||
}
|
||||
|
||||
private BigInteger defaultNumber(BigInteger value) {
|
||||
return value == null ? BigInteger.ZERO : value;
|
||||
}
|
||||
@@ -133,4 +307,27 @@ public class ChatlogRuntimeListener implements ChatRuntimeListener {
|
||||
}
|
||||
return new BusinessException("聊天记录持久化失败,请稍后重试");
|
||||
}
|
||||
|
||||
private String resolveExtJson(ChatRuntimeContext context) {
|
||||
if (context == null || context.getExt() == null || context.getExt().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (!context.getExt().containsKey(ChatRuntimeExtKeys.EXTRA_KNOWLEDGE_IDS)) {
|
||||
return null;
|
||||
}
|
||||
Object rawExtraKnowledgeIds = context.getExt().get(ChatRuntimeExtKeys.EXTRA_KNOWLEDGE_IDS);
|
||||
if (!(rawExtraKnowledgeIds instanceof List<?> rawList)) {
|
||||
return null;
|
||||
}
|
||||
List<BigInteger> extraKnowledgeIds = new ArrayList<>();
|
||||
for (Object item : rawList) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
extraKnowledgeIds.add(new BigInteger(String.valueOf(item)));
|
||||
}
|
||||
ChatSessionExtPayload payload = new ChatSessionExtPayload();
|
||||
payload.setExtraKnowledgeIds(extraKnowledgeIds);
|
||||
return chatJsonSupport.toJson(payload);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user