docs(conversation): bilingualize ConversationService comments (en/zh)

This commit is contained in:
matevip 2026-05-20 20:57:53 +08:00
parent 86c5d871bf
commit 82540a5fcb

View File

@ -45,7 +45,13 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 会话管理服务
* Conversation management service (会话管理服务).
*
* <p>Owns the full lifecycle of {@link ConversationEntity} and
* {@link MessageEntity} rows list / get-or-create / save / rename /
* pin / delete / compress / approval-state reconciliation and the
* cascade of side-tables that hang off a conversation (approvals,
* async tasks, channel sessions, attachment files, tool-result spill).
*
* @author MateClaw Team
*/
@ -79,18 +85,27 @@ public class ConversationService {
}
/**
* 获取用户的会话列表返回 VO包含 agentName/agentIcon/status
* List conversations for a user, returned as VOs that include
* {@code agentName} / {@code agentIcon} / {@code status}.
*
* <p>获取用户的会话列表返回 VO包含 agentName / agentIcon / status
*/
public List<ConversationVO> listConversations(String username) {
return listConversations(username, null);
}
/**
* 获取用户的会话列表按工作区过滤
* Workspace-scoped variant of {@link #listConversations(String)}.
*
* <p>获取用户的会话列表按工作区过滤
*/
public List<ConversationVO> listConversations(String username, Long workspaceId) {
// 同时返回当前用户的会话 定时任务system产生的会话
// 排除子会话委派产生的子会话不在侧边栏显示
// Return both the current user's conversations AND those created by
// scheduled jobs (owner=system). Child conversations spawned by
// delegation are excluded they don't belong in the sidebar.
//
// 同时返回当前用户的会话和定时任务system产生的会话
// 排除子会话委派产生的子会话不在侧边栏显示
LambdaQueryWrapper<ConversationEntity> wrapper = new LambdaQueryWrapper<ConversationEntity>()
.in(ConversationEntity::getUsername, username, SYSTEM_USER)
.isNull(ConversationEntity::getParentConversationId)
@ -105,7 +120,8 @@ public class ConversationService {
return List.of();
}
// 批量查询关联的 Agent 信息避免 N+1 查询
// Batch-load associated Agent rows to avoid N+1 queries.
// 批量查询关联的 Agent 信息避免 N+1 查询
List<Long> agentIds = entities.stream()
.filter(e -> e.getAgentId() != null)
.map(ConversationEntity::getAgentId)
@ -117,7 +133,8 @@ public class ConversationService {
: agentMapper.selectBatchIds(agentIds).stream()
.collect(Collectors.toMap(AgentEntity::getId, a -> a));
// 转换为 VO补充 agentName/agentIcon/status
// Map entities to VOs and enrich with agentName / agentIcon / status.
// 转换为 VO补充 agentName / agentIcon / status
return entities.stream()
.map(entity -> {
AgentEntity agent = entity.getAgentId() != null
@ -131,7 +148,9 @@ public class ConversationService {
}
/**
* 获取或创建会话向后兼容默认 workspace 1
* Get-or-create conversation (backward-compat overload, defaults to workspace 1).
*
* <p>获取或创建会话向后兼容默认 workspace 1
*/
@Transactional
public ConversationEntity getOrCreateConversation(String conversationId, Long agentId, String username) {
@ -139,7 +158,9 @@ public class ConversationService {
}
/**
* 获取或创建会话workspace 感知
* Workspace-aware get-or-create.
*
* <p>获取或创建会话workspace 感知
*/
@Transactional
public ConversationEntity getOrCreateConversation(String conversationId, Long agentId,
@ -163,7 +184,10 @@ public class ConversationService {
}
/**
* 创建子会话委派场景关联父会话 ID
* Create a child conversation (delegation scenario), linking it back to
* its parent via {@code parentConversationId}.
*
* <p>创建子会话委派场景关联父会话 ID
*/
@Transactional
public ConversationEntity createChildConversation(String childConversationId, Long agentId,
@ -177,11 +201,19 @@ public class ConversationService {
}
/**
* 获取或创建共享渠道会话
* <p>
* IM 渠道飞书/钉钉/企微等的会话需要在控制台中对登录用户可见
* 因此统一使用 system 作为 owner对于历史上已写成发送者昵称/open_id 的会话
* 这里会自动修正为 system避免控制台列表和消息接口因权限校验而不可见
* Get-or-create a shared channel conversation.
*
* <p>IM-channel (Feishu / DingTalk / WeCom / ) conversations must be
* visible to every logged-in user in the admin console, so the owner is
* uniformly set to {@code system}. For legacy rows whose owner was
* historically written as a sender nickname / {@code open_id}, this
* method silently rewrites it to {@code system} on read otherwise the
* console list and message endpoints would 403 those rows.
*
* <p>获取或创建共享渠道会话IM 渠道飞书 / 钉钉 / 企微等的会话需要在控制台中
* 对登录用户可见因此统一使用 {@code system} 作为 owner对于历史上已写成发送者
* 昵称 / open_id 的会话这里会自动修正为 {@code system}避免控制台列表和
* 消息接口因权限校验而不可见
*/
@Transactional
public ConversationEntity getOrCreateSharedConversation(String conversationId, Long agentId) {
@ -189,11 +221,14 @@ public class ConversationService {
}
/**
* 获取或创建共享渠道会话workspace 感知
* Workspace-aware get-or-create for shared channel conversations.
*
* <p>Delegates to the 5-arg overload with {@code null} model defaults
* preserves the legacy behavior for any caller that doesn't have an
* agent-level model to inherit from.
*
* <p>获取或创建共享渠道会话workspace 感知委托到 5 参重载model 默认值传
* {@code null}保留对不需要继承 agent 模型的调用方的旧行为
*/
@Transactional
public ConversationEntity getOrCreateSharedConversation(String conversationId, Long agentId, Long workspaceId) {
@ -245,13 +280,14 @@ public class ConversationService {
try {
conversationMapper.insert(conv);
} catch (org.springframework.dao.DuplicateKeyException e) {
// 并发插入另一个线程已创建回退到查询
// Concurrent insert: another thread won the race re-query
// and fall through to the owner-correction block below.
// 并发插入另一个线程已创建回退到查询继续走下面的 owner 修正逻辑
conv = conversationMapper.selectOne(new LambdaQueryWrapper<ConversationEntity>()
.eq(ConversationEntity::getConversationId, conversationId));
if (conv == null) {
throw new IllegalStateException("Conversation vanished after duplicate key: " + conversationId, e);
}
// 继续走下面的 owner 修正逻辑
}
}
@ -284,7 +320,10 @@ public class ConversationService {
}
/**
* 保存消息并更新会话统计
* Persist a message and update the conversation's aggregate counters
* ({@code messageCount}, {@code lastActiveTime}, {@code lastMessage}).
*
* <p>保存消息并更新会话统计
*/
@Transactional
public MessageEntity saveMessage(String conversationId, String role, String content) {
@ -327,21 +366,26 @@ public class ConversationService {
message.setCompletionTokens(completionTokens);
message.setRuntimeModel(runtimeModel);
message.setRuntimeProvider(runtimeProvider);
message.setMetadata(metadata != null ? metadata : "{}"); // 初始化为空对象
message.setMetadata(metadata != null ? metadata : "{}"); // Initialize as empty JSON object / 初始化为空对象
messageMapper.insert(message);
// 更新会话信息
// Update aggregate counters on the parent conversation row.
// 更新会话信息消息计数最后活跃时间最后一条摘要
ConversationEntity conv = conversationMapper.selectOne(new LambdaQueryWrapper<ConversationEntity>()
.eq(ConversationEntity::getConversationId, conversationId));
if (conv != null) {
conv.setMessageCount(conv.getMessageCount() + 1);
conv.setLastActiveTime(LocalDateTime.now());
String summary = summarizeMessage(content, parts);
// 用第一条用户消息作为会话标题
// Derive the conversation title from the first user message
// (only when the title is still the default "新对话").
// 用第一条用户消息作为会话标题
if ("user".equals(role) && "新对话".equals(conv.getTitle())) {
conv.setTitle(summary.length() > 20 ? summary.substring(0, 20) + "..." : summary);
}
// 保存最后一条 AI 回复摘要
// Keep a short preview of the latest assistant reply for the
// sidebar / list view (last_message column).
// 保存最后一条 AI 回复摘要
if ("assistant".equals(role)) {
conv.setLastMessage(summary.length() > 50 ? summary.substring(0, 50) + "..." : summary);
}
@ -351,7 +395,9 @@ public class ConversationService {
}
/**
* 更新消息的元数据toolCalls, plan, currentPhase
* Update a message's metadata JSON (toolCalls, plan, currentPhase, ).
*
* <p>更新消息的元数据toolCalls / plan / currentPhase
*/
@Transactional
public void updateMessageMetadata(Long messageId, String metadata) {
@ -362,7 +408,9 @@ public class ConversationService {
}
/**
* 重命名会话
* Rename a conversation.
*
* <p>重命名会话
*/
@Transactional
public void renameConversation(String conversationId, String title) {
@ -388,7 +436,9 @@ public class ConversationService {
}
/**
* 更新会话的流状态running / idle
* Update a conversation's stream status ({@code running} / {@code idle}).
*
* <p>更新会话的流状态running / idle
*/
@Transactional
public void updateStreamStatus(String conversationId, String streamStatus) {
@ -451,7 +501,10 @@ public class ConversationService {
}
/**
* 获取会话最后一条消息内容用于 rate limit 防护等场景
* Get the latest message preview text for a conversation used by
* rate-limit guards and similar duplicate-detection paths.
*
* <p>获取会话最后一条消息内容用于 rate limit 防护等场景
*/
public String getLastMessage(String conversationId) {
ConversationEntity conv = conversationMapper.selectOne(new LambdaQueryWrapper<ConversationEntity>()
@ -460,7 +513,9 @@ public class ConversationService {
}
/**
* 获取会话的消息数量
* Get a conversation's message count.
*
* <p>获取会话的消息数量
*/
public int getMessageCount(String conversationId) {
ConversationEntity conv = conversationMapper.selectOne(new LambdaQueryWrapper<ConversationEntity>()
@ -469,7 +524,9 @@ public class ConversationService {
}
/**
* 获取会话的消息历史
* Load the full message history for a conversation in chronological order.
*
* <p>获取会话的消息历史
*/
public List<MessageEntity> listMessages(String conversationId) {
return messageMapper.selectList(new LambdaQueryWrapper<MessageEntity>()
@ -503,8 +560,12 @@ public class ConversationService {
}
/**
* 加载最近 N 条消息倒序取出后翻转为正序
* 利用复合索引 (conversation_id, create_time) 高效分页
* Load the most recent N messages pulled DESC then reversed to ASC.
* Uses the composite index {@code (conversation_id, create_time)} for
* efficient tail pagination.
*
* <p>加载最近 N 条消息倒序取出后翻转为正序利用复合索引
* {@code (conversation_id, create_time)} 高效分页
*/
public List<MessageEntity> listRecentMessages(String conversationId, int lastN) {
List<MessageEntity> recent = messageMapper.selectList(
@ -518,7 +579,11 @@ public class ConversationService {
}
/**
* 分页加载指定 ID 之前的消息用于前端上拉加载更早消息
* Load a page of messages older than a given id used by the frontend
* pull-up infinite-scroll. Results are returned DESC; the caller is
* responsible for reversing if it needs ASC.
*
* <p>分页加载指定 ID 之前的消息用于前端上拉加载更早消息
* 返回倒序结果调用方需自行 reverse
*/
public List<MessageEntity> listMessagesBefore(String conversationId, Long beforeId, int limit) {
@ -534,7 +599,9 @@ public class ConversationService {
}
/**
* 查询会话消息总数
* Count the total number of messages in a conversation.
*
* <p>查询会话消息总数
*/
public long countMessages(String conversationId) {
return messageMapper.selectCount(
@ -748,7 +815,10 @@ public class ConversationService {
}
/**
* 清空会话消息同时清理附件文件
* Wipe all messages in a conversation and reset its aggregate counters,
* also cleaning any attachment files those messages produced.
*
* <p>清空会话消息同时清理附件文件
*/
@Transactional
public void clearMessages(String conversationId) {
@ -898,9 +968,13 @@ public class ConversationService {
}
/**
* 删除指定会话中所有审批占位 assistant 消息
* <p>
* replay 前调用确保 LLM 上下文中不包含任何审批相关文本
* Remove all approval-placeholder assistant messages from a conversation.
*
* <p>Called before replay so the LLM context contains no approval-related
* stub text that could confuse the next turn.
*
* <p>删除指定会话中所有审批占位 assistant 消息 replay 前调用
* 确保 LLM 上下文中不包含任何审批相关文本
*/
/**
* Reconcile persisted assistant-message state when one or more pending approvals
@ -1124,7 +1198,9 @@ public class ConversationService {
}
/**
* 检查会话是否存在
* Check whether a conversation row exists.
*
* <p>检查会话是否存在
*/
public boolean conversationExists(String conversationId) {
return conversationMapper.selectCount(
@ -1133,8 +1209,12 @@ public class ConversationService {
}
/**
* 校验用户是否拥有该会话
* 定时任务产生的会话username=system对所有登录用户可见
* Check whether a user owns the conversation, treating system-owned
* rows (e.g. from scheduled jobs / IM channels) as visible to every
* authenticated user.
*
* <p>校验用户是否拥有该会话定时任务产生的会话username = system
* 对所有登录用户可见
*/
public boolean isConversationOwner(String conversationId, String username) {
ConversationEntity conv = conversationMapper.selectOne(
@ -1147,7 +1227,9 @@ public class ConversationService {
}
/**
* 获取会话的持久化流状态
* Get the persisted stream status for a conversation.
*
* <p>获取会话的持久化流状态
*/
public String getStreamStatus(String conversationId) {
ConversationEntity conv = conversationMapper.selectOne(