package com.xly.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xly.config.RedisChatMemoryStore;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 会话(conversation)元数据管理 —— 支持「多条命名会话/用户」。
*
*
每用户一个会话列表存 Redis Hash {@code chat:convs:{userId}}(field=convId,value=JSON{id,title,updatedAt});
* 消息本体存在 {@link RedisChatMemoryStore}({@code chat:mem:{convId}})。标题取自该会话的首条用户消息。
*/
@Service
public class ConversationService {
private static final String CONVS_KEY = "chat:convs:";
private final StringRedisTemplate redis;
private final RedisChatMemoryStore memoryStore;
private final ObjectMapper mapper;
private final LedgerService ledger;
private final StateService state;
public ConversationService(StringRedisTemplate redis, RedisChatMemoryStore memoryStore,
ObjectMapper mapper, LedgerService ledger, StateService state) {
this.redis = redis;
this.memoryStore = memoryStore;
this.mapper = mapper;
this.ledger = ledger;
this.state = state;
}
/** 新建一个空会话,返回 convId。 */
public String create(String userId) {
String convId = "c-" + System.currentTimeMillis() + "-" + Integer.toHexString((int) (Math.random() * 0xFFFFF));
writeMeta(userId, convId, "新会话");
return convId;
}
/** 每次对话时调用:会话不存在则建、标题空则用首条消息命名,并刷新 updatedAt。 */
public void touch(String userId, String convId, String firstMsg) {
Object existing = redis.opsForHash().get(CONVS_KEY + userId, convId);
String title = null;
if (existing != null) {
try {
title = mapper.readTree(existing.toString()).path("title").asText(null);
} catch (Exception ignore) {
}
}
if (title == null || title.isBlank() || "新会话".equals(title)) {
title = deriveTitle(firstMsg);
}
writeMeta(userId, convId, title);
}
private void writeMeta(String userId, String convId, String title) {
try {
Map m = new LinkedHashMap<>();
m.put("id", convId);
m.put("title", title);
m.put("updatedAt", System.currentTimeMillis());
redis.opsForHash().put(CONVS_KEY + userId, convId, mapper.writeValueAsString(m));
} catch (Exception ignore) {
}
}
/** 该用户的会话列表,按最近更新倒序。 */
public List