/** IM 消息类型枚举(对齐后端 ImMessageTypeEnum) */ export const ImMessageType = { // ========== 用户聊天消息(101-105 直接复用 OpenIM 段位编号) ========== TEXT: 101, // 文本(对应 OpenIM Text=101) IMAGE: 102, // 图片(对应 OpenIM Picture=102) VOICE: 103, // 语音(对应 OpenIM Sound=103) VIDEO: 104, // 视频(对应 OpenIM Video=104) FILE: 105, // 文件(对应 OpenIM File=105) MERGE: 107, // 合并转发(对应 OpenIM Merger=107;payload 内嵌完整快照) CARD: 108, // 名片(对应 OpenIM Card=108) FACE: 115, // 表情贴图(对应 OpenIM Face=115;Unicode emoji 仍走 TEXT) // ========== 信号类(2101 / 2200 直接复用 OpenIM 段位编号;2201 自有扩展) ========== RECALL: 2101, // 撤回(对应 OpenIM RevokeNotification=2101) RECEIPT: 2200, // 回执(对应 OpenIM HasReadReceipt=2200) READ: 2201, // 已读(多端同步,OpenIM 无对应;自有扩展) // ========== 实时通话信令(1601-1605 段位与 OpenIM 对齐;1610+ 自有扩展) ========== RTC_CALL: 1601, // 通话信令统一入口(对应 OpenIM SignalingNotification=1601) RTC_PARTICIPANT_CONNECTED: 1602, // 通话参与者加入(对应 OpenIM RoomParticipantsConnectedNotification=1602) RTC_PARTICIPANT_DISCONNECTED: 1603, // 通话参与者离开(对应 OpenIM RoomParticipantsDisconnectedNotification=1603) // 1604-1609 OpenIM 已用 / 留作扩展,本系统暂不使用 RTC_CALL_START: 1610, // 通话开始(自有扩展,OpenIM 无;仅群聊;与 END 两段式配对) RTC_CALL_END: 1611, // 通话结束(自有扩展,OpenIM 无;私聊 / 群聊) // ========== 好友通知(1201-1210 直接复用 OpenIM 段位编号) ========== FRIEND_REQUEST_APPROVED: 1201, // 好友申请被同意 FRIEND_REQUEST_REJECTED: 1202, // 好友申请被拒绝 FRIEND_REQUEST_RECEIVED: 1203, // 收到新的好友申请 FRIEND_ADD: 1204, // 新增好友(双方建立关系) FRIEND_DELETE: 1205, // 好友被删除 // 1206 对应 OpenIM FriendRemarkSetNotification;本系统并入 FRIEND_UPDATE(1210) 统一推送,单一字段变更不再独立通道 FRIEND_BLOCK: 1207, // 加入黑名单 FRIEND_UNBLOCK: 1208, // 移出黑名单 FRIEND_INFO_UPDATED: 1209, // 好友资料变更(昵称 / 头像) FRIEND_UPDATE: 1210, // 好友信息批量更新(silent / pinned) // ========== 群事件(1501-1520 直接复用 OpenIM 段位编号;1530+ 自有扩展段) ========== GROUP_CREATE: 1501, // 群创建 GROUP_INFO_UPDATE: 1502, // 群信息变更(NAME / NOTICE 之外字段兜底) GROUP_REQUEST_RECEIVED: 1503, // 收到新的入群申请(私聊定向推送给群主 + 全部管理员) GROUP_MEMBER_QUIT: 1504, // 成员退群 GROUP_REQUEST_APPROVED: 1505, // 入群申请被同意(私聊推给申请人 + 群主 + 全部管理员) GROUP_REQUEST_REJECTED: 1506, // 入群申请被拒绝(同上) GROUP_OWNER_TRANSFER: 1507, // 群主转让 GROUP_MEMBER_KICK: 1508, // 成员被移出 GROUP_MEMBER_INVITE: 1509, // 成员加入 GROUP_MEMBER_ENTER: 1510, // 自由进群(FREE 模式或申请通过后;全员广播) GROUP_DISSOLVE: 1511, // 群解散 GROUP_MEMBER_MUTED: 1512, // 单成员禁言 GROUP_MEMBER_CANCEL_MUTED: 1513, // 单成员取消禁言 GROUP_MUTED: 1514, // 全群禁言 GROUP_CANCEL_MUTED: 1515, // 全群取消禁言 GROUP_MEMBER_NICKNAME_UPDATE: 1516, // 成员昵称变更(窄化到 displayUserName) GROUP_ADMIN_ADD: 1517, // 添加管理员 GROUP_ADMIN_REMOVE: 1518, // 撤销管理员 GROUP_NOTICE_UPDATE: 1519, // 群公告变更 GROUP_NAME_UPDATE: 1520, // 群名变更 // ========== 自有扩展段(1530+,OpenIM 1500-1520 段位无对应物) ========== GROUP_MEMBER_SETTING_UPDATE: 1530, // 群成员个人设置变更:silent / groupRemark 个人多端同步 GROUP_MESSAGE_PIN: 1531, // 群消息置顶(自有扩展,OpenIM 无) GROUP_MESSAGE_UNPIN: 1532, // 群消息取消置顶(自有扩展,OpenIM 无) GROUP_BANNED: 1533 // 群封禁变更(自有扩展,OpenIM 无) } as const /** 判断是否「群广播事件」:[GROUP_CREATE, GROUP_BANNED] 段位都算,仅 GROUP_MEMBER_SETTING_UPDATE 是个人信号排除 */ export function isGroupNotification(type: number): boolean { return ( type >= ImMessageType.GROUP_CREATE && type <= ImMessageType.GROUP_BANNED && type !== ImMessageType.GROUP_MEMBER_SETTING_UPDATE ) } /** 判断是否「好友通知事件」:1201-1210 段位 */ export function isFriendNotification(type: number): boolean { return type >= ImMessageType.FRIEND_REQUEST_APPROVED && type <= ImMessageType.FRIEND_UPDATE } /** 判断是否「加群申请通知事件」:1503/1505/1506 走私聊通道,按段位识别 */ export function isGroupRequestNotification(type: number): boolean { return ( type === ImMessageType.GROUP_REQUEST_RECEIVED || type === ImMessageType.GROUP_REQUEST_APPROVED || type === ImMessageType.GROUP_REQUEST_REJECTED ) } /** 判断是否「会话内的好友事件气泡」:FRIEND_ADD / FRIEND_DELETE 直接渲染成灰色提示,与群事件同处理 */ export function isFriendChatTip(type: number): boolean { return type === ImMessageType.FRIEND_ADD || type === ImMessageType.FRIEND_DELETE } /** 判断是否「会话内的通话事件气泡」:RTC_CALL_START / RTC_CALL_END 渲染成灰色提示 */ export function isRtcCallTip(type: number): boolean { return type === ImMessageType.RTC_CALL_START || type === ImMessageType.RTC_CALL_END } /** * IM 普通消息类型集合(normal vs event 二分;与后端 ImMessageTypeEnum.normal 字段语义一致) * * 这个集合在多处被复用,新增类型前先确认所有副作用都符合预期: * 1. 后端发送入口校验(Im{Private,Group}MessageSendReqVO.isNormalType)—— 用户发送的消息类型必须 normal=true * 2. 前端接收侧未读 / 提示音(websocketStore)—— normal 消息计入会话未读数 + 触发声音 * 3. 前端会话列表 lastType / @ 标签(ConversationItem)—— 只有 normal 才算「最后一条聊天消息」 * 4. 前端群消息置顶菜单(MessageItem.vue 的 canPin)—— normal 才允许群主 / 管理员置顶 * * 名片(CARD)/ 表情(FACE)都是「用户主动发的聊天消息」,1/2/3 都符合预期;4 同时放开 = 群主可置顶,语义合理 */ const ImMessageTypeNormals: number[] = [ ImMessageType.TEXT, ImMessageType.IMAGE, ImMessageType.FILE, ImMessageType.VOICE, ImMessageType.VIDEO, ImMessageType.CARD, ImMessageType.FACE, ImMessageType.MERGE ] /** 判断是否"普通消息" */ export function isNormalMessage(type: number): boolean { return ImMessageTypeNormals.includes(type) } /** IM 媒体消息类型集合:发送依赖本地 File 上传,刷新后 _localFile 丢失即不可恢复 */ const ImMessageTypeMedia: number[] = [ ImMessageType.IMAGE, ImMessageType.FILE, ImMessageType.VOICE, ImMessageType.VIDEO ] /** 判断是否「媒体消息」:图片 / 文件 / 语音 / 视频 */ export function isMediaMessageType(type: number): boolean { return ImMessageTypeMedia.includes(type) } /** * IM 消息状态枚举(对齐后端 ImMessageStatusEnum,前端扩展 SENDING + FAILED) * * 生命周期:FAILED(-2) → SENDING(-1) → UNREAD(0) → READ(3) / RECALL(2) */ export const ImMessageStatus = { FAILED: -2, // 发送失败(前端独有) SENDING: -1, // 发送中(前端独有) UNREAD: 0, // 未读 RECALL: 2, // 已撤回 READ: 3 // 已读 } as const /** IM 会话类型枚举 */ export const ImConversationType = { PRIVATE: 1, // 私聊 GROUP: 2 // 群聊 } as const /** ImConversationType 取值(用于消息 payload 字段类型收窄) */ export type ImConversationTypeValue = (typeof ImConversationType)[keyof typeof ImConversationType] /** 是否私聊会话;同时收窄类型 */ export function isPrivateConversation(type: number | undefined): boolean { return type === ImConversationType.PRIVATE } /** 是否群聊会话;同时收窄类型 */ export function isGroupConversation(type: number | undefined): boolean { return type === ImConversationType.GROUP } /** IM 通话媒体类型(对齐后端 ImRtcCallMediaTypeEnum) */ export const ImCallMediaType = { VOICE: 1, VIDEO: 2 } as const /** IM 通话状态(对齐后端 ImRtcCallStatusEnum) */ export const ImCallStatus = { CREATED: 10, // 创建:私聊等被叫接听;群聊发起人已进房等其他人加入 RUNNING: 20, // 进行中:第一个非发起人接通后进入 ENDED: 30 // 已结束 } as const /** IM 通话结束原因(对齐后端 ImRtcCallEndReasonEnum) */ export const ImCallEndReason = { HANGUP: 1, // 接通后任一方主动挂断 REJECT: 2, // 被叫接通前点拒接 CANCEL: 3, // 主叫接通前主动取消 BUSY: 5, // 私聊呼叫时对方正忙 ERROR: 9 // 网络中断 / 设备失败 } as const /** ImCallEndReason 取值类型 */ export type ImCallEndReasonValue = (typeof ImCallEndReason)[keyof typeof ImCallEndReason] /** IM 通话参与者状态(对齐后端 ImRtcParticipantStatusEnum);同时作为 RTC_CALL 信令 status 字段取值 */ export const ImCallParticipantStatus = { INVITING: 10, // 来电邀请 JOINED: 20, // 接听 / 已加入 REJECTED: 30, // 拒接 NO_ANSWER: 40, // 主叫取消,被邀请方未应答 LEFT: 50 // 挂断离开 } as const /** ImCallParticipantStatus 取值类型 */ export type ImCallParticipantStatusValue = (typeof ImCallParticipantStatus)[keyof typeof ImCallParticipantStatus] /** IM WebSocket 外层帧类型(对齐后端 ImPrivateMessageDTO.TYPE / ImGroupMessageDTO.TYPE) */ export const ImWebSocketMessageType = { PRIVATE_MESSAGE: 'im-private-message', // 私聊通道 GROUP_MESSAGE: 'im-group-message' // 群聊通道 } as const /** IM 群回执状态枚举(对齐后端 ImGroupMessageReceiptStatusEnum) */ export const ImGroupReceiptStatus = { NO_RECEIPT: 0, // 不需要回执 PENDING: 1, // 待完成 DONE: 2 // 已完成 } as const /** 群成员角色(对齐后端 ImGroupMemberRoleEnum) */ export const ImGroupMemberRole = { OWNER: 1, // 群主 ADMIN: 2, // 管理员 NORMAL: 3 // 普通成员 } as const /** 加群来源(对齐后端 ImGroupAddSourceEnum) */ export const ImGroupAddSource = { SEARCH: 1, // 搜索 INVITE: 2, // 邀请 QR_CODE: 3, // 扫码 SHARE_LINK: 4 // 分享链接 } as const /** 加群申请处理结果(对齐后端 ImGroupRequestHandleResultEnum) */ export const ImGroupRequestHandleResult = { UNHANDLED: 0, // 未处理 AGREED: 1, // 同意 REFUSED: 2 // 拒绝 } as const /** 好友添加来源(对齐后端 ImFriendAddSourceEnum) */ export const ImFriendAddSource = { SEARCH: 1, // 搜索 GROUP: 2, // 群聊 QR_CODE: 3, // 扫码 CARD: 4 // 名片 } as const /** 好友申请处理结果(对齐后端 ImFriendRequestHandleResultEnum) */ export const ImFriendRequestHandleResult = { UNHANDLED: 0, // 未处理 AGREED: 1, // 同意 REFUSED: 2 // 拒绝 } as const /** * @全体成员 的特殊 userId 标识:atUserIds 中包含 -1 表示 @ 全体成员 * * 与后端约定:群消息 atUserIds 数组里出现 -1 时,所有成员都收到提醒 * MentionPicker 渲染虚拟项 + conversationStore.applyAt 判定 atAll 都靠这个值 */ export const IM_AT_ALL_USER_ID = -1 /** @全体成员 的展示名(对齐微信 PC) */ export const IM_AT_ALL_NICKNAME = '所有人' /** 转发模式:SINGLE 逐条原样转 / MERGE 打包成 MergeMessage */ export const ImForwardMode = { SINGLE: 'single', MERGE: 'merge' } as const /** ImForwardMode 取值类型 */ export type ImForwardModeValue = (typeof ImForwardMode)[keyof typeof ImForwardMode]