Files
happy-life-star/web/src/services/message.ts
T
2025-10-26 23:26:30 +08:00

277 lines
8.5 KiB
TypeScript

import { http } from '@/utils/request'
import type { ChatMessage } from '@/types'
// 消息相关类型定义
export interface MessageResponse {
id: string
conversationId: string
content: string
type: string
sender: string
isRead: number
aiReply?: string
emotionAnalysis?: string
messageOrder?: number
createTime: string
updateTime: string
}
export interface PageResult<T> {
records: T[]
total: number
size: number
current: number
pages: number
}
export interface MessagePageRequest {
current: number
size: number
conversationId?: string
type?: string
sender?: string
startTime?: string
endTime?: string
}
export interface MessageSearchRequest {
keyword: string
limit: number
conversationId?: string
type?: string
sender?: string
startTime?: string
endTime?: string
}
export interface MessageRecentRequest {
limit: number
conversationId?: string
type?: string
sender?: string
}
/**
* 消息API服务 - 与web项目保持一致的接口
*/
export const messageApi = {
// 获取用户消息分页
getUserMessages: async (current: number = 1, size: number = 20) => {
console.log('📨 调用getUserMessages API:', { current, size })
// backend-single: GET /message/page (后端根据token识别用户)
const response = await http.get(`/message/page`, { params: { current, size } })
console.log('📨 getUserMessages API响应:', response)
return response
},
// 搜索用户消息
searchUserMessages: async (keyword: string, limit: number = 50) => {
console.log('🔍 调用searchUserMessages API:', { keyword, limit })
// backend-single: POST /message/search
const resp = await http.post(`/message/search`, { keyword, limit })
console.log('🔍 searchUserMessages API响应:', resp)
// 统一返回数组,兼容控制器返回 PageResult 结构
const data: any = (resp as any).data || resp
const records = data.records || data
return Array.isArray(records) ? records : []
},
// 获取用户最近的聊天记录 - 返回数组,兼容后端 PageResult 结构
getRecentMessages: async (limit: number = 10) => {
console.log('📝 调用getRecentMessages API:', { limit })
// backend-single: POST /message/recent
const resp = await http.post(`/message/recent`, { limit })
console.log('📝 getRecentMessages API响应:', resp)
const data: any = (resp as any).data || resp
const records = data.records || data
return Array.isArray(records) ? records : []
},
// 获取消息详情
getMessageById: async (id: string) => {
console.log('📄 调用getMessageById API:', { id })
// backend-single: GET /message/detail?id=xxx
const response = await http.get(`/message/detail`, { params: { id } })
console.log('📄 getMessageById API响应:', response)
return response
}
}
/**
* 消息服务类 - 提供高级封装
*/
class MessageService {
/**
* 获取用户消息分页
*/
static async getUserMessages(current: number = 1, size: number = 20): Promise<PageResult<MessageResponse>> {
const response = await messageApi.getUserMessages(current, size)
return response.data || response
}
/**
* 搜索用户消息
*/
static async searchUserMessages(request: MessageSearchRequest): Promise<MessageResponse[]> {
const response = await messageApi.searchUserMessages(request.keyword, request.limit)
return response.data || response
}
/**
* 获取用户最近的聊天记录
*/
static async getRecentMessages(request: MessageRecentRequest): Promise<MessageResponse[]> {
console.log('📝 MessageService.getRecentMessages 调用:', request)
const response = await messageApi.getRecentMessages(request.limit)
console.log('📝 MessageService.getRecentMessages 响应:', response)
// 处理响应数据结构
const messageList = response.data || response || []
return Array.isArray(messageList) ? messageList : []
}
/**
* 根据ID获取消息详情
*/
static async getMessageById(id: string): Promise<MessageResponse> {
const response = await messageApi.getMessageById(id)
return response.data || response
}
/**
* 转换消息格式为聊天消息格式 - 完整匹配后端字段
*/
static convertToChatMessage(msg: MessageResponse): ChatMessage {
console.log('🔄 转换消息格式:', msg)
// 处理时间格式 - 后端返回 "2025-07-26 22:09:10" 格式
// 直接保持原始字符串格式,让前端UI层处理
let timestamp = msg.createTime
console.log('🕐 保持原始时间格式:', msg.createTime)
const chatMessage: ChatMessage = {
id: msg.id,
content: msg.content,
type: msg.sender === 'user' ? 'user' : (msg.sender === 'ai' ? 'ai' : 'system'),
timestamp: timestamp,
conversationId: msg.conversationId,
sessionId: msg.conversationId, // 别名,保持兼容性
status: 'sent',
sender: msg.sender as 'user' | 'ai' | 'system',
isRead: msg.isRead,
role: msg.sender === 'user' ? 'user' : 'assistant', // 用于UI显示
messageOrder: msg.messageOrder // 消息顺序 - 用于确保消息展示顺序
}
console.log('✅ 转换后的消息详情:', {
原始sender: msg.sender,
转换后role: chatMessage.role,
转换后type: chatMessage.type,
时间: msg.createTime + ' -> ' + timestamp,
消息顺序: msg.messageOrder
})
console.log('✅ 转换后的消息:', chatMessage)
return chatMessage
}
/**
* 批量转换消息格式
*/
static convertToChatMessages(messages: MessageResponse[]): ChatMessage[] {
console.log('🔄 批量转换消息格式,数量:', messages.length)
const chatMessages = messages.map(msg => this.convertToChatMessage(msg))
console.log('✅ 批量转换完成,结果:', chatMessages)
return chatMessages
}
/**
* 解析时间戳为毫秒数 - 统一处理各种时间格式
* 支持格式:
* - "2025-07-26 22:09:10" (后端格式)
* - "2025-07-26T22:09:10" (ISO格式)
* - "2025-07-26T22:09:10.000Z" (ISO完整格式)
* - Date对象
*/
static parseTimestamp(timestamp: string | Date | number): number {
if (typeof timestamp === 'number') {
return timestamp
}
if (timestamp instanceof Date) {
return timestamp.getTime()
}
if (typeof timestamp === 'string') {
// 处理 "2025-07-26 22:09:10" 格式
if (timestamp.includes(' ') && !timestamp.includes('T')) {
const isoString = timestamp.replace(' ', 'T')
return new Date(isoString).getTime()
}
// 处理其他格式
return new Date(timestamp).getTime()
}
// 默认返回当前时间
return new Date().getTime()
}
/**
* 对消息进行排序和去重
* 优先使用 messageOrder 排序,如果没有则使用时间戳排序
* 确保消息按顺序排列,相同顺序的消息保持原有顺序
*/
static sortAndDeduplicateMessages(messages: ChatMessage[]): ChatMessage[] {
console.log('🔄 开始排序和去重消息,原始数量:', messages.length)
// 创建消息ID的Set用于去重
const seenIds = new Set<string>()
const uniqueMessages: ChatMessage[] = []
// 先去重
for (const msg of messages) {
if (!seenIds.has(msg.id)) {
seenIds.add(msg.id)
uniqueMessages.push(msg)
} else {
console.warn('⚠️ 发现重复消息,ID:', msg.id)
}
}
console.log('✅ 去重完成,去重后数量:', uniqueMessages.length)
// 按消息顺序排序(优先使用 messageOrder,如果没有则使用时间戳)
uniqueMessages.sort((a, b) => {
// 优先使用 messageOrder 排序
if (a.messageOrder !== undefined && b.messageOrder !== undefined) {
if (a.messageOrder !== b.messageOrder) {
return a.messageOrder - b.messageOrder
}
}
// 如果 messageOrder 相同或不存在,使用时间戳排序
const timeA = this.parseTimestamp(a.timestamp)
const timeB = this.parseTimestamp(b.timestamp)
if (timeA !== timeB) {
return timeA - timeB
}
// 时间相同时,保持原有顺序(稳定排序)
// 通过比较消息ID的字典序来保持一致性
return a.id.localeCompare(b.id)
})
console.log('✅ 排序完成,排序后消息:', uniqueMessages.map(m => ({
id: m.id,
type: m.type,
order: m.messageOrder,
time: m.timestamp,
content: m.content.substring(0, 20) + '...'
})))
return uniqueMessages
}
}
export default MessageService