import { needsImageDecrypt, parseImageExtra, toBusinessImagePath } from '@/utils/im/message'; /** 消息中心私信列表的一行数据(纯 SDK 会话映射) */ export type PrivateSessionItem = { id: string; /** 业务 uid;列表由 SDK 驱动时为 0,进会话前用 /im/user-id 反查 */ userId: number; /** 对端 IM userId(= conv.receiverId) */ friendImUserId: number; userName: string; userAvatar: string; preContent: string; createdAt: string | number; noReadNum: number; }; /** SDK conversation.subscribeList 推送的会话结构 */ export type SdkConversation = { type?: string; receiverId?: number | string; unreadCount?: number; extra?: string; lastMessageTime?: number; updatedAt?: number; createdAt?: number; content?: string; chatMessageType?: string | number; user?: { id?: number; nickName?: string; nickname?: string; name?: string; avatar?: string; portrait?: string; imgUrl?: string; }; remark?: string; }; // —— 展示文案 —— /** blob/data 本地地址、http(s) 公网图无需再走 imgDecypt */ export function isPublicImageUrl(url?: string): boolean { return !needsImageDecrypt(url); } export function formatSessionPreview(content?: string): string { const text = (content || '').trim(); if (!text) return '暂无消息'; if (/^\[图片\]|^图片$|image/i.test(text)) return '[图片]'; return text; } export function formatSessionListTime(date: string | number): string { if (date == null || date === '') return ''; const d = typeof date === 'number' ? new Date(date) : new Date(date); if (Number.isNaN(d.getTime()) || d.getFullYear() === 1) return ''; return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`; } export function buildMessagePreview(msg: { content?: string; chatMessageType?: string | number; extra?: unknown; }): string { const type = msg.chatMessageType; if (type === 'IMAGE' || type === 2 || parseImageExtra(msg.extra)) return '[图片]'; return (msg.content || '').trim() || '暂无消息'; } // —— SDK 会话 → 列表行 —— function toTimestamp(value: string | number | undefined): number { if (value == null || value === '') return 0; const ts = typeof value === 'number' ? value : new Date(value).getTime(); return Number.isFinite(ts) ? ts : 0; } function isDirectConversation(conv: SdkConversation): boolean { if (conv.type && conv.type !== 'direct') return false; const imId = Number(conv.receiverId); return Number.isFinite(imId) && imId > 0; } /** IM 快照偶发幽灵会话(user 资料整体缺失),无法渲染也无法跳转,不展示 */ function hasUserProfile(conv: SdkConversation): boolean { const user = conv.user; return !!(user?.nickName || user?.nickname || user?.name || user?.avatar || user?.portrait || user?.imgUrl); } function toSessionItem(conv: SdkConversation): PrivateSessionItem { const imId = Number(conv.receiverId); const user = conv.user; return { id: String(imId), userId: 0, friendImUserId: imId, userName: user?.nickName || user?.nickname || user?.name || conv.remark || '', userAvatar: toBusinessImagePath(user?.avatar || user?.portrait || user?.imgUrl || ''), preContent: parseImageExtra(conv.extra) ? '[图片]' : buildMessagePreview({ content: conv.content, chatMessageType: conv.chatMessageType, extra: conv.extra, }), createdAt: conv.lastMessageTime ?? conv.updatedAt ?? conv.createdAt ?? '', noReadNum: Number(conv.unreadCount ?? 0), }; } /** SDK 会话快照 → 私信列表(仅单聊;按最后消息时间倒序) */ export function mapSdkConversations(conversations: SdkConversation[]): PrivateSessionItem[] { const seen = new Set(); const list: PrivateSessionItem[] = []; for (const conv of conversations) { if (!isDirectConversation(conv) || !hasUserProfile(conv)) continue; const imId = Number(conv.receiverId); if (seen.has(imId)) continue; seen.add(imId); list.push(toSessionItem(conv)); } return list.sort((a, b) => toTimestamp(b.createdAt) - toTimestamp(a.createdAt)); }