package com.vincent.rsf.server.ai.service.impl;
|
|
import com.vincent.rsf.server.ai.model.AiChatMessage;
|
import com.vincent.rsf.server.ai.model.AiChatSession;
|
import com.vincent.rsf.server.ai.service.AiRuntimeConfigService;
|
import com.vincent.rsf.server.ai.service.AiSessionService;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.Resource;
|
import java.util.ArrayList;
|
import java.util.Comparator;
|
import java.util.Date;
|
import java.util.List;
|
import java.util.UUID;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentMap;
|
|
@Service
|
public class AiSessionServiceImpl implements AiSessionService {
|
|
private static final ConcurrentMap<String, List<AiChatSession>> LOCAL_SESSION_CACHE = new ConcurrentHashMap<>();
|
private static final ConcurrentMap<String, List<AiChatMessage>> LOCAL_MESSAGE_CACHE = new ConcurrentHashMap<>();
|
private static final ConcurrentMap<String, String> LOCAL_STOP_CACHE = new ConcurrentHashMap<>();
|
|
@Resource
|
private AiRuntimeConfigService aiRuntimeConfigService;
|
|
@Override
|
public synchronized List<AiChatSession> listSessions(Long tenantId, Long userId) {
|
List<AiChatSession> sessions = getSessions(tenantId, userId);
|
sessions.sort(Comparator.comparing(AiChatSession::getUpdateTime, Comparator.nullsLast(Date::compareTo)).reversed());
|
return sessions;
|
}
|
|
@Override
|
public synchronized AiChatSession createSession(Long tenantId, Long userId, String title, String modelCode) {
|
List<AiChatSession> sessions = getSessions(tenantId, userId);
|
Date now = new Date();
|
AiChatSession session = new AiChatSession()
|
.setId(UUID.randomUUID().toString().replace("-", ""))
|
.setTitle(resolveTitle(title, sessions.size() + 1))
|
.setModelCode(resolveModelCode(modelCode))
|
.setCreateTime(now)
|
.setUpdateTime(now)
|
.setLastMessageAt(now);
|
sessions.add(0, session);
|
saveSessions(tenantId, userId, sessions);
|
saveMessages(session.getId(), new ArrayList<>());
|
return session;
|
}
|
|
@Override
|
public synchronized AiChatSession ensureSession(Long tenantId, Long userId, String sessionId, String modelCode) {
|
AiChatSession session = getSession(tenantId, userId, sessionId);
|
if (session == null) {
|
return createSession(tenantId, userId, null, modelCode);
|
}
|
if (modelCode != null && !modelCode.trim().isEmpty() && !modelCode.equals(session.getModelCode())) {
|
session.setModelCode(modelCode);
|
session.setUpdateTime(new Date());
|
refreshSession(tenantId, userId, session);
|
}
|
return session;
|
}
|
|
@Override
|
public synchronized AiChatSession getSession(Long tenantId, Long userId, String sessionId) {
|
if (sessionId == null || sessionId.trim().isEmpty()) {
|
return null;
|
}
|
for (AiChatSession session : getSessions(tenantId, userId)) {
|
if (sessionId.equals(session.getId())) {
|
return session;
|
}
|
}
|
return null;
|
}
|
|
@Override
|
public synchronized AiChatSession renameSession(Long tenantId, Long userId, String sessionId, String title) {
|
AiChatSession session = getSession(tenantId, userId, sessionId);
|
if (session == null) {
|
return null;
|
}
|
session.setTitle(resolveTitle(title, 1));
|
session.setUpdateTime(new Date());
|
refreshSession(tenantId, userId, session);
|
return session;
|
}
|
|
@Override
|
public synchronized void removeSession(Long tenantId, Long userId, String sessionId) {
|
List<AiChatSession> sessions = getSessions(tenantId, userId);
|
sessions.removeIf(session -> sessionId.equals(session.getId()));
|
saveSessions(tenantId, userId, sessions);
|
LOCAL_MESSAGE_CACHE.remove(sessionId);
|
LOCAL_STOP_CACHE.remove(sessionId);
|
}
|
|
@Override
|
public synchronized List<AiChatMessage> listMessages(Long tenantId, Long userId, String sessionId) {
|
AiChatSession session = getSession(tenantId, userId, sessionId);
|
if (session == null) {
|
return new ArrayList<>();
|
}
|
return getMessages(sessionId);
|
}
|
|
@Override
|
public synchronized List<AiChatMessage> listContextMessages(Long tenantId, Long userId, String sessionId, int maxCount) {
|
List<AiChatMessage> messages = listMessages(tenantId, userId, sessionId);
|
if (messages.size() <= maxCount) {
|
return messages;
|
}
|
return new ArrayList<>(messages.subList(messages.size() - maxCount, messages.size()));
|
}
|
|
@Override
|
public synchronized AiChatMessage appendMessage(Long tenantId, Long userId, String sessionId, String role, String content, String modelCode) {
|
AiChatSession session = getSession(tenantId, userId, sessionId);
|
if (session == null) {
|
return null;
|
}
|
List<AiChatMessage> messages = getMessages(sessionId);
|
AiChatMessage message = new AiChatMessage()
|
.setId(UUID.randomUUID().toString().replace("-", ""))
|
.setSessionId(sessionId)
|
.setRole(role)
|
.setContent(content)
|
.setModelCode(resolveModelCode(modelCode))
|
.setCreateTime(new Date());
|
messages.add(message);
|
saveMessages(sessionId, messages);
|
session.setLastMessage(buildPreview(content));
|
session.setLastMessageAt(message.getCreateTime());
|
session.setUpdateTime(message.getCreateTime());
|
if (modelCode != null && !modelCode.trim().isEmpty()) {
|
session.setModelCode(modelCode);
|
}
|
if ((session.getTitle() == null || session.getTitle().startsWith("新对话")) && "user".equalsIgnoreCase(role)) {
|
session.setTitle(buildPreview(content));
|
}
|
refreshSession(tenantId, userId, session);
|
return message;
|
}
|
|
@Override
|
public void clearStopFlag(String sessionId) {
|
LOCAL_STOP_CACHE.remove(sessionId);
|
}
|
|
@Override
|
public void requestStop(String sessionId) {
|
LOCAL_STOP_CACHE.put(sessionId, "1");
|
}
|
|
@Override
|
public boolean isStopRequested(String sessionId) {
|
String stopFlag = LOCAL_STOP_CACHE.get(sessionId);
|
return "1".equals(stopFlag);
|
}
|
|
private List<AiChatSession> getSessions(Long tenantId, Long userId) {
|
String ownerKey = buildOwnerKey(tenantId, userId);
|
List<AiChatSession> sessions = LOCAL_SESSION_CACHE.get(ownerKey);
|
return sessions == null ? new ArrayList<>() : new ArrayList<>(sessions);
|
}
|
|
private void saveSessions(Long tenantId, Long userId, List<AiChatSession> sessions) {
|
String ownerKey = buildOwnerKey(tenantId, userId);
|
List<AiChatSession> cachedSessions = new ArrayList<>(sessions);
|
LOCAL_SESSION_CACHE.put(ownerKey, cachedSessions);
|
}
|
|
private List<AiChatMessage> getMessages(String sessionId) {
|
List<AiChatMessage> messages = LOCAL_MESSAGE_CACHE.get(sessionId);
|
return messages == null ? new ArrayList<>() : new ArrayList<>(messages);
|
}
|
|
private void saveMessages(String sessionId, List<AiChatMessage> messages) {
|
List<AiChatMessage> cachedMessages = new ArrayList<>(messages);
|
LOCAL_MESSAGE_CACHE.put(sessionId, cachedMessages);
|
}
|
|
private void refreshSession(Long tenantId, Long userId, AiChatSession target) {
|
List<AiChatSession> sessions = getSessions(tenantId, userId);
|
for (int i = 0; i < sessions.size(); i++) {
|
if (target.getId().equals(sessions.get(i).getId())) {
|
sessions.set(i, target);
|
saveSessions(tenantId, userId, sessions);
|
return;
|
}
|
}
|
sessions.add(target);
|
saveSessions(tenantId, userId, sessions);
|
}
|
|
private String buildOwnerKey(Long tenantId, Long userId) {
|
return String.valueOf(tenantId) + ":" + String.valueOf(userId);
|
}
|
|
private String resolveModelCode(String modelCode) {
|
return modelCode == null || modelCode.trim().isEmpty() ? aiRuntimeConfigService.resolveDefaultModelCode() : modelCode;
|
}
|
|
private String resolveTitle(String title, int index) {
|
if (title == null || title.trim().isEmpty()) {
|
return "新对话 " + index;
|
}
|
return buildPreview(title);
|
}
|
|
private String buildPreview(String content) {
|
if (content == null || content.trim().isEmpty()) {
|
return "新对话";
|
}
|
String normalized = content.replace("\r", " ").replace("\n", " ").trim();
|
return normalized.length() > 24 ? normalized.substring(0, 24) : normalized;
|
}
|
|
}
|