From 82540a5fcb0c79ee581e0e9fbf68b4d82147ac59 Mon Sep 17 00:00:00 2001 From: matevip Date: Wed, 20 May 2026 20:57:53 +0800 Subject: [PATCH] docs(conversation): bilingualize ConversationService comments (en/zh) --- .../conversation/ConversationService.java | 164 +++++++++++++----- 1 file changed, 123 insertions(+), 41 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/workspace/conversation/ConversationService.java b/mateclaw-server/src/main/java/vip/mate/workspace/conversation/ConversationService.java index 4fc8106a..1790e4c5 100644 --- a/mateclaw-server/src/main/java/vip/mate/workspace/conversation/ConversationService.java +++ b/mateclaw-server/src/main/java/vip/mate/workspace/conversation/ConversationService.java @@ -45,7 +45,13 @@ import java.util.stream.Collectors; import java.util.stream.Stream; /** - * 会话管理服务 + * Conversation management service (会话管理服务). + * + *

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}. + * + *

获取用户的会话列表(返回 VO,包含 agentName / agentIcon / status)。 */ public List listConversations(String username) { return listConversations(username, null); } /** - * 获取用户的会话列表(按工作区过滤) + * Workspace-scoped variant of {@link #listConversations(String)}. + * + *

获取用户的会话列表(按工作区过滤)。 */ public List 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 wrapper = new LambdaQueryWrapper() .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 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). + * + *

获取或创建会话(向后兼容,默认 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. + * + *

获取或创建会话(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}. + * + *

创建子会话(委派场景),关联父会话 ID。 */ @Transactional public ConversationEntity createChildConversation(String childConversationId, Long agentId, @@ -177,11 +201,19 @@ public class ConversationService { } /** - * 获取或创建共享渠道会话。 - *

- * IM 渠道(飞书/钉钉/企微等)的会话需要在控制台中对登录用户可见, - * 因此统一使用 system 作为 owner。对于历史上已写成发送者昵称/open_id 的会话, - * 这里会自动修正为 system,避免控制台列表和消息接口因权限校验而不可见。 + * Get-or-create a shared channel conversation. + * + *

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. + * + *

获取或创建共享渠道会话。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. * *

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. + * + *

获取或创建共享渠道会话(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() .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}). + * + *

保存消息并更新会话统计。 */ @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() .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, …). + * + *

更新消息的元数据(toolCalls / plan / currentPhase 等)。 */ @Transactional public void updateMessageMetadata(Long messageId, String metadata) { @@ -362,7 +408,9 @@ public class ConversationService { } /** - * 重命名会话 + * Rename a conversation. + * + *

重命名会话。 */ @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}). + * + *

更新会话的流状态(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. + * + *

获取会话最后一条消息内容(用于 rate limit 防护等场景)。 */ public String getLastMessage(String conversationId) { ConversationEntity conv = conversationMapper.selectOne(new LambdaQueryWrapper() @@ -460,7 +513,9 @@ public class ConversationService { } /** - * 获取会话的消息数量 + * Get a conversation's message count. + * + *

获取会话的消息数量。 */ public int getMessageCount(String conversationId) { ConversationEntity conv = conversationMapper.selectOne(new LambdaQueryWrapper() @@ -469,7 +524,9 @@ public class ConversationService { } /** - * 获取会话的消息历史 + * Load the full message history for a conversation in chronological order. + * + *

获取会话的消息历史。 */ public List listMessages(String conversationId) { return messageMapper.selectList(new LambdaQueryWrapper() @@ -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. + * + *

加载最近 N 条消息(倒序取出后翻转为正序);利用复合索引 + * {@code (conversation_id, create_time)} 高效分页。 */ public List listRecentMessages(String conversationId, int lastN) { List 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. + * + *

分页加载指定 ID 之前的消息(用于前端上拉加载更早消息); * 返回倒序结果,调用方需自行 reverse。 */ public List listMessagesBefore(String conversationId, Long beforeId, int limit) { @@ -534,7 +599,9 @@ public class ConversationService { } /** - * 查询会话消息总数。 + * Count the total number of messages in a conversation. + * + *

查询会话消息总数。 */ 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. + * + *

清空会话消息(同时清理附件文件)。 */ @Transactional public void clearMessages(String conversationId) { @@ -898,9 +968,13 @@ public class ConversationService { } /** - * 删除指定会话中所有审批占位 assistant 消息 - *

- * 在 replay 前调用,确保 LLM 上下文中不包含任何审批相关文本。 + * Remove all approval-placeholder assistant messages from a conversation. + * + *

Called before replay so the LLM context contains no approval-related + * stub text that could confuse the next turn. + * + *

删除指定会话中所有审批占位 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. + * + *

检查会话是否存在。 */ 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. + * + *

校验用户是否拥有该会话。定时任务产生的会话(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. + * + *

获取会话的持久化流状态。 */ public String getStreamStatus(String conversationId) { ConversationEntity conv = conversationMapper.selectOne(