gaoluyang
2026-06-29 27cd042df9aca0383a49f3514bc21958dd890912
src/views/im/home/types/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,318 @@
// ==================== WebSocket å¸§ / äº‹ä»¶ ====================
// åŽç«¯ WebSocket ç»Ÿä¸€å¸§ç»“构:{ type, content }
export interface WebSocketFrame {
  type: string // å¸§ç±»åž‹ï¼Œå¯¹é½ ImWebSocketMessageType
  content: string // å¸§å†…容(JSON å­—符串)
}
// IM WebSocket é€šçŸ¥ DTO(对齐后端 ImNotificationWebSocketDTO)
export interface ImNotificationWebSocketDTO {
  conversationType: number // ä¼šè¯ç±»åž‹
  contentType: number // å†…容类型
  payload: Record<string, any> // è´Ÿè½½æ•°æ®
}
// æ— ä¼šè¯åœ¨çº¿é€šçŸ¥ï¼ˆå¯¹é½åŽç«¯ conversationType = NONE çš„独立 payload)
export interface ImNoConversationNotification {
  type: number // å†…容类型
  [key: string]: any
}
// ç§èŠæ¶ˆæ¯ DTO(对齐后端 ImPrivateMessageNotification)
export interface ImPrivateMessageNotification {
  id: number // æ¶ˆæ¯ç¼–号
  clientMessageId: string // å®¢æˆ·ç«¯æ¶ˆæ¯ç¼–号
  senderId: number // å‘送人编号
  receiverId: number // æŽ¥æ”¶äººç¼–号
  type: number // å†…容类型
  content: string // æ¶ˆæ¯å†…容
  status: number // æ¶ˆæ¯çŠ¶æ€
  receiptStatus?: number // å›žæ‰§çŠ¶æ€ï¼ˆä¸éœ€è¦ / å¾…完成 / å·²å®Œæˆï¼‰
  sendTime: string // å‘送时间
}
// ç¾¤èŠæ¶ˆæ¯ DTO(对齐后端 ImGroupMessageNotification)
export interface ImGroupMessageNotification {
  id: number // æ¶ˆæ¯ç¼–号
  clientMessageId: string // å®¢æˆ·ç«¯æ¶ˆæ¯ç¼–号
  senderId: number // å‘送人编号
  groupId: number // ç¾¤ç¼–号
  type: number // å†…容类型
  content: string // æ¶ˆæ¯å†…容
  status: number // æ¶ˆæ¯çŠ¶æ€
  sendTime: string // å‘送时间
  atUserIds?: number[] // ç¾¤ @ ç›®æ ‡ç”¨æˆ·åˆ—表
  receiverUserIds?: number[] // ç¾¤å®šå‘接收用户列表
  readCount?: number // ç¾¤å›žæ‰§å·²è¯»äººæ•°ï¼ˆtype = RECEIPT æ—¶ä½¿ç”¨ï¼‰
  receiptStatus?: number // ç¾¤å›žæ‰§çŠ¶æ€ï¼ˆtype = RECEIPT æ—¶ä½¿ç”¨ï¼‰
  readId?: number // å·²è¯»ä½ç½®
}
// æ¶ˆæ¯å·²è¯»åŒæ­¥é€šçŸ¥ï¼ˆå¯¹é½åŽç«¯ ImMessageReadNotification)
export interface ImMessageReadNotification {
  id: number // å·²è¯»ä½ç½®
  type: number // å†…容类型
  senderId?: number // å‘送人编号
  receiverId?: number // ç§èŠæŽ¥æ”¶äººç¼–号
  groupId?: number // ç¾¤ç¼–号
  channelId?: number // é¢‘道编号
  readId?: number // å·²è¯»ä½ç½®
}
// æ¶ˆæ¯å›žæ‰§é€šçŸ¥ï¼ˆå¯¹é½åŽç«¯ ImMessageReceiptNotification)
export interface ImMessageReceiptNotification {
  id: number // æ¶ˆæ¯ç¼–号
  type: number // å†…容类型
  senderId?: number // å·²è¯»æ–¹ç”¨æˆ·ç¼–号
  receiverId?: number // ç§èŠæŽ¥æ”¶äººç¼–号
  groupId?: number // ç¾¤ç¼–号
  readCount?: number // ç¾¤å›žæ‰§å·²è¯»äººæ•°
  receiptStatus?: number // ç¾¤å›žæ‰§çŠ¶æ€
}
// ==================== æœ¬åœ°ä¼šè¯ / æ¶ˆæ¯ç»“æž„ ====================
/** å¼•用消息 */
export interface QuoteMessage {
  messageId: number // å¼•用消息编号
  senderId: number // å¼•用消息发送人编号
  type: number // å¼•用内容类型
  content: string // å¼•用消息内容
}
// ä¼šè¯æ•°æ®ç»“构(前端自有结构,后端无对应实体)
export interface Conversation {
  // ========== æ ¸å¿ƒæ ‡è¯† ==========
  targetId: number // ä¼šè¯ç›®æ ‡ç¼–号:私聊=对方 userId;群聊=groupId
  type: number // ä¼šè¯ç±»åž‹ï¼Œå¯¹é½ ImConversationType
  // ========== å±•示字段 ==========
  name: string // å±•示名称(私聊=好友昵称;群聊=群名)
  avatar: string // å¤´åƒ
  unreadCount: number // æœªè¯»æ•°
  // ========== æœ€åŽä¸€æ¡æ¶ˆæ¯äº‹å®žç´¢å¼• ==========
  lastContent: string // ä¼šè¯åˆ—表展示的最后一条消息摘要
  lastSendTime: number // æœ€åŽä¸€æ¡æ¶ˆæ¯æ—¶é—´ï¼Œç”¨äºŽæŽ’序
  lastSenderId?: number // å‘送人编号
  lastMessageType?: number // å†…容类型,对齐 ImContentType
  lastMessageId?: number // æœ€åŽä¸€æ¡æœåŠ¡ç«¯æ¶ˆæ¯ç¼–å·
  lastClientMessageId?: string // æœ€åŽä¸€æ¡å®¢æˆ·ç«¯æ¶ˆæ¯ç¼–号
  lastMessageStatus?: number // æœ€åŽä¸€æ¡æ¶ˆæ¯çŠ¶æ€
  lastReceiptStatus?: number // æœ€åŽä¸€æ¡ç¾¤å›žæ‰§çŠ¶æ€
  lastSelfSend?: boolean // æ˜¯å¦è‡ªå·±å‘çš„
  lastSenderDisplayName?: string // å‘送人显示名快照——仅作 utils/user.getSenderDisplayName å®žæ—¶ç®—不出真名时的 fallback
  // ========== UI çŠ¶æ€ ==========
  deleted?: boolean // æ˜¯å¦å·²åˆ é™¤ï¼ˆè½¯åˆ æ ‡è®°ï¼ŒæŒä¹…化时过滤)
  top?: boolean // æ˜¯å¦ç½®é¡¶ï¼ˆæŽ’序时优先)
  silent?: boolean // æ˜¯å¦å…æ‰“扰(不展示未读徽标 + ä¸å“æç¤ºéŸ³ï¼‰
  atMe?: boolean // ç¾¤èŠï¼šæ˜¯å¦æœ‰äºº @我
  atAll?: boolean // ç¾¤èŠï¼šæ˜¯å¦æœ‰äºº @全体成员
  reportedReadMessageId?: number // å·²ä¸ŠæŠ¥åˆ°æœåŠ¡ç«¯çš„æœ€å¤§å·²è¯»æ¶ˆæ¯ç¼–å·
  draft?: {
    html: string // è¾“入框 HTML
    plain: string // è¾“入框纯文本
    reply?: QuoteMessage // å¼•用消息
  } // è¾“入框草稿
}
// æ¶ˆæ¯æ•°æ®ç»“æž„
export interface Message {
  // ========== åŽç«¯å­—段(对齐 ImPrivateMessageNotification / ImGroupMessageNotification) ==========
  id?: number // æœåŠ¡ç«¯æ¶ˆæ¯ç¼–å·ï¼Œå‘é€ä¸­ä¸ºç©º
  clientMessageId: string // å®¢æˆ·ç«¯æ¶ˆæ¯ç¼–号,本地生成用于合并去重
  type: number // å†…容类型,对齐 ImContentType
  content: string // æ¶ˆæ¯å†…容,JSON å­—符串
  status: number // æ¶ˆæ¯çŠ¶æ€ï¼Œå¯¹é½ ImMessageStatus
  sendTime: number // å‘送时间(前端转毫秒时间戳;后端为 LocalDateTime å­—符串)
  senderId: number // å‘送人编号
  atUserIds?: number[] // ç¾¤ @ ç›®æ ‡ç”¨æˆ·åˆ—表
  receiverUserIds?: number[] // ç¾¤å®šå‘接收用户列表
  receiptStatus?: number // å›žæ‰§çŠ¶æ€ï¼Œå¯¹é½ ImMessageReceiptStatus(私聊 / ç¾¤ / é¢‘道通用)
  readCount?: number // ç¾¤å›žæ‰§å·²è¯»äººæ•°ï¼ˆä»…群消息)
  materialId?: number // å…³è”频道素材编号(仅频道消息 type=MATERIAL)
  // ========== å‰ç«¯æ‰©å±•字段 ==========
  // å‘送人显示名一律渲染时实时算:utils/user.getSenderDisplayName / getSenderRealNickname
  // ä¸åœ¨ Message ä¸Šå­˜ä»»ä½•名字快照,避免备注 / ç¾¤æ˜µç§°å˜æ›´åŽåŽ†å²æ¶ˆæ¯æ˜¾ç¤ºé™ˆæ—§
  targetId: number // ä¼šè¯ç›®æ ‡ç¼–号(私聊=对端 userId / ç¾¤èŠ=groupId),与 Conversation.targetId ä¸€è‡´
  selfSend: boolean // æ˜¯å¦è‡ªå·±å‘送(前端按 senderId è®¡ç®—)
  uploadProgress?: number // åª’体消息上传进度(0-100);status=SENDING æœŸé—´æŒç»­æ›´æ–°ï¼›ack åŽç½® undefined
  // åª’体消息内存中保留的原始 File;下划线前缀表示不进 JSON / ä¸æŒä¹…化(IDB æ¢å¤åŽå¿…为 undefined)
  // å¤±è´¥é‡è¯•时按它重走上传;页面刷新后该字段丢失,恢复阶段直接 drop æ•´æ¡æ¶ˆæ¯
  _localFile?: File
  _ackMerging?: boolean // ack åˆå¹¶ä¸­æ ‡è®°ï¼Œä¸æŒä¹…化
}
// ==================== IndexedDB æœ¬åœ°å­˜å‚¨ç»“æž„ ====================
/** ä¼šè¯ IndexedDB å­˜å‚¨ç»“æž„ */
export interface ConversationDO extends Conversation {
  clientConversationId: string // `${type}:${targetId}`
}
export interface ConversationRead {
  conversationType: number // ä¼šè¯ç±»åž‹ï¼Œå¯¹é½ ImConversationType
  targetId: number // ä¼šè¯ç›®æ ‡ç¼–号
  messageId: number // å½“前用户已读到的最大消息编号
  updateTime?: number // æ›´æ–°æ—¶é—´
}
/** ä¼šè¯è¯»ä½ç½® IndexedDB å­˜å‚¨ç»“æž„ */
export interface ConversationReadDO extends ConversationRead {
  clientConversationId: string // `${conversationType}:${targetId}`
}
/** æ¶ˆæ¯ IndexedDB å­˜å‚¨ç»“æž„ */
export interface MessageDO extends Omit<Message, '_ackMerging' | '_localFile' | 'uploadProgress'> {
  messageKey: string // `${conversationType}:${id}` æˆ– `client:${clientMessageId}`
  conversationType: number // ä¼šè¯ç±»åž‹ï¼Œå¯¹é½ ImConversationType
  clientConversationId: string // ConversationDO.clientConversationId
}
/** è®¾ç½® IndexedDB å­˜å‚¨ç»“æž„ */
export interface SettingDO<T = unknown> {
  key: string
  value: T
  updateTime?: number
}
// ==================== ç¾¤ / ç¾¤æˆå‘˜ ====================
// ç¾¤å®žä½“(前端内部结构)
export interface Group {
  // ========== åŽç«¯å­—段(对齐 ImGroupApi.GroupRespVO) ==========
  id: number // ç¾¤ç¼–号
  name: string // ç¾¤åç§°
  avatar?: string // ç¾¤å¤´åƒ
  notice?: string // ç¾¤å…¬å‘Š
  ownerUserId?: number // ç¾¤ä¸»ç”¨æˆ·ç¼–号
  pinnedMessages?: Message[] // ç¾¤ç½®é¡¶æ¶ˆæ¯åˆ—表
  mutedAll?: boolean // æ˜¯å¦å…¨ç¾¤ç¦è¨€
  banned?: boolean // æ˜¯å¦è¢«ç®¡ç†å‘˜å°ç¦
  joinApproval?: boolean // è¿›ç¾¤æ˜¯å¦éœ€ç¾¤ä¸» / ç®¡ç†å‘˜å®¡æ‰¹
  joinStatus?: number // å½“前登录用户在该群的成员状态(参见 CommonStatusEnum:0 åœ¨ç¾¤ / 1 å·²é€€ç¾¤ï¼‰ï¼›åŽ†å²é€€ç¾¤ç¾¤ä»è¿”å›žï¼Œä¾›å±•ç¤ºåŽ†å²æ¶ˆæ¯çš„ç¾¤å / å¤´åƒ
  // ========== å‰ç«¯æ‰©å±•字段(user-per-group ç»´åº¦ï¼‰ ==========
  silent?: boolean // æ˜¯å¦å…æ‰“扰。从当前用户的 GroupMember å›žå¡«
  groupRemark?: string // ç¾¤å¤‡æ³¨ã€‚从当前用户的 GroupMember å›žå¡«ï¼ˆå½“前用户对该群的自定义名)
  members?: GroupMember[] // ç¾¤æˆå‘˜ç¼“存(按需懒加载)
  infoLoaded?: boolean // ç¾¤è¯¦æƒ…是否已加载,本轮会话内存标记,不持久化
  activeCallLoaded?: boolean // ç¾¤æ´»è·ƒé€šè¯æ˜¯å¦å·²æŽ¢æµ‹ï¼Œæœ¬è½®ä¼šè¯å†…存标记,不持久化
  activeCallExpired?: boolean // ç¾¤æ´»è·ƒé€šè¯æŽ¢æµ‹æ˜¯å¦å·²è¿‡æœŸ
  membersLoaded?: boolean // members æ˜¯å¦"完整加载"——只有整群 loadGroupMemberList / fetchGroupMemberList å‘½ä¸­æ—¶ä¸º true;fetchGroupMember å•成员补齐不置位,避免 fetchGroupMemberList(force=false) å‘½ä¸­ç¼“存时误判整群已加载
  membersExpired?: boolean // ç¾¤æˆå‘˜ç¼“存是否已过期;重连 / é‡æ–°è¿›å…¥ IM åŽåªæ ‡è®°ä¸åˆ é™¤ï¼Œä¸‹æ¬¡è¿›å…¥ç¾¤ä¼šè¯å†åˆ·æ–°
  memberCount?: number // æˆå‘˜æ€»æ•°
}
/** ç¾¤ IndexedDB å­˜å‚¨ç»“æž„ */
export type GroupDO = Omit<
  Group,
  'activeCallExpired' | 'activeCallLoaded' | 'infoLoaded' | 'members' | 'membersExpired' | 'membersLoaded'
>
// ç¾¤æˆå‘˜å®žä½“(前端内部结构)
export interface GroupMember {
  // ========== åŽç«¯å­—段(对齐 ImGroupMemberApi.GroupMemberRespVO) ==========
  id?: number // ç¾¤æˆå‘˜å…³ç³»è®°å½•编号
  groupId: number // ç¾¤ç¼–号
  userId: number // ç”¨æˆ·ç¼–号
  avatar?: string // å¤´åƒ
  nickname: string // ç”¨æˆ·æ˜µç§°
  displayUserName?: string // è¯¥æˆå‘˜åœ¨ç¾¤å†…自定义昵称(每个 member ä¸€ä»½ï¼›ä¸ä¸Ž nickname åˆå¹¶ï¼Œç”±æ¶ˆè´¹æ–¹æŒ‰éœ€å–舍)
  status?: number // åœ¨ç¾¤ / é€€ç¾¤çŠ¶æ€ï¼Œå¯¹é½ CommonStatusEnum
  role?: number // æˆå‘˜è§’色,参见 ImGroupMemberRole æžšä¸¾ï¼š1=群主 2=管理员 3=普通成员
  muteEndTime?: string // ç¦è¨€åˆ°æœŸæ—¶é—´ï¼ˆISO å­—符串)
  // ========== å‰ç«¯æ‰©å±•字段 ==========
  isOwner?: boolean // æ˜¯å¦ç¾¤ä¸»ï¼ˆå‰ç«¯ä»Ž Group.ownerUserId è®¡ç®—)
}
// ==================== å¥½å‹ ====================
// å¥½å‹å®žä½“(前端内部结构)
export interface Friend {
  // ========== åŽç«¯å­—段(对齐 ImFriendApi.FriendRespVO) ==========
  id?: number // å¥½å‹å…³ç³»è®°å½•编号(本地乐观新增时可能暂缺)
  friendUserId: number // å¥½å‹ç”¨æˆ·ç¼–号(与 Conversation.targetId å¯¹é½ï¼‰
  nickname: string // å¥½å‹æ˜µç§°ï¼ˆå¯¹æ–¹çœŸå®žæ˜µç§°ï¼Œæ°¸è¿œä¸è¢«å¤‡æ³¨è¦†ç›–ï¼›UI æ˜¾ç¤ºèµ° displayName || nickname)
  nicknamePinyin?: string // æ˜µç§°çš„æ‹¼éŸ³ï¼ˆåŽç«¯ç”¨ Pinyin4j ç®—好回填,小写无空格)
  avatar?: string // å¥½å‹å¤´åƒ
  silent?: boolean // æ˜¯å¦å…æ‰“扰(不展示未读徽标 + ä¸å“æç¤ºéŸ³ï¼‰
  displayName?: string // å¥½å‹å±•示备注:仅自己可见的别名(单字段不歧义,不带 Friend å‰ç¼€ï¼‰
  displayNamePinyin?: string // å¤‡æ³¨çš„æ‹¼éŸ³ï¼ˆåŽç«¯ç”¨ Pinyin4j ç®—好回填,小写无空格)
  status?: number // å¥½å‹çŠ¶æ€ï¼Œå¯¹é½ CommonStatusEnum(DISABLE = å·²åˆ é™¤ï¼Œè½¯åˆ ä¿ç•™è®°å½•)
  addSource?: number // æ·»åŠ æ¥æºï¼›å‚è§ ImFriendAddSourceEnum
  pinned?: boolean // æ˜¯å¦ç½®é¡¶è”系人
  blocked?: boolean // æ˜¯å¦æ‹‰é»‘(仅自己可见,单边屏蔽对方私聊消息)
  addTime?: number // æ·»åŠ å¥½å‹æ—¶é—´ï¼ˆæ¯«ç§’æ—¶é—´æˆ³ï¼›åŽç«¯ä¸º LocalDateTime å­—符串,在 convertFriend è½¬æ¢ï¼‰
  deleteTime?: number // åˆ é™¤å¥½å‹æ—¶é—´ï¼ˆæ¯«ç§’时间戳;后端为 LocalDateTime å­—符串,在 convertFriend è½¬æ¢ï¼‰
}
/**
 * å¥½å‹ç”³è¯·è®°å½•(前端内部结构,对齐后端 ImFriendRequestApi.FriendRequestRespVO)
 */
export interface FriendRequest {
  // ========== åŽç«¯å­—段(对齐 ImFriendRequestApi.FriendRequestRespVO) ==========
  id: number // ç”³è¯·ç¼–号
  fromUserId: number // å‘起方用户编号
  toUserId: number // æŽ¥æ”¶æ–¹ç”¨æˆ·ç¼–号
  handleResult: number // å¤„理结果:0=未处理;1=同意;2=拒绝
  applyContent?: string // ç”³è¯·ç†ç”±ï¼ˆå‘起方填写)
  handleContent?: string // å¤„理理由(接收方拒绝时可选填)
  addSource?: number // æ·»åŠ æ¥æºï¼›å‚è§ ImFriendAddSourceEnum
  handleTime?: number // å¤„理时间(毫秒时间戳)
  createTime: number // ç”³è¯·åˆ›å»ºæ—¶é—´ï¼ˆæ¯«ç§’时间戳)
  // ========== èšåˆå­—段(自 AdminUser,仅展示用) ==========
  fromNickname?: string // å‘起方昵称
  fromAvatar?: string // å‘起方头像
  toNickname?: string // æŽ¥æ”¶æ–¹æ˜µç§°
  toAvatar?: string // æŽ¥æ”¶æ–¹å¤´åƒ
}
// ==================== ç”¨æˆ·åç‰‡ ====================
// ç”¨æˆ·ç²¾ç®€ä¿¡æ¯ï¼ˆå¯¹é½åŽç«¯ UserSimpleRespVO,名片 / å¤´åƒ hover ç­‰åœºæ™¯å…±ç”¨ï¼‰
export interface User {
  id: number
  nickname?: string
  avatar?: string
  sex?: number
  deptId?: number
  deptName?: string
}
// ==================== åˆ—表行展示用 Lite ç±»åž‹ ====================
/**
 * å¥½å‹åˆ—表行:从 Friend æ´¾ç”Ÿçš„展示快照
 * - id ç”¨ friendUserId(与列表 click / é€‰ä¸­æ¯”对一致),不是 Friend.id(关系记录主键)
 * - è½¯åˆ ï¼ˆstatus === DISABLE)由上游 friendStore.getActiveFriendList / getActiveFriendLiteList ç»Ÿä¸€è¿‡æ»¤æŽ‰
 */
export interface FriendLite {
  id: number
  nickname: string
  nicknamePinyin?: string // æ˜µç§°æ‹¼éŸ³ï¼ˆç”¨äºŽå­—母分桶 / æ‹¼éŸ³æœç´¢ï¼‰
  avatar?: string
  displayName?: string
  displayNamePinyin?: string // å¤‡æ³¨æ‹¼éŸ³ï¼ˆä¼˜å…ˆäºŽ nicknamePinyin å‚与分桶)
}
/**
 * ç¾¤åˆ—表行:从 Group æ´¾ç”Ÿçš„展示快照
 * - showGroupName / showImage:调用方决定带不带备注(如个人备注群名);展示按 show* > åŽŸå€¼å…œåº•
 * - showImageThumb:高频列表用缩略图,避免拉原图阻塞滚动
 */
export interface GroupLite {
  id: number
  name?: string
  showGroupName?: string
  showImage?: string
  showImageThumb?: string
  memberCount?: number
  ownerId?: number
  joinApproval?: boolean // è¿›ç¾¤æ˜¯å¦éœ€ç¾¤ä¸» / ç®¡ç†å‘˜å®¡æ‰¹
}