277 lines
8.5 KiB
TypeScript
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
|