refactor: merge messageThunk.v2.ts into messageThunk.ts

Remove the confusing V2 naming from message thunk functions to avoid conflicts with the upcoming real V2 data refactoring.

  Changes:
  - Inline V2 function implementations directly into messageThunk.ts
  - Replace V2 function calls with direct dbService calls
  - Remove messageThunk.v2.ts file
  - Remove misleading "V2 DATA&UI REFACTORING" header comments

  The V2 suffix was originally added for agent session support, not for a data layer refactoring. This cleanup clears the naming space for the actual V2 refactoring work.
This commit is contained in:
fullex 2026-01-03 16:36:53 +08:00
parent 078cf39313
commit ca2b0ac28d
4 changed files with 232 additions and 288 deletions

View File

@ -81,9 +81,7 @@ const AgentSessionMessages: React.FC<Props> = ({ agentId, sessionId }) => {
// Listen for send message events to auto-scroll to bottom
useEffect(() => {
const unsubscribes = [
EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, scrollToBottom)
]
const unsubscribes = [EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, scrollToBottom)]
return () => unsubscribes.forEach((unsub) => unsub())
}, [scrollToBottom])

View File

@ -162,7 +162,7 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic, o
const { message: clearMessage } = getUserMessage({ assistant, topic, type: 'clear' })
dispatch(newMessagesActions.addMessage({ topicId: topic.id, message: clearMessage }))
await saveMessageAndBlocksToDB(clearMessage, [])
await saveMessageAndBlocksToDB(topic.id, clearMessage, [])
scrollToBottom()
} finally {

View File

@ -19,6 +19,7 @@ import { AiSdkToChunkAdapter } from '@renderer/aiCore/chunk/AiSdkToChunkAdapter'
import { AgentApiClient } from '@renderer/api/agent'
import db from '@renderer/databases'
import { fetchMessagesSummary, transformMessagesAndFetch } from '@renderer/services/ApiService'
import { dbService } from '@renderer/services/db'
import { DbService } from '@renderer/services/db/DbService'
import FileManager from '@renderer/services/FileManager'
import { BlockManager } from '@renderer/services/messageStreaming/BlockManager'
@ -57,18 +58,18 @@ import { mutate } from 'swr'
import type { AppDispatch, RootState } from '../index'
import { removeManyBlocks, updateOneBlock, upsertManyBlocks, upsertOneBlock } from '../messageBlock'
import { newMessagesActions, selectMessagesForTopic } from '../newMessage'
import {
bulkAddBlocksV2,
clearMessagesFromDBV2,
deleteMessageFromDBV2,
deleteMessagesFromDBV2,
loadTopicMessagesThunkV2,
saveMessageAndBlocksToDBV2,
updateBlocksV2,
updateFileCountV2,
updateMessageV2,
updateSingleBlockV2
} from './messageThunk.v2'
// import {
// bulkAddBlocksV2,
// clearMessagesFromDBV2,
// deleteMessageFromDBV2,
// deleteMessagesFromDBV2,
// loadTopicMessagesThunkV2,
// saveMessageAndBlocksToDBV2,
// updateBlocksV2,
// updateFileCountV2,
// updateMessageV2,
// updateSingleBlockV2
// } from './messageThunk.v2'
const logger = loggerService.withContext('MessageThunk')
@ -363,9 +364,9 @@ const createAgentMessageStream = async (
return createSSEReadableStream(response.body, signal)
}
// TODO: 后续可以将db操作移到Listener Middleware中
export const saveMessageAndBlocksToDB = async (message: Message, blocks: MessageBlock[], messageIndex: number = -1) => {
return saveMessageAndBlocksToDBV2(message.topicId, message, blocks, messageIndex)
}
// export const saveMessageAndBlocksToDB = async (message: Message, blocks: MessageBlock[], messageIndex: number = -1) => {
// return saveMessageAndBlocksToDBV2(message.topicId, message, blocks, messageIndex)
// }
const updateExistingMessageAndBlocksInDB = async (
updatedMessage: Partial<Message> & Pick<Message, 'id' | 'topicId'>,
@ -374,7 +375,7 @@ const updateExistingMessageAndBlocksInDB = async (
try {
// Always update blocks if provided
if (updatedBlocks.length > 0) {
await updateBlocksV2(updatedBlocks)
await updateBlocks(updatedBlocks)
}
// Check if there are message properties to update beyond id and topicId
@ -386,7 +387,7 @@ const updateExistingMessageAndBlocksInDB = async (
return acc
}, {})
await updateMessageV2(updatedMessage.topicId, updatedMessage.id, messageUpdatesPayload)
await updateMessage(updatedMessage.topicId, updatedMessage.id, messageUpdatesPayload)
store.dispatch(updateTopicUpdatedAt({ topicId: updatedMessage.topicId }))
}
@ -432,7 +433,7 @@ const getBlockThrottler = (id: string) => {
})
blockUpdateRafs.set(id, rafId)
await updateSingleBlockV2(id, blockUpdate)
await updateSingleBlock(id, blockUpdate)
}, 150)
blockUpdateThrottlers.set(id, throttler)
@ -893,7 +894,7 @@ export const sendMessage =
userMessage.agentSessionId = activeAgentSession.agentSessionId
}
await saveMessageAndBlocksToDB(userMessage, userMessageBlocks)
await saveMessageAndBlocksToDB(topicId, userMessage, userMessageBlocks)
dispatch(newMessagesActions.addMessage({ topicId, message: userMessage }))
if (userMessageBlocks.length > 0) {
dispatch(upsertManyBlocks(userMessageBlocks))
@ -911,7 +912,7 @@ export const sendMessage =
if (activeAgentSession.agentSessionId && !assistantMessage.agentSessionId) {
assistantMessage.agentSessionId = activeAgentSession.agentSessionId
}
await saveMessageAndBlocksToDB(assistantMessage, [])
await saveMessageAndBlocksToDB(topicId, assistantMessage, [])
dispatch(newMessagesActions.addMessage({ topicId, message: assistantMessage }))
queue.add(async () => {
@ -934,7 +935,7 @@ export const sendMessage =
model: assistant.model,
traceId: userMessage.traceId
})
await saveMessageAndBlocksToDB(assistantMessage, [])
await saveMessageAndBlocksToDB(topicId, assistantMessage, [])
dispatch(newMessagesActions.addMessage({ topicId, message: assistantMessage }))
queue.add(async () => {
@ -1000,11 +1001,11 @@ export const loadAgentSessionMessagesThunk =
* Loads messages and their blocks for a specific topic from the database
* and updates the Redux store.
*/
export const loadTopicMessagesThunk =
(topicId: string, forceReload: boolean = false) =>
async (dispatch: AppDispatch, getState: () => RootState) => {
return loadTopicMessagesThunkV2(topicId, forceReload)(dispatch, getState)
}
// export const loadTopicMessagesThunk =
// (topicId: string, forceReload: boolean = false) =>
// async (dispatch: AppDispatch, getState: () => RootState) => {
// return loadTopicMessagesThunkV2(topicId, forceReload)(dispatch, getState)
// }
/**
* Thunk to delete a single message and its associated blocks.
@ -1023,7 +1024,7 @@ export const deleteSingleMessageThunk =
try {
dispatch(newMessagesActions.removeMessage({ topicId, messageId }))
cleanupMultipleBlocks(dispatch, blockIdsToDelete)
await deleteMessageFromDBV2(topicId, messageId)
await deleteMessageFromDB(topicId, messageId)
} catch (error) {
logger.error(`[deleteSingleMessage] Failed to delete message ${messageId}:`, error as Error)
}
@ -1062,7 +1063,7 @@ export const deleteMessageGroupThunk =
try {
dispatch(newMessagesActions.removeMessagesByAskId({ topicId, askId }))
cleanupMultipleBlocks(dispatch, blockIdsToDelete)
await deleteMessagesFromDBV2(topicId, messageIdsToDelete)
await deleteMessagesFromDB(topicId, messageIdsToDelete)
} catch (error) {
logger.error(`[deleteMessageGroup] Failed to delete messages with askId ${askId}:`, error as Error)
}
@ -1087,7 +1088,7 @@ export const clearTopicMessagesThunk =
dispatch(newMessagesActions.clearTopicMessages(topicId))
cleanupMultipleBlocks(dispatch, blockIdsToDelete)
await clearMessagesFromDBV2(topicId)
await clearMessagesFromDB(topicId)
} catch (error) {
logger.error(`[clearTopicMessagesThunk] Failed to clear messages for topic ${topicId}:`, error as Error)
}
@ -1408,7 +1409,7 @@ export const updateTranslationBlockThunk =
// 更新Redux状态
dispatch(updateOneBlock({ id: blockId, changes }))
await updateSingleBlockV2(blockId, changes)
await updateSingleBlock(blockId, changes)
// Logger.log(`[updateTranslationBlockThunk] Successfully updated translation block ${blockId}.`)
} catch (error) {
logger.error(`[updateTranslationBlockThunk] Failed to update translation block ${blockId}:`, error as Error)
@ -1479,7 +1480,7 @@ export const appendAssistantResponseThunk =
const insertAtIndex = existingMessageIndex !== -1 ? existingMessageIndex + 1 : currentTopicMessageIds.length
// 4. Update Database (Save the stub to the topic's message list)
await saveMessageAndBlocksToDB(newAssistantMessageStub, [], insertAtIndex)
await saveMessageAndBlocksToDB(topicId, newAssistantMessageStub, [], insertAtIndex)
dispatch(
newMessagesActions.insertMessageAtIndex({ topicId, message: newAssistantMessageStub, index: insertAtIndex })
@ -1631,12 +1632,12 @@ export const cloneMessagesToNewTopicThunk =
// Add the NEW blocks
if (clonedBlocks.length > 0) {
await bulkAddBlocksV2(clonedBlocks)
await bulkAddBlocks(clonedBlocks)
}
// Update file counts
const uniqueFiles = [...new Map(filesToUpdateCount.map((f) => [f.id, f])).values()]
for (const file of uniqueFiles) {
await updateFileCountV2(file.id, 1, false)
await updateFileCount(file.id, 1, false)
}
})
@ -1690,11 +1691,11 @@ export const updateMessageAndBlocksThunk =
}
// Update message properties if provided
if (messageUpdates && Object.keys(messageUpdates).length > 0 && messageId) {
await updateMessageV2(topicId, messageId, messageUpdates)
await updateMessage(topicId, messageId, messageUpdates)
}
// Update blocks if provided
if (blockUpdatesList.length > 0) {
await updateBlocksV2(blockUpdatesList)
await updateBlocks(blockUpdatesList)
}
dispatch(updateTopicUpdatedAt({ topicId }))
@ -1748,3 +1749,197 @@ export const removeBlocksThunk =
throw error
}
}
//以下内容从原 messageThunk.v2.ts 迁移过来,原文件已经删除
//原因v2.ts并不是v2数据重构的一部分而相关命名对v2重构造成重大误解故两文件合并以消除误解
/**
* Load messages for a topic using unified DbService
*/
export const loadTopicMessagesThunk =
(topicId: string, forceReload: boolean = false) =>
async (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState()
dispatch(newMessagesActions.setCurrentTopicId(topicId))
// Skip if already cached and not forcing reload
if (!forceReload && state.messages.messageIdsByTopic[topicId]) {
return
}
try {
dispatch(newMessagesActions.setTopicLoading({ topicId, loading: true }))
// Unified call - no need to check isAgentSessionTopicId
const { messages, blocks } = await dbService.fetchMessages(topicId)
logger.silly('Loaded messages via DbService', {
topicId,
messageCount: messages.length,
blockCount: blocks.length
})
// Update Redux state with fetched data
if (blocks.length > 0) {
dispatch(upsertManyBlocks(blocks))
}
dispatch(newMessagesActions.messagesReceived({ topicId, messages }))
} catch (error) {
logger.error(`Failed to load messages for topic ${topicId}:`, error as Error)
// Could dispatch an error action here if needed
} finally {
dispatch(newMessagesActions.setTopicLoading({ topicId, loading: false }))
}
}
/**
* Get raw topic data using unified DbService
* Returns topic with messages array
*/
export const getRawTopic = async (topicId: string): Promise<{ id: string; messages: Message[] } | undefined> => {
try {
const rawTopic = await dbService.getRawTopic(topicId)
logger.silly('Retrieved raw topic via DbService', { topicId, found: !!rawTopic })
return rawTopic
} catch (error) {
logger.error('Failed to get raw topic:', { topicId, error })
return undefined
}
}
/**
* Update file reference count
* Only applies to Dexie data source, no-op for agent sessions
*/
export const updateFileCount = async (fileId: string, delta: number, deleteIfZero: boolean = false): Promise<void> => {
try {
// Pass all parameters to dbService, including deleteIfZero
await dbService.updateFileCount(fileId, delta, deleteIfZero)
logger.silly('Updated file count', { fileId, delta, deleteIfZero })
} catch (error) {
logger.error('Failed to update file count:', { fileId, delta, error })
throw error
}
}
/**
* Delete a single message from database
*/
export const deleteMessageFromDB = async (topicId: string, messageId: string): Promise<void> => {
try {
await dbService.deleteMessage(topicId, messageId)
logger.silly('Deleted message via DbService', { topicId, messageId })
} catch (error) {
logger.error('Failed to delete message:', { topicId, messageId, error })
throw error
}
}
/**
* Delete multiple messages from database
*/
export const deleteMessagesFromDB = async (topicId: string, messageIds: string[]): Promise<void> => {
try {
await dbService.deleteMessages(topicId, messageIds)
logger.silly('Deleted messages via DbService', { topicId, count: messageIds.length })
} catch (error) {
logger.error('Failed to delete messages:', { topicId, messageIds, error })
throw error
}
}
/**
* Clear all messages from a topic
*/
export const clearMessagesFromDB = async (topicId: string): Promise<void> => {
try {
await dbService.clearMessages(topicId)
logger.silly('Cleared all messages via DbService', { topicId })
} catch (error) {
logger.error('Failed to clear messages:', { topicId, error })
throw error
}
}
/**
* Save a message and its blocks to database
* Uses unified interface, no need for isAgentSessionTopicId check
*/
export const saveMessageAndBlocksToDB = async (
topicId: string,
message: Message,
blocks: MessageBlock[],
messageIndex: number = -1
): Promise<void> => {
try {
const blockIds = blocks.map((block) => block.id)
const shouldSyncBlocks =
blockIds.length > 0 && (!message.blocks || blockIds.some((id, index) => message.blocks?.[index] !== id))
const messageWithBlocks = shouldSyncBlocks ? { ...message, blocks: blockIds } : message
// Direct call without conditional logic, now with messageIndex
await dbService.appendMessage(topicId, messageWithBlocks, blocks, messageIndex)
logger.silly('Saved message and blocks via DbService', {
topicId,
messageId: message.id,
blockCount: blocks.length,
messageIndex
})
} catch (error) {
logger.error('Failed to save message and blocks:', { topicId, messageId: message.id, error })
throw error
}
}
/**
* Update a message in the database
*/
export const updateMessage = async (topicId: string, messageId: string, updates: Partial<Message>): Promise<void> => {
try {
await dbService.updateMessage(topicId, messageId, updates)
logger.silly('Updated message via DbService', { topicId, messageId })
} catch (error) {
logger.error('Failed to update message:', { topicId, messageId, error })
throw error
}
}
/**
* Update a single message block
*/
export const updateSingleBlock = async (blockId: string, updates: Partial<MessageBlock>): Promise<void> => {
try {
await dbService.updateSingleBlock(blockId, updates)
logger.silly('Updated single block via DbService', { blockId })
} catch (error) {
logger.error('Failed to update single block:', { blockId, error })
throw error
}
}
/**
* Bulk add message blocks (for new blocks)
*/
export const bulkAddBlocks = async (blocks: MessageBlock[]): Promise<void> => {
try {
await dbService.bulkAddBlocks(blocks)
logger.silly('Bulk added blocks via DbService', { count: blocks.length })
} catch (error) {
logger.error('Failed to bulk add blocks:', { count: blocks.length, error })
throw error
}
}
/**
* Update multiple message blocks (upsert operation)
*/
export const updateBlocks = async (blocks: MessageBlock[]): Promise<void> => {
try {
await dbService.updateBlocks(blocks)
logger.silly('Updated blocks via DbService', { count: blocks.length })
} catch (error) {
logger.error('Failed to update blocks:', { count: blocks.length, error })
throw error
}
}

View File

@ -1,249 +0,0 @@
/**
* @deprecated Scheduled for removal in v2.0.0
* --------------------------------------------------------------------------
* NOTICE: V2 DATA&UI REFACTORING (by 0xfullex)
* --------------------------------------------------------------------------
* STOP: Feature PRs affecting this file are currently BLOCKED.
* Only critical bug fixes are accepted during this migration phase.
*
* This file is being refactored to v2 standards.
* Any non-critical changes will conflict with the ongoing work.
*
* 🔗 Context & Status:
* - Contribution Hold: https://github.com/CherryHQ/cherry-studio/issues/10954
* - v2 Refactor PR : https://github.com/CherryHQ/cherry-studio/pull/10162
* --------------------------------------------------------------------------
*/
/**
* V2 implementations of message thunk functions using the unified DbService
* These implementations will be gradually rolled out using feature flags
*/
import { loggerService } from '@logger'
import { dbService } from '@renderer/services/db'
import type { Message, MessageBlock } from '@renderer/types/newMessage'
import type { AppDispatch, RootState } from '../index'
import { upsertManyBlocks } from '../messageBlock'
import { newMessagesActions } from '../newMessage'
const logger = loggerService.withContext('MessageThunkV2')
// =================================================================
// Phase 2.1 - Batch 1: Read-only operations (lowest risk)
// =================================================================
/**
* Load messages for a topic using unified DbService
* This is the V2 implementation that will replace the original
*/
export const loadTopicMessagesThunkV2 =
(topicId: string, forceReload: boolean = false) =>
async (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState()
dispatch(newMessagesActions.setCurrentTopicId(topicId))
// Skip if already cached and not forcing reload
if (!forceReload && state.messages.messageIdsByTopic[topicId]) {
return
}
try {
dispatch(newMessagesActions.setTopicLoading({ topicId, loading: true }))
// Unified call - no need to check isAgentSessionTopicId
const { messages, blocks } = await dbService.fetchMessages(topicId)
logger.silly('Loaded messages via DbService', {
topicId,
messageCount: messages.length,
blockCount: blocks.length
})
// Update Redux state with fetched data
if (blocks.length > 0) {
dispatch(upsertManyBlocks(blocks))
}
dispatch(newMessagesActions.messagesReceived({ topicId, messages }))
} catch (error) {
logger.error(`Failed to load messages for topic ${topicId}:`, error as Error)
// Could dispatch an error action here if needed
} finally {
dispatch(newMessagesActions.setTopicLoading({ topicId, loading: false }))
}
}
/**
* Get raw topic data using unified DbService
* Returns topic with messages array
*/
export const getRawTopicV2 = async (topicId: string): Promise<{ id: string; messages: Message[] } | undefined> => {
try {
const rawTopic = await dbService.getRawTopic(topicId)
logger.silly('Retrieved raw topic via DbService', { topicId, found: !!rawTopic })
return rawTopic
} catch (error) {
logger.error('Failed to get raw topic:', { topicId, error })
return undefined
}
}
// =================================================================
// Phase 2.2 - Batch 2: Helper functions
// =================================================================
/**
* Update file reference count
* Only applies to Dexie data source, no-op for agent sessions
*/
export const updateFileCountV2 = async (
fileId: string,
delta: number,
deleteIfZero: boolean = false
): Promise<void> => {
try {
// Pass all parameters to dbService, including deleteIfZero
await dbService.updateFileCount(fileId, delta, deleteIfZero)
logger.silly('Updated file count', { fileId, delta, deleteIfZero })
} catch (error) {
logger.error('Failed to update file count:', { fileId, delta, error })
throw error
}
}
// =================================================================
// Phase 2.3 - Batch 3: Delete operations
// =================================================================
/**
* Delete a single message from database
*/
export const deleteMessageFromDBV2 = async (topicId: string, messageId: string): Promise<void> => {
try {
await dbService.deleteMessage(topicId, messageId)
logger.silly('Deleted message via DbService', { topicId, messageId })
} catch (error) {
logger.error('Failed to delete message:', { topicId, messageId, error })
throw error
}
}
/**
* Delete multiple messages from database
*/
export const deleteMessagesFromDBV2 = async (topicId: string, messageIds: string[]): Promise<void> => {
try {
await dbService.deleteMessages(topicId, messageIds)
logger.silly('Deleted messages via DbService', { topicId, count: messageIds.length })
} catch (error) {
logger.error('Failed to delete messages:', { topicId, messageIds, error })
throw error
}
}
/**
* Clear all messages from a topic
*/
export const clearMessagesFromDBV2 = async (topicId: string): Promise<void> => {
try {
await dbService.clearMessages(topicId)
logger.silly('Cleared all messages via DbService', { topicId })
} catch (error) {
logger.error('Failed to clear messages:', { topicId, error })
throw error
}
}
// =================================================================
// Phase 2.4 - Batch 4: Complex write operations
// =================================================================
/**
* Save a message and its blocks to database
* Uses unified interface, no need for isAgentSessionTopicId check
*/
export const saveMessageAndBlocksToDBV2 = async (
topicId: string,
message: Message,
blocks: MessageBlock[],
messageIndex: number = -1
): Promise<void> => {
try {
const blockIds = blocks.map((block) => block.id)
const shouldSyncBlocks =
blockIds.length > 0 && (!message.blocks || blockIds.some((id, index) => message.blocks?.[index] !== id))
const messageWithBlocks = shouldSyncBlocks ? { ...message, blocks: blockIds } : message
// Direct call without conditional logic, now with messageIndex
await dbService.appendMessage(topicId, messageWithBlocks, blocks, messageIndex)
logger.silly('Saved message and blocks via DbService', {
topicId,
messageId: message.id,
blockCount: blocks.length,
messageIndex
})
} catch (error) {
logger.error('Failed to save message and blocks:', { topicId, messageId: message.id, error })
throw error
}
}
// Note: sendMessageV2 would be implemented here but it's more complex
// and would require more of the supporting code from messageThunk.ts
// =================================================================
// Phase 2.5 - Batch 5: Update operations
// =================================================================
/**
* Update a message in the database
*/
export const updateMessageV2 = async (topicId: string, messageId: string, updates: Partial<Message>): Promise<void> => {
try {
await dbService.updateMessage(topicId, messageId, updates)
logger.silly('Updated message via DbService', { topicId, messageId })
} catch (error) {
logger.error('Failed to update message:', { topicId, messageId, error })
throw error
}
}
/**
* Update a single message block
*/
export const updateSingleBlockV2 = async (blockId: string, updates: Partial<MessageBlock>): Promise<void> => {
try {
await dbService.updateSingleBlock(blockId, updates)
logger.silly('Updated single block via DbService', { blockId })
} catch (error) {
logger.error('Failed to update single block:', { blockId, error })
throw error
}
}
/**
* Bulk add message blocks (for new blocks)
*/
export const bulkAddBlocksV2 = async (blocks: MessageBlock[]): Promise<void> => {
try {
await dbService.bulkAddBlocks(blocks)
logger.silly('Bulk added blocks via DbService', { count: blocks.length })
} catch (error) {
logger.error('Failed to bulk add blocks:', { count: blocks.length, error })
throw error
}
}
/**
* Update multiple message blocks (upsert operation)
*/
export const updateBlocksV2 = async (blocks: MessageBlock[]): Promise<void> => {
try {
await dbService.updateBlocks(blocks)
logger.silly('Updated blocks via DbService', { count: blocks.length })
} catch (error) {
logger.error('Failed to update blocks:', { count: blocks.length, error })
throw error
}
}