msg.js 38.3 KB
Newer Older
DCloud_JSON's avatar
DCloud_JSON 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
const {
  db,
  dbCmd,
  $,
  getConversationId,
  checkParam
} = require('uni-im-utils')

const dbUniImMsg = db.collection('uni-im-msg')
const dbUniImConversation = db.collection('uni-im-conversation')
const dbUniImGroupMember = db.collection('uni-im-group-member')
const dbUniImFriend = db.collection('uni-im-friend')

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
14
// 获取 uni-im 配置
DCloud_JSON's avatar
DCloud_JSON 已提交
15 16
const createConfig = require("uni-config-center");
const uniImConfig = createConfig({
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
17
  pluginId: 'uni-im', // 插件 id
DCloud_JSON's avatar
DCloud_JSON 已提交
18
})
19
const conversation_grade = uniImConfig.config('conversation_grade')
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
20
const { invokeExts } = require('uni-im-ext')
DCloud_JSON's avatar
DCloud_JSON 已提交
21 22
// 发送消息方法(含:单聊和群聊)
async function sendMsg(params) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
23 24 25 26 27 28 29 30 31
  
  // 执行扩展点 beforeSendMsg
  let [beforeSendMsgRes] = await invokeExts('before-send-msg', params, this)
  // console.error('beforeSendMsgRes:', beforeSendMsgRes)
  if (beforeSendMsgRes === false) {
    throw new Error('扩展点 beforeSendMsgRes 出错')
  }


DCloud_JSON's avatar
DCloud_JSON 已提交
32 33 34
  await _beforeSendMsgActions.call(this, params)

  let {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
35
    // 指定接收消息的用户 id
DCloud_JSON's avatar
DCloud_JSON 已提交
36
    to_uid,
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
37
    // 指定接收消息的群 id
DCloud_JSON's avatar
DCloud_JSON 已提交
38 39 40 41 42 43 44
    group_id,
    // 消息内容
    body,
    // 消息类型
    type,
    // 是否为失败 重试
    isRetries,
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
45
    // 接收消息的客户端的 DCloud appid
DCloud_JSON's avatar
DCloud_JSON 已提交
46
    appId,
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
47
    // 回复关联的消息的 id
DCloud_JSON's avatar
DCloud_JSON 已提交
48 49 50 51 52 53
    about_msg_id,
    call_uid,
    action,
    // 是否为静默消息
    is_mute
  } = params
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
54 55

  // 清除旧版三方系统客户端直传的 call_uid 参数
DCloud_JSON's avatar
DCloud_JSON 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69
  call_uid = []

  // 校验参数是否合法
  checkParam(params, {
    required: ["body", "type", "appId"],
    type: {
      to_uid: ["String"],
      group_id: ["String"],
      body: ["String", "Object", "Array"],
      type: ["String"],
      isRetries: ["Boolean"],
      appId: ["String"],
      about_msg_id: ['String'],
      action: ['String'],
70 71
      is_mute: ['Boolean'],
      chat_source: ['Object']
DCloud_JSON's avatar
DCloud_JSON 已提交
72 73 74
    }
  })

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
75
  // 存在 action 时,如果不是其他云函数或者触发器调用时,且调用者也不是系统时,拦截非法操作。
DCloud_JSON's avatar
DCloud_JSON 已提交
76 77 78 79 80 81 82
  if (action && this.getClientInfo().source != 'function' && this.current_uid != 'system') {
    throw new Error('非法操作')
  }

  // 调用扩展点 valid-msg
  let result = await invokeExts('validate-msg', params, this)
  let isValid = result.find(valid => typeof valid !== 'undefined')
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
83 84

  if (!isValid && is_mute !== undefined && this.current_uid !== 'system') {
DCloud_JSON's avatar
DCloud_JSON 已提交
85 86 87 88 89
    throw new Error('非法操作')
  }

  // 补充特殊校验
  if (!to_uid && !group_id) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
90
    throw new Error('接收消息的用户 id 和群 id 最少指定一个') // alert:否则表示将消息群发
DCloud_JSON's avatar
DCloud_JSON 已提交
91 92
  }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
93
  // 其他云函数或者触发器调用时,消息类型才能用 system
DCloud_JSON's avatar
DCloud_JSON 已提交
94 95 96 97 98
  if (this.getClientInfo().source != 'function' && type == 'system') {
    throw new Error('非法消息类型')
  }

  if (type == 'text' && typeof body != 'string') {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
99
    throw new Error('错误:type 为 text 类型,但 body 的类型确不是 string')
DCloud_JSON's avatar
DCloud_JSON 已提交
100 101
  }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
102
  // 发送者身份 id
DCloud_JSON's avatar
DCloud_JSON 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
  const from_uid = this.current_uid

  await _checkConversationGrade.call(this, {
    from_uid,
    to_uid,
    group_id,
  })

  // TODO: 群聊时,需要校验当前用户是否为群成员,后续会优化性能问题
  if (this.current_uid != 'system' && group_id) {
    let {
      data: [member]
    } = await dbUniImGroupMember
      .where({
        group_id,
        user_id: this.current_uid
      })
      .get()
    // console.log('member', member);
    if (!member) {
      throw new Error('非群成员不能发起会话')
    } else if (!['creator', 'admin'].includes(...member.role) && member.mute_type > 0) {
      throw new Error('你已被禁言')
    }
  }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141
  let nickname = "系统消息",avatar_file = "";
  if (from_uid && from_uid != 'system') {
    const { data: [userInfo] } = await db.collection('uni-id-users').doc(from_uid).get()
    if (userInfo) {
      nickname = userInfo.nickname
      avatar_file = userInfo.avatar_file
    } else {
      console.error('用户信息不存在',from_uid);
    }
  }

  // 生成会话 id
  const conversation_id = getConversationId({group_id,from_uid,to_uid})
DCloud_JSON's avatar
DCloud_JSON 已提交
142 143 144 145 146 147 148
  // 现在的时间戳
  const nowTimestamp = Date.now()
  // 构建基本消息内容
  const msgData = {
    body,
    type,
    from_uid,
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
149 150 151 152
    // 消息发送者昵称
    nickname,
    // 消息发送者头像文件
    avatar_file,
DCloud_JSON's avatar
DCloud_JSON 已提交
153
    to_uid,
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
154
    // 是否已读,默认为:false
DCloud_JSON's avatar
DCloud_JSON 已提交
155
    is_read: false,
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
156
    // 创建时间
DCloud_JSON's avatar
DCloud_JSON 已提交
157 158 159 160 161
    create_time: nowTimestamp,
    // 更新时间,默认为创建时间
    update_time: nowTimestamp,
    conversation_id,
    group_id,
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
162
    appid: appId, // 接收消息的 appid(撤回消息时会用到)
DCloud_JSON's avatar
DCloud_JSON 已提交
163 164 165 166 167 168
    about_msg_id,
    call_uid, // @某些人
    action,
    reader_list: [], // 已读消息的用户列表
    is_mute // 是否为静默消息
  }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
169

DCloud_JSON's avatar
DCloud_JSON 已提交
170 171 172 173 174

  if (type === 'rich-text') {
    let callUid = []
    for (let node of msgData.body) {
      if (node.name == 'span' && node.attrs && node.attrs.class == 'nickname' && node.attrs.user_id) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
175
        // 保护用户昵称,移除。客户端将通过 node.attrs.user_id,获取用户昵称
DCloud_JSON's avatar
DCloud_JSON 已提交
176 177 178 179 180 181 182
        delete node.children
        callUid.push(node.attrs.user_id)
      }
    }
    if (callUid.length) {
      msgData.call_uid = callUid
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
183 184 185
  } else if (type === 'order') {
    // 发送订单消息时,需要 call 此订单的用户
    msgData.call_uid = msgData.body.user_id || []
DCloud_JSON's avatar
DCloud_JSON 已提交
186 187 188
  }

  // 创建新会话或者更新已有会话。
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
189 190
  // 拿到消息接收者的 isMute 状态(仅私聊有效)
  let {isMute} = await _createOrUpdateConversation.call(this, conversation_id, msgData, params.chat_source)
DCloud_JSON's avatar
DCloud_JSON 已提交
191 192 193 194 195 196 197 198 199 200 201 202 203
  // console.log({
  // 	...msgData,
  // 	conversation_id
  // });

  // 如果是扩展的消息类型,由扩展模块决定是否要保存入库
  let noPersistent = false
  const { msgTypes } = require('uni-im-ext')
  let msgType = await msgTypes.get(type)
  if (msgType && msgType.noPersistent) {
    noPersistent = msgType.noPersistent(msgData)
  }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
204 205 206 207 208 209 210 211 212 213
  // todo:临时增加私有代码逻辑,后续会迁移到扩展模块中
  if (type === 'system' && action === 'set-group-member-ext-plugin-order-info') {
    // 群成员设置插件排序信息,不需要保存至数据库
    noPersistent = true
  }

  // 如果是无需保存至数据库的消息,因缺失 _id ,将影响客户端对该消息接收状态的判断,所以这里需要创建临时 _id
  if (noPersistent) {
    msgData._id = 'temp_' + Math.random().toString(36).substr(2) + Date.now()
  }
DCloud_JSON's avatar
DCloud_JSON 已提交
214
  // 将消息内容存到数据库,“点击重发按钮的”和“不需要保存的扩展消息”除外
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
215
  else if (!isRetries || !msgData._id) {
DCloud_JSON's avatar
DCloud_JSON 已提交
216 217 218 219 220 221 222 223 224 225
    const __text = _extractTextFromMsg(msgData)
    if (__text) {
      msgData.__text = __text
      // console.log(' msgData.__text', msgData.__text);
    }

    let res = await dbUniImMsg.add({
      ...msgData,
      conversation_id
    })
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
226

DCloud_JSON's avatar
DCloud_JSON 已提交
227 228 229 230 231 232 233
    msgData._id = res.id
    // console.error('uni-im-msg msgData:',msgData, res);

    // 调用扩展点 send-msg
    // 这里需要 clone,因为是异步调用,而下面的代码马上就要修改 msgData
    let { ...clonedMsg } = msgData
    this.addPromise(invokeExts('send-msg', clonedMsg, this))
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
234 235

    // 客户端不需要返回__text 字段
DCloud_JSON's avatar
DCloud_JSON 已提交
236 237
    delete msgData.__text
  } else {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
238
    // console.error('跳过添加 msg 到数据库!~~~~~~~~~')
DCloud_JSON's avatar
DCloud_JSON 已提交
239
  }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
240 241 242

  // 不可见/静音消息一定是 isMute
  if (!_isReadableMsg(msgData) || msgData.is_mute) {
DCloud_JSON's avatar
DCloud_JSON 已提交
243 244 245 246
    isMute = true
  }

  // 处理产生的推送
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
247
  let res = await _processPush.call(this, {msgData,isMute,appId})
DCloud_JSON's avatar
DCloud_JSON 已提交
248 249 250 251 252 253 254

  if (!res.data) {
    res.data = {}
  }

  // 返回云端消息记录创建的时间
  res.data.create_time = msgData.create_time
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
255
  // 返回云端消息记录的 id
DCloud_JSON's avatar
DCloud_JSON 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
  res.data._id = msgData._id
  return res
}


// 执行发送消息前的操作
async function _beforeSendMsgActions(params) {
  if (params.type === 'revoke_msg') {
    let res = await _revokeMsg.call(this, params.body.msg_id)
    let {
      appid,
      group_id,
      to_uid
    } = res
    params.appId = appid
    params.group_id = group_id
    params.to_uid = to_uid
    return
  }

  // 如果是扩展的消息类型,由扩展模块执行前置操作
  const { msgTypes } = require('uni-im-ext')
  let msgType = await msgTypes.get(params.type)
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
279
  if (msgType?.beforeSendMsg) {
DCloud_JSON's avatar
DCloud_JSON 已提交
280
    await msgType.beforeSendMsg(params, this.current_uid)
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
281
    console.error('############----->beforeSendMsg', params)
DCloud_JSON's avatar
DCloud_JSON 已提交
282 283 284 285 286 287 288 289 290
    return
  }
}

async function _checkConversationGrade({
  from_uid,
  to_uid,
  group_id,
}) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
291
  // 客服模式下,如果配置的客服 id。则只能向客服发起会话
DCloud_JSON's avatar
DCloud_JSON 已提交
292
  let chatToCustomerService = async () => {
293 294 295 296 297 298 299 300
    const customer_service_uids = uniImConfig.config('customer_service_uids') || []
    if (typeof customer_service_uids == 'string') {
      customer_service_uids = [customer_service_uids]
    }
    if (
      !(customer_service_uids.includes(from_uid) || customer_service_uids.includes(to_uid))
    ) {
      throw new Error('非法通讯,会话双方用户 id,均不属于 uni-im-co 中配置的 customer_service_uids')
DCloud_JSON's avatar
DCloud_JSON 已提交
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
    }
    return true
  }

  // 只能是好友关系,或者群成员才能发送
  let chatToFriendOrGroupMember = async () => {
    if (group_id) {
      let {
        data: [has]
      } = await dbUniImGroupMember
        .where({
          group_id,
          user_id: this.current_uid
        })
        .get()
      if (!has) {
        throw new Error('非群成员不能发起会话')
      }
    }
    if (to_uid) {
      let {
        data: [has]
      } = await dbUniImFriend
        .where({
          friend_uid: to_uid,
          user_id: this.current_uid
        })
        .get()
      if (!has) {
        throw new Error('非好友不能发起会话')
      }
    }
    return true
  }

  /** 读取配置的对话等级,校验是否有权发送消息
   * 	0	-	任何人可以发起会话
   *	100	-	客服 or 好友或者群成员
   *	200	-	必须是好友或者群成员
   **/
  switch (conversation_grade) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
342 343 344 345 346 347 348 349 350 351 352 353 354 355
    case 0:
      // 任何人可以发起会话,不校验
      break;
    case 100:
      // 客服 or 好友或者群成员
      try {
        await chatToCustomerService()
      } catch (error) {
        console.error(error)
        await chatToFriendOrGroupMember()
      }
      break;
    case 200:
      // 必须是好友或者群成员
DCloud_JSON's avatar
DCloud_JSON 已提交
356
      await chatToFriendOrGroupMember()
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
357 358 359
      break;
    default:
      break;
DCloud_JSON's avatar
DCloud_JSON 已提交
360 361 362 363 364 365 366 367
  }
}

function _getLastMsgNote({
  body,
  type,
  action,
}) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
368
  // 文本 => 文本的前 30 个字
DCloud_JSON's avatar
DCloud_JSON 已提交
369 370 371 372 373 374 375 376 377 378
  // 其他 => 消息类型
  let last_msg_note = '[多媒体]'
  if (type == 'text') {
    last_msg_note = body.toString()
    last_msg_note = last_msg_note.replace(/[\r\n]/g, "");
    last_msg_note = last_msg_note.slice(0, 30)
  } else if (type === 'userinfo-card') {
    last_msg_note = `${body.name} 的名片`
  } else if (type === 'system') {
    switch (action) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
      case 'group-join':
        last_msg_note = '加入群聊'
        break;
      case 'group-exit':
        last_msg_note = '退出群聊'
        break;
      case 'group-expel':
        last_msg_note = '被踢出群聊'
        break;
      default:
        // 更新群资料
        if (action.includes("update-group-info-")) {
          const { fieldsName } = body;
          last_msg_note = `[${fieldsName + '更新'}]`;
        }
        break;
DCloud_JSON's avatar
DCloud_JSON 已提交
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
    }
  } else {
    last_msg_note = {
      image: "[图片]",
      sound: "语音",
      video: "[视频]",
      file: "[文件]",
      location: "[位置]",
      system: "[系统消息]",
      code: "[代码]",
      'rich-text': "[图文消息]",
    } [type] || `[${type}]`
  }
  return last_msg_note
}

411
async function _createOrUpdateConversation(conversation_id, msgData, chat_source) {
DCloud_JSON's avatar
DCloud_JSON 已提交
412 413 414 415 416 417 418 419 420
  // 设置会话 最后一条消息 的描述
  let last_msg_note = _getLastMsgNote(msgData)

  // 查询当前用户的此会话
  const {
    data: conversationList
  } = await dbUniImConversation
    .where(
      dbCmd.and([
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
421
        { id: conversation_id },
DCloud_JSON's avatar
DCloud_JSON 已提交
422
        dbCmd.or([
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
423 424
          { user_id: msgData.from_uid },
          { group_id: dbCmd.exists(false) }
DCloud_JSON's avatar
DCloud_JSON 已提交
425 426 427 428 429 430 431 432 433 434
        ])
      ])
    )
    .get()
  // console.log('conversation', conversation);
  // 消息发送者的会话
  let senderConversation = conversationList.find(item => item.user_id == msgData.from_uid)
  // 消息接收者的会话(此数据仅私聊时存在)
  let receiverConversation = conversationList.find(item => item.user_id == msgData.to_uid)
  const isMute = (receiverConversation ? receiverConversation.mute : false)
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
435 436 437 438
  if (msgData.type === 'read_msg'){
    // todo:临时方案,拦截已读动作更新会话
   // console.error('已读动作无需更新会话') 
  }
DCloud_JSON's avatar
DCloud_JSON 已提交
439
  // 不存在,需要先创建会话记录
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
440
  else if (!senderConversation) {
441
    // 除了云函数之间(包括触发器)调用 和 特殊角色用户,需验证是否绑定了手机号码
442
    const check_mobile = uniImConfig.config('check_mobile')
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
443
    if (
444
      check_mobile &&
445 446 447
      this.getClientInfo().source != 'function' &&
      !this.current_user_role.includes('uni-im-admin') &&
      !this.current_user_role.includes('staff')
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
448
    ) {
449 450
      // 验证账号是否绑定了手机号
      let getUserInfoRes = await db.collection('uni-id-users')
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
451 452 453 454 455 456 457
        .doc(this.current_uid)
        .field({
          mobile_confirmed: 1
        })
        .get()
      if (!getUserInfoRes.data[0].mobile_confirmed) {
        throw new Error('账号未绑定手机号无法发送消息,请完成绑定后退出并重新登录本系统后重试。')
458 459
      }
    }
460 461 462 463
    const clientInfo = this.getClientInfo()
    if( conversation_grade === 300 && msgData.to_uid && clientInfo.source != 'function' && this.current_uid != 'system'){
      // console.error('createOrUpdateConversation conversation_grade 300~~~~~',clientInfo.appVersionCode);
      // TODO:用于向下兼容
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
464
      if (clientInfo.appVersionCode < 24042501) {
465 466 467 468 469
        throw new Error('你的客户端版本过旧,不能发起新会话。请刷新后重试')
      }
      // 仅限:系统管理员参与的 或 群成员向群管理员 发起私聊
      let check = false
      // 消息发送者是系统管理员
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
470
      if (this.current_user_role.includes('staff')) {
471 472
        check = true
        // console.error('createOrUpdateConversation staff~~~~~');
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
473
      } else if (chat_source && chat_source.group_id) {
474 475 476 477 478
        // console.error('createOrUpdateConversation group_id~~~~~');
        // 临时会话来源群,判断双方是否有一个是群管理员
        let {
          data: [member],
        } = await dbUniImGroupMember
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
479 480 481 482 483 484 485
          .where({
            group_id: chat_source.group_id,
            user_id: dbCmd.in([msgData.from_uid, msgData.to_uid]),
            role: dbCmd.in(['admin'])
          })
          .get()
        if (member) {
486 487 488
          check = true
          // console.error('createOrUpdateConversation member~~~~~', member);
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
489
      } else {
490 491 492 493
        // 判断消息接收者是否为系统管理员(staff)
        let {
          data: [receiver],
        } = await db.collection('uni-id-users')
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
494 495 496 497 498 499 500
          .doc(msgData.to_uid)
          .field({
            role: 1,
            nickname: 1,
            avatar_file: 1,
          })
          .get()
501
        // console.error('createOrUpdateConversation receiver~~~~~', receiver);
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
502
        if (receiver && receiver.role && receiver.role.includes('staff')) {
503 504 505 506
          // console.error('createOrUpdateConversation receiver.staff~~~~~');
          check = true
        }
      }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
507 508

      if (!check) {
509 510 511 512 513 514 515 516
        let {
          data: [has]
        } = await dbUniImFriend
          .where({
            friend_uid: msgData.to_uid,
            user_id: this.current_uid
          })
          .get()
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
517
        if (has) {
518 519 520
          check = true
        }
      }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
521 522

      if (!check) {
523 524 525 526
        throw new Error('仅限:好友之间、系统管理员参与的、群成员与群管理员,发起私聊')
      }
    }

DCloud_JSON's avatar
DCloud_JSON 已提交
527 528 529 530 531 532 533 534 535 536
    // 1.消息发送者 会话数据
    senderConversation = {
      id: conversation_id,
      type: msgData.group_id ? 2 : 1,
      user_id: msgData.from_uid,
      friend_uid: msgData.to_uid,
      group_id: msgData.group_id,
      unread_count: 0,
      last_msg_note,
      update_time: msgData.create_time,
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
537 538 539 540 541
      create_time: msgData.create_time,
      last_msg_create_time: msgData.create_time,
      pinned: false,
      hidden: false,
      mute: false
DCloud_JSON's avatar
DCloud_JSON 已提交
542 543 544
    }
    if (msgData.group_id) {
      senderConversation.leave = false
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
545 546 547 548 549 550 551 552 553
      senderConversation.has_unread_group_notification = null
      // 拿到group_type
      let {data:[groupInfo]} = await db.collection('uni-im-group')
        .doc(msgData.group_id)
        .field({
          type: true,
        })
        .get()
      senderConversation.group_type = groupInfo?.type
DCloud_JSON's avatar
DCloud_JSON 已提交
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
      // 群聊只为当前用户创建会话
      await dbUniImConversation.add(senderConversation)
    } else {
      // 2.消息接收者 会话数据
      receiverConversation = {
        ...senderConversation,
        unread_count: 1,
        user_id: msgData.to_uid,
        friend_uid: msgData.from_uid
      }
      // 单聊,同时为收发双方创建此会话
      await dbUniImConversation.add([senderConversation, receiverConversation])
    }
  } else {
    // 会话已存在,更新相关内容
    let updateObj = {
      last_msg_note,
      update_time: msgData.create_time,
      hidden: false
    }

    // 如果不是:用户自己给自己发消息,则需要增加未读消息数
    if (msgData.from_uid != msgData.to_uid) {
      updateObj.unread_count = dbCmd.inc(1)
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
579

DCloud_JSON's avatar
DCloud_JSON 已提交
580
    // TODO:部分情况下不需要更新未读消息数,后续会改成都通过is_mute字段来判断
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
581
    if (
DCloud_JSON's avatar
DCloud_JSON 已提交
582
      // 加群消息
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
583
      msgData.action === "join-group"
DCloud_JSON's avatar
DCloud_JSON 已提交
584 585
      ||
      // 禁言通知
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
586
      msgData.action === 'update-group-info-mute_all_members'
DCloud_JSON's avatar
DCloud_JSON 已提交
587 588 589
      ||
      // 本身是一条静默消息
      msgData.is_mute === true
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
590
    ) {
DCloud_JSON's avatar
DCloud_JSON 已提交
591 592
      delete updateObj.unread_count
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
593
    
DCloud_JSON's avatar
DCloud_JSON 已提交
594 595 596 597 598 599 600 601
    if (!(await _isReadableMsg(msgData))) {
      // “非可见消息”不更新“最后一次对话消息概述”
      delete updateObj.last_msg_note
    }
    if (_isMuteMsg(msgData) || !(await _isReadableMsg(msgData))) {
      // “静音消息”不更新“未读消息数”
      delete updateObj.unread_count
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
602 603 604 605 606
    
    // 如果有last_msg_note,增加一个last_msg_create_time字段
    if (updateObj.last_msg_note) {
      updateObj.last_msg_create_time = msgData.create_time
    }
DCloud_JSON's avatar
DCloud_JSON 已提交
607 608 609 610 611

    // 所有接收者的会话表更新
    let res = await dbUniImConversation
      .where({
        id: conversation_id,
612 613
        user_id: dbCmd.neq(msgData.from_uid),
        leave: dbCmd.neq(true)
DCloud_JSON's avatar
DCloud_JSON 已提交
614 615 616 617
      })
      .update(updateObj)
    // console.log(res);

618
    // 更新发送者的会话( 发送者的消息未读数不需要 +1 )
DCloud_JSON's avatar
DCloud_JSON 已提交
619 620 621 622
    delete updateObj.unread_count
    res = await dbUniImConversation
      .where({
        id: conversation_id,
623
        user_id: msgData.from_uid
DCloud_JSON's avatar
DCloud_JSON 已提交
624 625 626 627
      })
      .update(updateObj)
    // console.log(res);
  }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
628

DCloud_JSON's avatar
DCloud_JSON 已提交
629 630 631 632 633 634 635
  // 返回消息接收者的isMute状态(仅私聊有效)
  return {
    isMute
  }
}

function _extractTextFromMsg(msgData) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
    let __text = null
    if (["text", "code"].includes(msgData.type)) {
        __text = msgData.body.replace(/[\s\n]+/g, " ")
    } else if (["image", "video", "file"].includes(msgData.type)) {
        __text = msgData.body.name
    } else if (msgData.type == "rich-text") {
        let getTextOfNodeList = (nodesList) => {
            let text = ''
            nodesList.forEach(item => {
                if (item.type == "text") {
                    text += item.text
                }
                if (Array.isArray(item.children)) {
                    text += " " + getTextOfNodeList(item.children)
                    if (item.attrs && item.attrs.class === 'nickname') {
                        // 增加空格,用于区分昵称和消息内容
                        text += " "
                    }
                }
            })
            return text
DCloud_JSON's avatar
DCloud_JSON 已提交
657
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
658
        __text = getTextOfNodeList(msgData.body).replace(/[\s\n]+/g, " ")
DCloud_JSON's avatar
DCloud_JSON 已提交
659 660
    }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
661 662 663 664 665 666 667
    if (__text) {
        // 全部转成小写,以便进行大小写不敏感的匹配
        __text = __text.toLowerCase()
        // 24 位 hex 会被 mongodb 自动转换成 ObjectId,需阻止
        if (__text.match(/^[0-9a-f]{24}$/)) {
            __text = __text + ' '
        }
DCloud_JSON's avatar
DCloud_JSON 已提交
668 669
    }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
670
    return __text
DCloud_JSON's avatar
DCloud_JSON 已提交
671 672 673
}

function _checkForLongMsg(msgData) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
674 675 676
    let bodyStr = JSON.stringify(msgData.body)
    if (bodyStr > 50000) {
        throw new Error('单次消息最长字符长度不得超过 50000')
DCloud_JSON's avatar
DCloud_JSON 已提交
677
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
    if (bodyStr.length > 250) {
        switch (msgData.type) {
            case 'rich-text':
                msgData.body = "富文本消息"
                break;
            case 'history':
                msgData.body = "转发的聊天记录"
                break;
            case 'text':
                // 截断,但标识消息为长文本消息,客户端收到此标识,会从数据库中拉取完整消息内容
                msgData.body = msgData.body.slice(0, 50)
                break;
            default:
                msgData.body = `[${msgData.type}]类型消息`
                break;
        }
DCloud_JSON's avatar
DCloud_JSON 已提交
694

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
695 696
        msgData.LongMsg = true
    }
DCloud_JSON's avatar
DCloud_JSON 已提交
697 698 699
}

async function _checkForReplyMsg(msgData) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
700
    if (!msgData.about_msg_id) return
DCloud_JSON's avatar
DCloud_JSON 已提交
701

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
702
    let { 
DCloud_JSON's avatar
DCloud_JSON 已提交
703
    data: [aboutMsg]
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
704 705 706
    } = await dbUniImMsg.doc(msgData.about_msg_id).get()
    if (!aboutMsg) {
        throw new Error('方法的 about_msg_id')
DCloud_JSON's avatar
DCloud_JSON 已提交
707
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
708 709 710 711 712
    if (!Array.isArray(msgData.call_uid)) {
        msgData.call_uid = []
    }
    msgData.call_uid.push(aboutMsg.from_uid)
    // console.log(987,aboutMsg,msgData.call_uid);
DCloud_JSON's avatar
DCloud_JSON 已提交
713 714 715
}

async function _processPush({ msgData, isMute, appId }) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
    // 处理超长文本,push 发送不了的问题
    _checkForLongMsg(msgData)

    // 处理 消息回复 @某人的
    await _checkForReplyMsg(msgData)

    const { nickname, avatar_file } = msgData
    const title = msgData.type == 'system'? '系统消息' : nickname.slice(0, 20)
    const content = msgData.type == 'text'? msgData.body : '[多媒体]'
    // 定义推送参数
    const pushParam = {
        payload: {
            type: "uni-im",
            data: msgData,
            title, // "收到 im 消息,在线时显示的标题",
            content, // "在线时显示的副标题",
            avatar_file, // 头像文件对象,
            device_id: this.clientInfo.deviceId // 发送消息的设备(客户端)id,阻止当前用户收到自己发的消息
        },
        title: title.slice(0, 20), // "收到 im 消息,离线时显示的标题",
        content: content.slice(0, 50) //"离线时显示的内容"
    }
DCloud_JSON's avatar
DCloud_JSON 已提交
738

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
    let res

    if (msgData.from_uid!= 'system' && (msgData.to_uid || msgData.group_id)) {
        // 以"非离线"且免打扰的消息(避免系统通知栏出现消息)同步给当前用户的其他设备(比如 web 和 app、小程序同时登录时;注:发送此消息的“设备”在客户端通过业务代码过滤,这里不排除)
        this.addPromise(
            this.sendPushMsg({
               ...pushParam,
                user_id: msgData.from_uid,
                settings: {
                    strategy: {
                        default: 3
                    }
                }
            }, appId)
        )
    }
DCloud_JSON's avatar
DCloud_JSON 已提交
755

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
    if (msgData.to_uid) {
        // WS 推送:单聊消息推送给接收方
        if (!isMute &&!_isMuteMsg(msgData) && (await _isReadableMsg(msgData))) {
          this.addPromise(
            new Promise(resolve => {
              // 给500毫秒时间,确保请求大概率能发出去,但不接收响应,防止请求异常导致的等待时间过长
                setTimeout(()=>{
                  invokeExts('push-msg-notify', {
                    to_uids: [msgData.to_uid],
                    msg: {
                      type: 'incr-conversation',
                      conversation_id: msgData.conversation_id,
                    }
                  })
                  resolve()
                }, 500)
            })
          )
        }
DCloud_JSON's avatar
DCloud_JSON 已提交
775

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
776 777 778 779 780 781 782 783 784
        // 单聊,直接调用 before 中封装好的消息推送方法
        pushParam.user_id = msgData.to_uid
        if (isMute) {
            console.log('消息接收者设置了免打扰,或本身就是一条静音消息,应用离线时,此消息不会有系统通知')
            pushParam.settings = {
                strategy: {
                    default: 3
                }
            }
DCloud_JSON's avatar
DCloud_JSON 已提交
785
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
786 787 788 789 790 791 792 793 794 795 796
        res = await this.sendPushMsg(pushParam, appId)
        // console.log('sendMessage', JSON.stringify(res))
        /*
        //判断是否已经有客户端接收到消息,注意:收到不等于已读
        let taskData = res.data[Object.keys(res.data)]
        let state = false;
        for (let key in taskData) {
            if (taskData[key] == 'successed_online') {
                state = true
                break
            }
DCloud_JSON's avatar
DCloud_JSON 已提交
797
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
        console.log('state : ============> ' + state);*/
        res.data = {} // 不返回给客户端发送结果
    } else if (msgData.group_id) {
        this.addPromise(
            // 把当前发消息的用户的,在群成员中的活跃时间更新
            db.collection('uni-im-group-member').where({
                group_id: msgData.group_id,
                user_id: msgData.from_uid
            }).update({
                active_time: Date.now()
            })
        )

        // 如果是群聊则调用 sendMsgToGroup 云方法,此方法内部会递归发送(500 个为用户一批)
        if (this.getClientInfo().clientIP == "127.0.0.1") {
            // 如果是本地调试,直接 await 调用(因为本地调试下,云函数互调“不是调用新实例”,必须等待执行结束,注:相比生产环境的速度会更慢。)
            const start = Date.now() // 计时
            let sendMsgToGroupRes = await sendMsgToGroup()
            console.error(
                'sendMsgToGroupRes', sendMsgToGroupRes,
                'sendMsgToGroup 耗时:', Date.now() - start, 'ms',
                '因为本地调试下,云函数互调“不是调用新实例”,必须等待执行结束,注:相比生产环境的速度会更慢。'
            )
        } else {
            sendMsgToGroup()
            // 等待 500 毫秒,给一个请求发出去的时间
            this.addPromise(
                new Promise(resolve => {
                    setTimeout(resolve, 500)
                })
            )
DCloud_JSON's avatar
DCloud_JSON 已提交
829
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
830 831 832 833 834 835 836 837 838 839 840
        function sendMsgToGroup() {
            return uniCloud.importObject('uni-im-co').sendMsgToGroup({
                pushParam,
                appId
            })
        }
        res = {
            errCode: 0
        }
    } else {
        throw new Error('接受者标识,不能为空')
DCloud_JSON's avatar
DCloud_JSON 已提交
841
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
842
    return res
DCloud_JSON's avatar
DCloud_JSON 已提交
843 844 845
}

async function sendMsgToGroup({
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
846 847 848 849 850 851 852
    pushParam,
    before_id,
    push_clientids = [],
    member = [],
    appId,
    // 默认先给未设置消息免打扰的用户发消息
    mute = false
DCloud_JSON's avatar
DCloud_JSON 已提交
853
}) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
854 855 856 857 858 859 860 861
    // 注意:这是一个递归云对象,用递归的方式处理批量任务
    const limit = 1000
    if (this.getClientInfo().source!= 'function') {
        return {
            errSubject: 'uni-im-co',
            errCode: 0,
            errMsg: '该方法仅支持云对象的方法,或者触发器调用'
        }
DCloud_JSON's avatar
DCloud_JSON 已提交
862 863
    }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
864 865
    // console.log('sendMsgToGroup=========', {
    // 	pushParam,
DCloud_JSON's avatar
DCloud_JSON 已提交
866 867 868 869
    // 	before_id,
    // 	push_clientids
    // });

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
870 871 872 873 874 875
    if (before_id || push_clientids) {
        // console.log({
        // 	before_id,
        // 	push_clientids
        // });
        // return 123
DCloud_JSON's avatar
DCloud_JSON 已提交
876
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
877 878 879 880 881 882

    if (push_clientids.length === 0) {
        // console.log('开始查库', push_clientids.length, push_clientids);
        let group_id = pushParam.payload.data.group_id
        if (!group_id) {
            throw new Error('群 id 不能为空')
883
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
884 885 886 887 888 889
        let getMemberwhere = {
            group_id,
            mute: mute? true : dbCmd.neq(true),
            leave: dbCmd.neq(true),
            // 需要排除自己
            user_id: dbCmd.neq(pushParam.payload.data.from_uid)
890
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
891 892 893 894 895 896 897

        if (before_id) {
            getMemberwhere._id = dbCmd.gt(before_id)
        }
        // console.log({
        // 	getMemberwhere
        // });
898
        let res = await dbUniImConversation
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
           .aggregate()
           .match(getMemberwhere)
           .sort({
                _id: 1
            })
           .limit(limit)
           .project({
                user_id: 1,
                mute: 1
            })
           .lookup({
                from: "uni-id-device",
                let: {
                    user_id: '$user_id'
                },
                pipeline: $.pipeline()
                   .match(
                        dbCmd.expr(
                            $.and([
                                $.eq(['$user_id', '$$user_id']),
                                $.gt(['$token_expired', Date.now()])
                            ])
                        )
                    ).project({
                        push_clientid: 1
                    })
                   .done(),
                as: 'push_clientids',
            })
           .end()
        member = res.data
        console.error('符合条件的' + (mute? '【设置消息免打扰的用户】' : '') + '用户数', member.length); //,member
        push_clientids = member.reduce((sum, item) => {
            sum.push(...item.push_clientids.map(i => i.push_clientid))
            return sum
        }, [])
        console.log('查到需要接收消息的设备数:', push_clientids.length);

        // 消息接收者用户 id
        const receiverUids = member.map(user => user.user_id)
        const msgData = pushParam.payload.data

        // console.error('###########msgData.action', msgData.action);
        // 更新:退群、解散群、被踢出群 相关的用户会话
        if (receiverUids.length && ["group-exit", "group-expel", "group-dissolved"].includes(msgData.action)) {
            const updateUids = []
            if (['group-exit', 'group-expel'].includes(msgData.action)) {
                // 主动退群/被踢出群 时,仅退群用户所在“消息推送批次”时更新会话即可
                msgData.body.user_id_list.forEach(uid => {
                    if (receiverUids.includes(uid)) {
                        updateUids.push(uid)
                    }
                })
            } else {
                // 群解散时,所有“本批次”的用户会话都需要更新(注:分批 解散/退出群 的设计模式)
                updateUids.push(...receiverUids)
                // 解散群时,在第一批次时,增加更新操作者(群主)的会话
                if (!before_id) {
                    updateUids.push(msgData.from_uid)
                }
            }

            if (updateUids.length) {
                let res = await dbUniImConversation
                   .where({
                        id: msgData.conversation_id,
                        user_id: dbCmd.in(updateUids)
                    })
                   .update({ "leave": true })
                // console.error('###########更新退群或者被踢出群的用户会话', res,updateUids);
            }
        }
DCloud_JSON's avatar
DCloud_JSON 已提交
971

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
        // WS 推送:群聊消息推送给相关用户
        if (!mute &&!_isMuteMsg(msgData) && (await _isReadableMsg(msgData))) {
          this.addPromise(
            new Promise(resolve => {
              // 给500毫秒时间,确保请求大概率能发出去,但不接收响应,防止请求异常导致的等待时间过长
                setTimeout(()=>{
                  invokeExts('push-msg-notify', {
                    to_uids: receiverUids,
                    msg: {
                      type: 'incr-conversation',
                      conversation_id: msgData.conversation_id,
                    }
                  })
                  resolve()
                }, 500)
            })
          )
DCloud_JSON's avatar
DCloud_JSON 已提交
989
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
990 991
    } else {
        console.log('不需要查库,继续发送任务', push_clientids.length);
DCloud_JSON's avatar
DCloud_JSON 已提交
992 993
    }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
994 995 996 997 998 999 1000 1001 1002 1003
    if (push_clientids.length === 0) {
        if (mute) {
            // console.log('已没有设置了免打扰的用户需要接收消息');
            return {
                errCode: 0,
                errMsg: '',
            }
        } else {
            // console.log('已没有未设置免打扰的用户需要接收消息,继续查询设置免打扰的用户');
        }
DCloud_JSON's avatar
DCloud_JSON 已提交
1004
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019

    // 下一批推送的设备 id
    let next_push_clientids = push_clientids.slice(limit)
    // 当前这一批要推送的设备 id
    push_clientids = push_clientids.slice(0, limit)
    if (push_clientids.length > 0) {
        pushParam.push_clientid = push_clientids
        // console.log("pushParam", pushParam);
        // 如果是给免打扰的用户发消息,需要设置策略,只通过个推通道下发,不考虑设备是否在线
        if (mute || pushParam.payload.data.is_mute) {
            pushParam.settings = {
                strategy: {
                    default: 3
                }
            }
DCloud_JSON's avatar
DCloud_JSON 已提交
1020
        }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1021 1022 1023
        // 执行推送
        let sendPushMsgRes = await this.sendPushMsg(pushParam, appId)
        // console.error(sendPushMsgRes)
DCloud_JSON's avatar
DCloud_JSON 已提交
1024
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041

    if (next_push_clientids.length!== 0) {
        // 发起下一批数据的推送
        uniCloud.importObject('uni-im-co').sendMsgToGroup({
            pushParam,
            push_clientids: next_push_clientids,
            member,
            mute
        }, appId)
        // 等待 500 毫秒,给一个请求发出去的时间
        return await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve({
                    errCode: 0,
                    errMsg: ''
                })
            }, 500)
DCloud_JSON's avatar
DCloud_JSON 已提交
1042
        })
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
    } else if (member.length == limit) {
        // 成员数等于分页数,则处于递归翻页进行中,且当前批次的数据已经全部推送完毕,继续查询下一批数据
        // console.log('member---*--*', member);
        before_id = member[member.length - 1]._id
        uniCloud.importObject('uni-im-co').sendMsgToGroup({
            pushParam,
            before_id,
            mute
        }, appId)
        // 等待 500 毫秒,给一个请求发出去的时间
        return await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve({
                    errCode: 0,
                    errMsg: ''
                })
            }, 500)
DCloud_JSON's avatar
DCloud_JSON 已提交
1060
        })
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
    } else {
        if (!mute) {
            // 如果之前不是给设置了免打扰的用户发消息,接下来需要给设置了免打扰的用户发消息
            uniCloud.importObject('uni-im-co').sendMsgToGroup({
                pushParam,
                mute: true
            }, appId)
            // 等待 500 毫秒,给一个请求发出去的时间
            return await new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve({
                        errCode: 0,
                        errMsg: ''
                    })
                }, 500)
            })
        } else {
            // console.log('没有更多用户需要接收消息');
            return {
                errCode: 0,
                errMsg: ''
            }
        }
DCloud_JSON's avatar
DCloud_JSON 已提交
1084 1085 1086 1087
    }
}

async function getMsgList({
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1088 1089 1090
    conversation_ids,
    minUpdateTime,
    limit
DCloud_JSON's avatar
DCloud_JSON 已提交
1091
}) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
    // 校验 conversation_ids 是否为当前用户
    let {
      data: conversationList
    } = await dbUniImConversation.where({
        id: dbCmd.in(conversation_ids),
        user_id: this.current_uid,
        leave: dbCmd.neq(true)
    }).get()
    if (conversationList.length!== conversation_ids.length) {
        throw new Error('无效的会话 id')
    }
DCloud_JSON's avatar
DCloud_JSON 已提交
1103

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113
    // 查询消息列表
    let res = await dbUniImMsg.where({
        conversation_id: dbCmd.in(conversation_ids),
        update_time: dbCmd.gt(minUpdateTime)
    })
    .orderBy('update_time', 'asc')
    .limit(limit)
    .get()

    return res
DCloud_JSON's avatar
DCloud_JSON 已提交
1114 1115 1116
}

async function _revokeMsg(msgId) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1117
    //1. 先查到这条消息
DCloud_JSON's avatar
DCloud_JSON 已提交
1118
    let {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1119 1120 1121 1122
      data: [msgData]
    } = await dbUniImMsg.doc(msgId).get()
    if (!msgData) {
        throw new Error('无效的消息 id')
DCloud_JSON's avatar
DCloud_JSON 已提交
1123
    }
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
    // 如果不是 im 产品的管理员
    if (!this.current_user_role.includes('uni-im-admin') && this.getClientInfo().source!= 'function') {
        let {
          conversation_id,
          from_uid,
          appid: appId,
          group_id,
          create_time
        } = msgData

        // 判断是不是群主
        let isGroupAdmin = false
        if (group_id) {
            let res = await dbUniImGroupMember
            .where({
                group_id,
                user_id: this.current_uid
            })
            .get()
            let role = res.data[0].role || []
            isGroupAdmin = res.data[0] && role.includes('admin')
        }
        // console.error('isGroupAdmin',isGroupAdmin)

        // 如果不是群主,且消息不是发送者的就无权撤回
        if (!isGroupAdmin) {
            if (from_uid!= this.current_uid) {
                throw new Error('你不是消息的发送者或群管理员,无权操作')
            } else {
                // 消息发送者为“当前用户”时,消息创建时间需要时间小于 2 分钟
                if (Date.now() - create_time > 1000 * 60 * 2) {
                    throw {
                        errSubject: 'uni-im-co',
                        errCode: 10004,
                        errMsg: '消息已超过 2 分钟不能撤回'
                    }
                }
            }
DCloud_JSON's avatar
DCloud_JSON 已提交
1162 1163 1164
        }
    }

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1165 1166
    // 改变云数据库中此消息的撤回状态
    let res = await dbUniImMsg
DCloud_JSON's avatar
DCloud_JSON 已提交
1167 1168
    .doc(msgId)
    .update({
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1169 1170
        is_revoke: true,
        update_time: Date.now()
DCloud_JSON's avatar
DCloud_JSON 已提交
1171
    })
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1172
    return msgData
DCloud_JSON's avatar
DCloud_JSON 已提交
1173 1174 1175
}

function _isMuteMsg(msg) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
    return (
        // 加群消息
        msg.action === "join-group"
        ||
        // 禁言通知
        msg.action === 'update-group-info-mute_all_members'
        ||
        // 消息本身是静默消息
        msg.is_mute === true
    )
DCloud_JSON's avatar
DCloud_JSON 已提交
1186 1187 1188
}

async function _isReadableMsg(msg) {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
    if (msg.type === 'revoke_msg') return false
    if (msg.action === 'update-group-info-avatar_file') return false
    if (msg.type === 'clear-conversation-unreadCount') return false

    // 如果是扩展的消息类型,由扩展模块决定消息是否可见
    const { msgTypes } = require('uni-im-ext')
    const msgTypesGetSatrt = Date.now()
    let msgType = await msgTypes.get(msg.type)
    // console.error('msgTypes.get', Date.now() - msgTypesGetSatrt)
    if (msgType && msgType.isReadable) {
        return msgType.isReadable(msg)
    }
DCloud_JSON's avatar
DCloud_JSON 已提交
1201

DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1202
    return true
DCloud_JSON's avatar
DCloud_JSON 已提交
1203 1204 1205
}

module.exports = {
DCloud_JSON's avatar
3.4.31  
DCloud_JSON 已提交
1206 1207 1208 1209
    sendMsg,
    sendMsgToGroup,
    getMsgList
}