feat: 支持聊天多版本答案切换

- 为管理端、公共聊天和用户中心补充回答变体查询与切换能力

- 支持基于指定轮次重新生成并同步前后端多版本状态

- 保留 application.yml 与本地截图文件为未提交状态
This commit is contained in:
2026-05-14 21:23:20 +08:00
parent da58077d59
commit 1a6ea64e80
23 changed files with 2625 additions and 122 deletions

View File

@@ -0,0 +1,65 @@
package tech.easyflow.chatlog.domain.dto;
import java.io.Serializable;
import java.math.BigInteger;
/**
* 轮次答案版本视图。
*/
public class ChatRoundVariantView implements Serializable {
private BigInteger roundId;
private Integer roundNo;
private Integer variantCount;
private Integer selectedVariantIndex;
private Boolean switchable;
private ChatMessageRecord selectedMessage;
public BigInteger getRoundId() {
return roundId;
}
public void setRoundId(BigInteger roundId) {
this.roundId = roundId;
}
public Integer getRoundNo() {
return roundNo;
}
public void setRoundNo(Integer roundNo) {
this.roundNo = roundNo;
}
public Integer getVariantCount() {
return variantCount;
}
public void setVariantCount(Integer variantCount) {
this.variantCount = variantCount;
}
public Integer getSelectedVariantIndex() {
return selectedVariantIndex;
}
public void setSelectedVariantIndex(Integer selectedVariantIndex) {
this.selectedVariantIndex = selectedVariantIndex;
}
public Boolean getSwitchable() {
return switchable;
}
public void setSwitchable(Boolean switchable) {
this.switchable = switchable;
}
public ChatMessageRecord getSelectedMessage() {
return selectedMessage;
}
public void setSelectedMessage(ChatMessageRecord selectedMessage) {
this.selectedMessage = selectedMessage;
}
}

View File

@@ -1,12 +1,14 @@
package tech.easyflow.chatlog.service;
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.domain.query.ChatSessionFilterQuery;
import java.math.BigInteger;
import java.util.List;
public interface ChatHistoryManageService {
@@ -25,4 +27,12 @@ public interface ChatHistoryManageService {
void renameUserSession(BigInteger userId, BigInteger sessionId, String title, BigInteger operatorId);
void deleteUserSession(BigInteger userId, BigInteger sessionId, BigInteger operatorId);
List<ChatMessageRecord> listUserRoundVariants(BigInteger userId, BigInteger sessionId, BigInteger roundId);
ChatMessageRecord selectUserRoundVariant(BigInteger userId, BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId);
List<ChatMessageRecord> listAdminRoundVariants(BigInteger sessionId, BigInteger roundId);
ChatMessageRecord selectAdminRoundVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId);
}

View File

@@ -1,10 +1,16 @@
package tech.easyflow.chatlog.service;
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
import tech.easyflow.chatlog.domain.dto.PublicChatSessionRestoreResult;
import java.math.BigInteger;
import java.util.List;
public interface PublicChatSessionRestoreService {
PublicChatSessionRestoreResult restoreSession(BigInteger userId, BigInteger assistantId, BigInteger sessionId, Integer limit);
List<ChatMessageRecord> listVariants(BigInteger userId, BigInteger assistantId, BigInteger sessionId, BigInteger roundId);
ChatMessageRecord selectVariant(BigInteger userId, BigInteger assistantId, BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId);
}

View File

@@ -11,6 +11,7 @@ import tech.easyflow.chatlog.domain.query.ChatSessionFilterQuery;
import tech.easyflow.chatlog.repository.analyticaldb.ChatAnalyticalDBRepository;
import tech.easyflow.chatlog.service.ChatHistoryManageService;
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.common.web.exceptions.BusinessException;
@@ -23,15 +24,18 @@ public class ChatHistoryManageServiceImpl implements ChatHistoryManageService {
private final ChatSessionQueryService chatSessionQueryService;
private final ChatSessionCommandService chatSessionCommandService;
private final ChatHistoryQueryService chatHistoryQueryService;
private final ChatRoundOperateService chatRoundOperateService;
private final ChatAnalyticalDBRepository chatAnalyticalDBRepository;
public ChatHistoryManageServiceImpl(ChatSessionQueryService chatSessionQueryService,
ChatSessionCommandService chatSessionCommandService,
ChatHistoryQueryService chatHistoryQueryService,
ChatRoundOperateService chatRoundOperateService,
ChatAnalyticalDBRepository chatAnalyticalDBRepository) {
this.chatSessionQueryService = chatSessionQueryService;
this.chatSessionCommandService = chatSessionCommandService;
this.chatHistoryQueryService = chatHistoryQueryService;
this.chatRoundOperateService = chatRoundOperateService;
this.chatAnalyticalDBRepository = chatAnalyticalDBRepository;
}
@@ -68,13 +72,21 @@ public class ChatHistoryManageServiceImpl implements ChatHistoryManageService {
@Override
public ChatHistoryPage queryUserMessages(BigInteger userId, BigInteger sessionId, ChatPageQuery query) {
getUserSession(userId, sessionId);
ChatSessionSummary summary = getUserSession(userId, sessionId);
ChatHistoryPage firstPage = restoreRecentMessages(summary, query);
if (firstPage != null) {
return firstPage;
}
return chatHistoryQueryService.queryHistoryMessages(sessionId, query);
}
@Override
public ChatHistoryPage queryAdminMessages(BigInteger sessionId, ChatPageQuery query) {
getAdminSession(sessionId);
ChatSessionSummary summary = getAdminSession(sessionId);
ChatHistoryPage firstPage = restoreRecentMessages(summary, query);
if (firstPage != null) {
return firstPage;
}
return chatHistoryQueryService.queryHistoryMessages(sessionId, query);
}
@@ -92,4 +104,48 @@ public class ChatHistoryManageServiceImpl implements ChatHistoryManageService {
getUserSession(userId, sessionId);
chatSessionCommandService.deleteSession(sessionId, userId, operatorId);
}
@Override
public java.util.List<ChatMessageRecord> listUserRoundVariants(BigInteger userId, BigInteger sessionId, BigInteger roundId) {
getUserSession(userId, sessionId);
return chatRoundOperateService.listVariants(sessionId, roundId);
}
@Override
public ChatMessageRecord selectUserRoundVariant(BigInteger userId, BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId) {
getUserSession(userId, sessionId);
return chatRoundOperateService.selectVariant(sessionId, roundId, variantIndex, operatorId);
}
@Override
public java.util.List<ChatMessageRecord> listAdminRoundVariants(BigInteger sessionId, BigInteger roundId) {
getAdminSession(sessionId);
return chatRoundOperateService.listVariants(sessionId, roundId);
}
@Override
public ChatMessageRecord selectAdminRoundVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId) {
getAdminSession(sessionId);
return chatRoundOperateService.selectVariant(sessionId, roundId, variantIndex, operatorId);
}
private ChatHistoryPage restoreRecentMessages(ChatSessionSummary summary, ChatPageQuery query) {
if (summary == null || query == null || query.getPageNumber() != 1) {
return null;
}
java.util.List<ChatMessageRecord> records = chatSessionQueryService.getRecentTail(
summary.getId(),
Math.toIntExact(query.getPageSize())
);
if (records == null || records.isEmpty()) {
return null;
}
ChatHistoryPage page = new ChatHistoryPage();
page.setPageNumber(query.getPageNumber());
page.setPageSize(query.getPageSize());
page.setRecords(records);
long total = summary.getMessageCount() == null ? 0L : summary.getMessageCount();
page.setTotal(Math.max(total, records.size()));
return page;
}
}

View File

@@ -5,8 +5,10 @@ import tech.easyflow.chatlog.config.ChatCacheProperties;
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
import tech.easyflow.chatlog.domain.dto.ChatSessionSummary;
import tech.easyflow.chatlog.domain.dto.PublicChatSessionRestoreResult;
import tech.easyflow.chatlog.service.ChatRoundOperateService;
import tech.easyflow.chatlog.service.ChatSessionQueryService;
import tech.easyflow.chatlog.service.PublicChatSessionRestoreService;
import tech.easyflow.common.web.exceptions.BusinessException;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -18,11 +20,14 @@ import java.util.Objects;
public class PublicChatSessionRestoreServiceImpl implements PublicChatSessionRestoreService {
private final ChatSessionQueryService chatSessionQueryService;
private final ChatRoundOperateService chatRoundOperateService;
private final ChatCacheProperties chatCacheProperties;
public PublicChatSessionRestoreServiceImpl(ChatSessionQueryService chatSessionQueryService,
ChatRoundOperateService chatRoundOperateService,
ChatCacheProperties chatCacheProperties) {
this.chatSessionQueryService = chatSessionQueryService;
this.chatRoundOperateService = chatRoundOperateService;
this.chatCacheProperties = chatCacheProperties;
}
@@ -54,6 +59,18 @@ public class PublicChatSessionRestoreServiceImpl implements PublicChatSessionRes
return result;
}
@Override
public List<ChatMessageRecord> listVariants(BigInteger userId, BigInteger assistantId, BigInteger sessionId, BigInteger roundId) {
requireOwnedSession(userId, assistantId, sessionId);
return chatRoundOperateService.listVariants(sessionId, roundId);
}
@Override
public ChatMessageRecord selectVariant(BigInteger userId, BigInteger assistantId, BigInteger sessionId, BigInteger roundId, Integer variantIndex, BigInteger operatorId) {
requireOwnedSession(userId, assistantId, sessionId);
return chatRoundOperateService.selectVariant(sessionId, roundId, variantIndex, operatorId);
}
private int resolveLimit(Integer limit) {
int defaultLimit = Math.max(chatCacheProperties.getTailSize(), 1);
if (limit == null || limit <= 0) {
@@ -61,4 +78,15 @@ public class PublicChatSessionRestoreServiceImpl implements PublicChatSessionRes
}
return Math.min(limit, defaultLimit);
}
private ChatSessionSummary requireOwnedSession(BigInteger userId, BigInteger assistantId, BigInteger sessionId) {
ChatSessionSummary summary = chatSessionQueryService.getSessionSummary(sessionId);
if (summary == null || Integer.valueOf(1).equals(summary.getIsDeleted())) {
throw new BusinessException("会话不存在");
}
if (!Objects.equals(summary.getUserId(), userId) || !Objects.equals(summary.getAssistantId(), assistantId)) {
throw new BusinessException("无权访问该会话");
}
return summary;
}
}