1132 lines
29 KiB
JavaScript
1132 lines
29 KiB
JavaScript
// 云对象教程: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj
|
||
// jsdoc语法提示教程:https://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/129
|
||
const db = uniCloud.database();
|
||
// 会话表
|
||
const conversationTable = db.collection('uni-im-conversation')
|
||
// 消息表
|
||
const msgTable = db.collection('uni-im-msg')
|
||
// 群成员表
|
||
const groupMemberTable = db.collection('uni-im-group-member')
|
||
// 用户表
|
||
const usersTable = db.collection('uni-id-users')
|
||
// uni-id公共模块
|
||
const uniIdCommon = require('uni-id-common')
|
||
const dbCmd = db.command
|
||
const $ = dbCmd.aggregate
|
||
|
||
// 哈希算法,用于生成会话id
|
||
const md5 = require("md5");
|
||
|
||
// 获取uni-im配置
|
||
const createConfig = require("uni-config-center");
|
||
const uniImConfig = createConfig({
|
||
pluginId: 'uni-im', // 插件id
|
||
})
|
||
|
||
module.exports = {
|
||
async _before() {
|
||
// 获取客户端信息
|
||
this.clientInfo = this.getClientInfo()
|
||
// console.log(this.clientInfo);
|
||
// 定义uni-id公共模块对象
|
||
this.uniIdCommon = uniIdCommon.createInstance({
|
||
clientInfo: this.clientInfo
|
||
})
|
||
|
||
// 指定需要使用当前用户身份id的云对的方法名称,即:需要验证用户的token信息
|
||
const needLoginMethodName = [
|
||
"sendMsg",
|
||
"revokeMsg",
|
||
"addFriendInvite",
|
||
"getJoinGroupList",
|
||
"getConversationList",
|
||
"chooseUserIntoGroup"
|
||
]
|
||
|
||
if (
|
||
needLoginMethodName.includes(this.getMethodName())
|
||
) {
|
||
// 云函数互调时,免校验token直接使用传来的用户id
|
||
if (this.getClientInfo().source == 'function') {
|
||
this.current_uid = this.getParams()[1]
|
||
} else {
|
||
let res = await this.uniIdCommon.checkToken(this.clientInfo.uniIdToken)
|
||
if (res.errCode) {
|
||
// 如果token校验出错,则抛出错误
|
||
throw res
|
||
} else {
|
||
// 通过token校验则,拿去用户id
|
||
this.current_uid = res.uid
|
||
}
|
||
}
|
||
}
|
||
|
||
// 提醒用户需要连接云端云函数
|
||
|
||
// if(this.getMethodName() == 'sendMsg' && this.clientInfo.clientIP == "127.0.0.1"){
|
||
// throw {
|
||
// errSubject: 'uni-im-co-sendMsg',
|
||
// errCode: "uni-im-co-sendMsg-error",
|
||
// errMsg: '发送消息方法,仅限连接云端云函数使用(在HBuilderX uniCloud控制台切换)'
|
||
// }
|
||
// }
|
||
|
||
// 封装push方法
|
||
this.sendPushMsg = async (param, appId) => {
|
||
let pushParam = {
|
||
// 验证token是否有效,无效则不向此设备推送消息
|
||
check_token: true,
|
||
settings: {
|
||
//-1表示不设离线,因为离线后重新打开数据统一从数据库中拉取。否则会重复
|
||
ttl: -1
|
||
},
|
||
// 离线推送厂商信息配置,需要到云厂商后台申请
|
||
channel: {
|
||
// 华为离线推送
|
||
"HW": "NORMAL",
|
||
// 小米离线推送
|
||
"XM": "high_system"
|
||
}
|
||
}
|
||
/**
|
||
* 如果不指定接收消息的客户端appid,则指定为消息推送者的客户端appid。
|
||
* 用于两个客户端appid不同的场景,比如电商项目,商家和普通用户端appid不同
|
||
*/
|
||
if (!appId) {
|
||
appId = this.clientInfo.appId
|
||
if (!appId) {
|
||
throw new Error('appId is not definded')
|
||
}
|
||
}
|
||
pushParam = Object.assign(pushParam, param)
|
||
|
||
// 如果是im通知消息(比如:加好友申请,用户请求加群,用户退群等),则记录到数据表uni-im-notification中
|
||
if (param.payload.type == "uni-im-notification") {
|
||
let {
|
||
title,
|
||
content,
|
||
payload,
|
||
sound,
|
||
open_url,
|
||
path,
|
||
user_id
|
||
} = pushParam
|
||
let notificationContent = {
|
||
title,
|
||
content,
|
||
payload,
|
||
sound,
|
||
open_url,
|
||
path
|
||
}
|
||
notificationContent.is_read = false
|
||
notificationContent.create_time = Date.now()
|
||
let notificationData;
|
||
// 如果接收消息的用户量不止一个,则需要插入数据表的记录为多条(数组)
|
||
if (Array.isArray(user_id)) {
|
||
notificationData = user_id.map(uid => {
|
||
return {
|
||
user_id: uid,
|
||
...notificationContent
|
||
}
|
||
})
|
||
} else {
|
||
notificationData = Object.assign(notificationContent, {
|
||
user_id
|
||
})
|
||
}
|
||
// 执行写入到数据库
|
||
let uinRes = await db.collection('uni-im-notification').add(notificationData)
|
||
// console.log('uinRes',uinRes)
|
||
param.payload.notificationId = uinRes.id
|
||
}
|
||
|
||
let res = await uniCloud.getPushManager({
|
||
appId
|
||
}).sendMessage(pushParam)
|
||
if (res.errCode) {
|
||
if (res.errCode == "uni-push-user-invalid") {
|
||
//可能因为用户长时间没有登录导致的cid过期而发送失败,但是此时已将离线数据写入数据库,登录后可获取。客户端不需要进入 catch
|
||
res = {
|
||
data: {
|
||
"uni-push-err-res": res
|
||
},
|
||
errCode: 0
|
||
}
|
||
} else {
|
||
console.log(res.errCode);
|
||
throw new Error(res.errMsg)
|
||
}
|
||
}
|
||
return res
|
||
}
|
||
},
|
||
_after(error, result) {
|
||
if (error) {
|
||
console.log({
|
||
error,
|
||
result
|
||
});
|
||
if (error.errCode && error.errMsg) {
|
||
// 符合响应体规范的错误,直接返回
|
||
return error
|
||
} else {
|
||
throw error // 直接抛出异常
|
||
}
|
||
}
|
||
return result
|
||
},
|
||
// 获取会话列表
|
||
async getConversationList({
|
||
// 会话条数
|
||
limit = 500,
|
||
// 最大的会话更新时间
|
||
maxUpdateTime = false,
|
||
// 不为false则表示获取指定的会话
|
||
conversation_id = false
|
||
}) {
|
||
let matchObj = {
|
||
// 限制只能查询当前用户自己的会话记录
|
||
"user_id": this.current_uid,
|
||
// "group_id": dbCmd.exists(false), //是否查询群聊会话
|
||
}
|
||
if (conversation_id) {
|
||
matchObj.id = conversation_id
|
||
} else if (maxUpdateTime) {
|
||
matchObj.update_time = dbCmd.lt(maxUpdateTime)
|
||
}
|
||
|
||
let res = await conversationTable.aggregate()
|
||
.sort({
|
||
// 根据更新时间倒序
|
||
update_time: -1
|
||
})
|
||
.match(matchObj)
|
||
.limit(limit)
|
||
// 联查获得最新的对话记录
|
||
.lookup({
|
||
from: "uni-im-msg",
|
||
let: {
|
||
id: '$id'
|
||
},
|
||
pipeline: $.pipeline()
|
||
.match(
|
||
dbCmd.expr($.eq(['$conversation_id', '$$id']))
|
||
)
|
||
.sort({
|
||
create_time: -1
|
||
})
|
||
.limit(1)
|
||
.project({
|
||
_id: 1,
|
||
type: 1,
|
||
body: 1,
|
||
to_uid: 1,
|
||
is_read: 1,
|
||
from_uid: 1,
|
||
create_time: 1,
|
||
conversation_id: 1
|
||
})
|
||
.done(),
|
||
as: 'msgList'
|
||
})
|
||
// 联查获得对话的用户信息
|
||
// .lookup({
|
||
// from: "uni-id-users",
|
||
// let: {
|
||
// friend_uid: '$friend_uid'
|
||
// },
|
||
// pipeline: $.pipeline()
|
||
// .match(
|
||
// dbCmd.expr($.eq(['$_id', '$$friend_uid']))
|
||
// )
|
||
// .limit(1)
|
||
// .project({
|
||
// _id: 1,
|
||
// avatar_file: 1,
|
||
// nickname: 1
|
||
// })
|
||
// .done(),
|
||
// as: 'user_info'
|
||
// })
|
||
// 联查获得对话的群信息
|
||
.lookup({
|
||
from: "uni-im-group",
|
||
let: {
|
||
group_id: '$group_id'
|
||
},
|
||
pipeline: $.pipeline()
|
||
.match(
|
||
dbCmd.expr($.eq(['$_id', '$$group_id']))
|
||
)
|
||
.project({
|
||
user_id: 1,
|
||
name: 1,
|
||
introduction: 1,
|
||
notification: 1,
|
||
avatar_file: 1,
|
||
join_option: 1
|
||
})
|
||
.done(),
|
||
as: 'group_info'
|
||
})
|
||
.end()
|
||
|
||
const dbJQL = uniCloud.databaseForJQL({
|
||
clientInfo: this.clientInfo
|
||
})
|
||
let friend_uids = res.data.map(item=>item.friend_uid).filter(i=>i)
|
||
let usersInfoRes = await dbJQL.collection('uni-id-users')
|
||
.where(`_id in ${JSON.stringify(friend_uids)}`)
|
||
.field('_id,avatar_file,nickname')
|
||
.get()
|
||
let usersInfoObj = {}
|
||
usersInfoRes.data.forEach(item=>{
|
||
usersInfoObj[item._id] = item
|
||
})
|
||
res.data.forEach(item=>{
|
||
item.user_info = [usersInfoObj[item.friend_uid]]
|
||
})
|
||
return {
|
||
data: res.data,
|
||
errCode: 0
|
||
}
|
||
},
|
||
// 发送消息方法(含:单聊和群聊)
|
||
async sendMsg(params, context) {
|
||
let {
|
||
// 指定接收消息的用户id
|
||
to_uid,
|
||
// 指定接收消息的群id
|
||
group_id,
|
||
// 消息内容
|
||
body,
|
||
// 消息类型
|
||
type,
|
||
// 是否为失败 重试
|
||
isRetries,
|
||
// 接收消息的客户端的DCloud appid
|
||
appId,
|
||
// 回复关联的消息的id
|
||
about_msg_id
|
||
} = params
|
||
|
||
// 校验参数是否合法
|
||
check_param(params, {
|
||
"required": ["body", "type", "appId"],
|
||
"type": {
|
||
to_uid: ["String"],
|
||
group_id: ["String"],
|
||
body: ["String", "Object"],
|
||
type: ["String"],
|
||
isRetries: ["Boolean"],
|
||
appId: ["String"],
|
||
about_msg_id: ['String']
|
||
}
|
||
})
|
||
|
||
// 补充特殊校验
|
||
if (!to_uid && !group_id) {
|
||
throw new Error('接收消息的用户id和群id最少指定一个') // alert:否则表示将消息群发
|
||
}
|
||
|
||
//发送者身份id
|
||
const from_uid = this.current_uid
|
||
|
||
/** 读取配置的对话等级,校验是否有权发送消息
|
||
* 0 - 任何人可以发起会话
|
||
* 100 - 客服 or 好友或者群成员
|
||
* 200 - 必须是好友或者群成员
|
||
**/
|
||
const conversation_grade = uniImConfig.config('conversation_grade')
|
||
|
||
// 客服模式下,如果配置的客服id。则只能向客服发起会话
|
||
async function chatToCustomerService() {
|
||
const customer_service_uids = uniImConfig.config('customer_service_uids_uids')
|
||
if (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')
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
// 只能是好友关系,或者群成员才能发送
|
||
async function chatToFriendOrGroupMember() {
|
||
if (group_id) {
|
||
let {
|
||
data: [has]
|
||
} = await db.collection('uni-im-group-member')
|
||
.where({
|
||
group_id,
|
||
user_id: this.current_uid
|
||
})
|
||
.get()
|
||
if (!has) {
|
||
throw new Error('非群成员不能发起会话')
|
||
}
|
||
}
|
||
if (to_uid) {
|
||
let {
|
||
data: [has]
|
||
} = await db.collection('uni-im-friend')
|
||
.where({
|
||
friend_uid: to_uid,
|
||
user_id: this.current_uid
|
||
})
|
||
.get()
|
||
if (!has) {
|
||
throw new Error('非好友不能发起会话')
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
switch (conversation_grade) {
|
||
case 0:
|
||
//任何人可以发起会话,不校验
|
||
break;
|
||
case 100:
|
||
// 客服 or 好友或者群成员
|
||
try {
|
||
await chatToCustomerService()
|
||
} catch (error) {
|
||
console.error(error)
|
||
await chatToFriendOrGroupMember()
|
||
}
|
||
break;
|
||
case 200:
|
||
// 必须是好友或者群成员
|
||
await chatToFriendOrGroupMember()
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
// 生成会话id
|
||
const conversation_id = group_id ? 'group_' + group_id : getConversationId([from_uid, to_uid])
|
||
|
||
// 构建基本消息内容
|
||
const msgData = {
|
||
body,
|
||
type,
|
||
from_uid,
|
||
to_uid,
|
||
//是否已读,默认为:false
|
||
is_read: false,
|
||
//创建时间
|
||
create_time: Date.now(),
|
||
conversation_id,
|
||
group_id,
|
||
appid: appId, //接收消息的appid(撤回消息时会用到)
|
||
about_msg_id
|
||
}
|
||
|
||
// 设置会话 最后一条消息 的描述。文本=>文本的前30个字 其他=>消息类型
|
||
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 {
|
||
last_msg_note = {
|
||
"image": "[图片]",
|
||
"sound": "语音",
|
||
"video": "[视频]",
|
||
"file": "文件",
|
||
"location": "位置"
|
||
} [type]
|
||
}
|
||
|
||
// 查会话表是否存在
|
||
let {
|
||
data: [conversation]
|
||
} = await conversationTable.where({
|
||
id: conversation_id
|
||
}).get()
|
||
// 如果没有,需要先创建会话记录
|
||
if (!conversation) {
|
||
// 1.消息接收者 会话数据
|
||
let data1 = {
|
||
id: conversation_id,
|
||
type: msgData.group_id ? 2 : 1,
|
||
user_id: msgData.to_uid,
|
||
friend_uid: msgData.from_uid,
|
||
group_id: msgData.group_id,
|
||
unread_count: 1,
|
||
last_msg_note,
|
||
update_time: msgData.create_time,
|
||
create_time: msgData.create_time
|
||
}
|
||
// 2.消息发送者 会话数据
|
||
let data2 = {
|
||
...data1,
|
||
unread_count: 0,
|
||
user_id: msgData.from_uid,
|
||
friend_uid: msgData.to_uid
|
||
}
|
||
let res = await conversationTable.add([data1, data2])
|
||
// console.log(res);
|
||
} else {
|
||
// 对方的会话表更新,并且未读数+1
|
||
let res = await conversationTable.where({
|
||
id: conversation_id,
|
||
user_id: to_uid
|
||
}).update({
|
||
unread_count: dbCmd.inc(1),
|
||
last_msg_note,
|
||
update_time: msgData.create_time
|
||
})
|
||
// console.log(res);
|
||
//自己的会话表更新。仅更新最后一条消息的时间和内容
|
||
res = await conversationTable.where({
|
||
id: conversation_id,
|
||
user_id: from_uid
|
||
}).update({
|
||
last_msg_note,
|
||
update_time: msgData.create_time
|
||
})
|
||
// console.log(res);
|
||
}
|
||
// console.log({
|
||
// ...msgData,
|
||
// conversation_id
|
||
// });
|
||
|
||
// 将消息内容存到数据库,点击重发按钮的除外
|
||
if (!isRetries) {
|
||
let res = await msgTable.add({
|
||
...msgData,
|
||
conversation_id
|
||
})
|
||
msgData._id = res.id
|
||
// console.log('msgTable msgData:', res);
|
||
}
|
||
|
||
// 处理超长文本,push发送不了的问题
|
||
if (msgData.body.length > 250) {
|
||
// 截断,但标识消息为长文本消息,客户端收到此标识,会从数据库中拉取完整消息内容
|
||
msgData.body = msgData.body.slice(0, 50)
|
||
msgData.LongMsg = true
|
||
}
|
||
|
||
// 查询消息发送者的头像和昵称
|
||
const dbJQL = uniCloud.databaseForJQL({
|
||
clientInfo: this.clientInfo
|
||
})
|
||
let res = await dbJQL.collection('uni-id-users')
|
||
.doc(from_uid)
|
||
.field('_id,nickname,avatar_file')
|
||
.get()
|
||
|
||
let {
|
||
nickname,
|
||
avatar_file
|
||
} = res.data[0]
|
||
|
||
let title = nickname.slice(0, 20)
|
||
let content = msgData.type == 'text' ? msgData.body : '[多媒体]'
|
||
|
||
// 定义推送参数
|
||
let pushParam = {
|
||
// 同时发给对方和当前用户自己(包括了他的其他设备,比如web和app、小程序同时登录时,实现消息同步;注:发送此消息的设备会在客户端过滤)
|
||
"user_id": [msgData.to_uid, msgData.from_uid],
|
||
"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) //"离线时显示的内容"
|
||
}
|
||
|
||
if (msgData.to_uid) {
|
||
//单聊,直接调用before中封装好的消息推送方法
|
||
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
|
||
}
|
||
}
|
||
console.log('state : ============> ' + state);*/
|
||
} else if (msgData.group_id) {
|
||
// console.error('群聊消息*-----------------',pushParam);
|
||
delete pushParam.user_id
|
||
// 如果是群聊则调用sendMsgToGroup云方法,递归发送(500个为用户一批)
|
||
uniCloud.importObject('uni-im-co')
|
||
.sendMsgToGroup({
|
||
pushParam,
|
||
appId
|
||
})
|
||
// 等待500毫秒,给一个请求发出去的时间
|
||
await new Promise((resolve, reject) => {
|
||
setTimeout(() => {
|
||
resolve()
|
||
}, 500)
|
||
})
|
||
res = {
|
||
errCode: 0
|
||
}
|
||
} else {
|
||
throw new Error('接受者标识,不能为空')
|
||
}
|
||
|
||
if (!res.data) {
|
||
res.data = {}
|
||
}
|
||
|
||
// 返回云端消息记录创建的时间
|
||
res.data.create_time = msgData.create_time
|
||
// 返回云端消息记录的id
|
||
res.data._id = msgData._id
|
||
return res
|
||
},
|
||
async sendPushMsg(pushParam, appId) {
|
||
// 注意:这是用于在触发器中调用推送的方法。不是this.sendPushMsg调用的方法,this.sendPushMsg定义在before中
|
||
if (this.getClientInfo().source != 'function') {
|
||
return {
|
||
errSubject: 'uni-im-co',
|
||
errCode: 0,
|
||
errMsg: '该方法仅支持云对象的方法,或者触发器调用'
|
||
}
|
||
}
|
||
// console.log('pushParam', pushParam);
|
||
return await this.sendPushMsg(pushParam, appId)
|
||
},
|
||
async sendMsgToGroup({
|
||
pushParam,
|
||
before_id,
|
||
push_clientids = [],
|
||
member = [],
|
||
appId
|
||
}) {
|
||
// 注意:这是一个递归云对象,用递归的方式处理批量任务
|
||
const limit = 500 //腾讯云收费版服务空间可以改成 1000
|
||
if (this.getClientInfo().source != 'function') {
|
||
return {
|
||
errSubject: 'uni-im-co',
|
||
errCode: 0,
|
||
errMsg: '该方法仅支持云对象的方法,或者触发器调用'
|
||
}
|
||
}
|
||
|
||
// console.log('sendMsgToGroup=========', {
|
||
// pushParam,
|
||
// before_id,
|
||
// push_clientids
|
||
// });
|
||
|
||
if (before_id || push_clientids) {
|
||
// console.log({
|
||
// before_id,
|
||
// push_clientids
|
||
// });
|
||
// return 123
|
||
}
|
||
|
||
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不能为空')
|
||
}
|
||
let getMemberwhere = {
|
||
group_id
|
||
}
|
||
if (before_id) {
|
||
getMemberwhere._id = dbCmd.gt(before_id)
|
||
}
|
||
// console.log({
|
||
// getMemberwhere
|
||
// });
|
||
let res = await groupMemberTable
|
||
.aggregate()
|
||
.match(getMemberwhere)
|
||
.sort({
|
||
_id: 1
|
||
})
|
||
.limit(limit)
|
||
.project({
|
||
user_id: 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('符合条件的用户数', member, member.length);
|
||
push_clientids = member.reduce((sum, item) => {
|
||
sum.push(...item.push_clientids.map(i => i.push_clientid))
|
||
return sum
|
||
}, [])
|
||
// console.error('查到需要接收消息的设备数:', push_clientids.length);
|
||
} else {
|
||
// console.log('不需要查库,继续发送任务', push_clientids.length);
|
||
}
|
||
|
||
if (push_clientids.length === 0) {
|
||
// console.error('没有更多用户需要接收消息');
|
||
return {
|
||
errCode: 0,
|
||
errMsg: ''
|
||
}
|
||
} else {
|
||
// console.log('push_clientids====>', push_clientids)
|
||
}
|
||
let next_push_clientids = push_clientids.slice(limit)
|
||
push_clientids = push_clientids.slice(0, limit)
|
||
pushParam.push_clientid = push_clientids
|
||
console.log("pushParam", pushParam);
|
||
|
||
let sendPushMsgRes = await this.sendPushMsg(pushParam, appId)
|
||
// console.error(sendPushMsgRes)
|
||
if (next_push_clientids.length !== 0) {
|
||
uniCloud.importObject('uni-im-co').sendMsgToGroup({
|
||
pushParam,
|
||
push_clientids: next_push_clientids,
|
||
member
|
||
}, appId)
|
||
// 等待500毫秒,给一个请求发出去的时间
|
||
return await new Promise((resolve, reject) => {
|
||
setTimeout(() => {
|
||
resolve({
|
||
errCode: 0,
|
||
errMsg: ''
|
||
})
|
||
}, 500)
|
||
})
|
||
} else if (member.length == limit) {
|
||
// console.log('member---*--*', member);
|
||
before_id = member[member.length - limit]._id
|
||
uniCloud.importObject('uni-im-co').sendMsgToGroup({
|
||
pushParam,
|
||
before_id
|
||
}, appId)
|
||
// 等待500毫秒,给一个请求发出去的时间
|
||
return await new Promise((resolve, reject) => {
|
||
setTimeout(() => {
|
||
resolve({
|
||
errCode: 0,
|
||
errMsg: ''
|
||
})
|
||
}, 500)
|
||
})
|
||
} else {
|
||
return {
|
||
errCode: 0,
|
||
errMsg: ''
|
||
}
|
||
}
|
||
},
|
||
async addFriendInvite({
|
||
to_uid,
|
||
message
|
||
}) {
|
||
const from_uid = this.current_uid
|
||
// console.log('from_uid-----', from_uid);
|
||
if (this.current_uid == to_uid) {
|
||
throw new Error('不能加自己为好友')
|
||
}
|
||
|
||
let {
|
||
data: [has]
|
||
} = await db.collection('uni-im-friend').where({
|
||
user_id: from_uid,
|
||
friend_uid: to_uid
|
||
}).get()
|
||
|
||
if (has) {
|
||
return {
|
||
errSubject: 'uni-im-co',
|
||
errCode: 1000,
|
||
errMsg: '已经是好友'
|
||
}
|
||
}
|
||
|
||
// 检查是不是黑名单
|
||
//略
|
||
let docId = md5(JSON.stringify([from_uid, to_uid]))
|
||
|
||
// 不存在就添加,存在就更新
|
||
let res = await db.collection('uni-im-friend-invite').doc(docId)
|
||
.set({
|
||
from_uid,
|
||
to_uid,
|
||
message
|
||
})
|
||
|
||
// console.log({
|
||
// res
|
||
// });
|
||
/*const dbJQL = uniCloud.databaseForJQL({
|
||
clientInfo
|
||
})
|
||
dbJQL.setUser({
|
||
uid: from_uid, // 建议此处使用真实uid
|
||
role: ['admin'], // 指定当前执行用户的角色为admin。如果只希望指定为admin身份,可以删除uid和permission节点
|
||
permission: []
|
||
})*/
|
||
|
||
const dbJQL = uniCloud.databaseForJQL({
|
||
clientInfo: this.clientInfo
|
||
})
|
||
let {
|
||
data: [userInfo]
|
||
} = await dbJQL.collection('uni-id-users')
|
||
.doc(this.current_uid)
|
||
.field('_id,nickname,avatar_file')
|
||
.get()
|
||
// console.log({
|
||
// userInfo
|
||
// });
|
||
let {
|
||
nickname
|
||
} = userInfo
|
||
let title = nickname.slice(0, 20),
|
||
content = message || "请求添加对方为好友"
|
||
let pushParam = {
|
||
user_id: to_uid,
|
||
payload: {
|
||
type: "uni-im-notification", // im消息通知,比如加好友请求,有用户退群等
|
||
subType: "uni-im-friend-invite", // 通知子类型(可选)
|
||
avatar_file: userInfo.avatar_file, // 头像或图标的图片地址,支持Base64
|
||
confirmText: "同意", // 确认按钮的文字(可选)
|
||
// cancelText: "拒绝", // 取消按钮的文字(可选)
|
||
state: false, // 是否已经处理过 false 未处理,confirm:已确认,cancel:已拒绝(可选)
|
||
unique: from_uid, // 去重字段,比如同一个用户重复申请加好友,通知数据始终只显示一条,但是会通知多次(可选)
|
||
data: { // 自定义的其他参数(可选)
|
||
"_id": docId,
|
||
from_uid
|
||
},
|
||
path: false
|
||
},
|
||
title, // "收到im消息,离线时显示的标题",
|
||
content, //"离线时显示的内容"
|
||
path: false
|
||
}
|
||
|
||
return await this.sendPushMsg(pushParam, this.clientInfo.appId)
|
||
},
|
||
async chooseUserIntoGroup({
|
||
group_id,
|
||
user_ids
|
||
}) {
|
||
if (typeof user_ids != 'object') {
|
||
return {
|
||
errSubject: 'uni-im',
|
||
errCode: 1000,
|
||
errMsg: 'user_ids必须是数组'
|
||
}
|
||
} else {
|
||
if (user_ids.length > 499) {
|
||
return {
|
||
errSubject: 'uni-im',
|
||
errCode: 2000,
|
||
errMsg: "拉人进群一次不能超过500人,请分多次操作"
|
||
}
|
||
}
|
||
}
|
||
|
||
const dbJQL = uniCloud.databaseForJQL({
|
||
clientInfo: this.clientInfo
|
||
})
|
||
// 如果群id不存在,把当前操作的用户也加入群用户列表(群管理员&&群创建者)
|
||
if (!group_id) {
|
||
user_ids.push(this.current_uid)
|
||
}
|
||
|
||
let {
|
||
data: userList
|
||
} = await dbJQL.collection('uni-id-users')
|
||
.where(`"_id" in ${JSON.stringify(user_ids)}`)
|
||
.field('_id,nickname,avatar_file')
|
||
.limit(500)
|
||
.get()
|
||
|
||
//如果没有输入群id,就先创建群
|
||
if (!group_id) {
|
||
let groupName = userList.reduce((sum, {
|
||
nickname
|
||
}) => {
|
||
sum += nickname + ' '
|
||
return sum
|
||
}, '')
|
||
|
||
groupName = groupName.substring(0, 30)
|
||
|
||
// console.log({
|
||
// groupName
|
||
// });
|
||
|
||
// JQL触发器会自动把,当前用户设置为群主
|
||
let res = await dbJQL.collection('uni-im-group').add({
|
||
name: groupName
|
||
})
|
||
// console.log(122121, res);
|
||
|
||
// 把群主从用户列表中删除,因为他已经进群
|
||
userList.splice(userList.findIndex(item => item._id == this.current_uid), 1)
|
||
user_ids.pop()
|
||
// console.error(123123121, userList);
|
||
group_id = res.id
|
||
|
||
} else {
|
||
let res = await db.collection('uni-im-group').doc(group_id).get()
|
||
if (!res.data[0]) {
|
||
throw new Error('群id不存在')
|
||
}
|
||
}
|
||
|
||
// 如果一个用户都没选直接创建群,则 结束。否则将用户加入群&&创建会话&&发push通知
|
||
if (user_ids.length === 0) {
|
||
return {
|
||
errSubject: 'uni-im',
|
||
errCode: 0,
|
||
data: {
|
||
group_id
|
||
},
|
||
errMsg: ''
|
||
}
|
||
}
|
||
|
||
// 1.成为群成员
|
||
let groupMemberList = user_ids.map(user_id => {
|
||
data = {
|
||
group_id,
|
||
user_id,
|
||
role: [],
|
||
create_time: Date.now()
|
||
}
|
||
return data
|
||
})
|
||
let res = await db.collection('uni-im-group-member').add(groupMemberList)
|
||
// console.log('uni-im-group-member- res', res);
|
||
// 2.会话表加上
|
||
let conversationList = user_ids.map(user_id => {
|
||
data = {
|
||
group_id,
|
||
id: 'group_' + group_id,
|
||
user_id: user_id,
|
||
type: 2,
|
||
unread_count: 0,
|
||
update_time: Date.now()
|
||
}
|
||
return data
|
||
})
|
||
res = await db.collection('uni-im-conversation').add(conversationList)
|
||
|
||
// console.log('uni-im-group-member- res', res);
|
||
|
||
// 通知用户
|
||
let title = "新用户加群通知",
|
||
content = "新用户加群通知",
|
||
pushParam = {
|
||
payload: {
|
||
"type": "uni-im",
|
||
"data": {
|
||
type: "system",
|
||
action: "join-group-notice",
|
||
from_uid: "system",
|
||
//是否已读,默认为:false
|
||
is_read: false,
|
||
//创建时间
|
||
create_time: Date.now(),
|
||
conversation_id: "group_" + group_id,
|
||
group_id,
|
||
body: {
|
||
user_list: userList
|
||
}
|
||
},
|
||
title, // "收到im消息,在线时显示的标题",
|
||
content, // "在线时显示的副标题",
|
||
},
|
||
title, // "收到im消息,离线时显示的标题",
|
||
content //"离线时显示的内容"
|
||
}
|
||
const uniImCo = uniCloud.importObject("uni-im-co")
|
||
let pushRes = await uniImCo.sendMsgToGroup({
|
||
pushParam,
|
||
appId: this.clientInfo.appId
|
||
})
|
||
|
||
return {
|
||
errSubject: 'uni-im',
|
||
errCode: 0,
|
||
data: {
|
||
pushRes,
|
||
group_id
|
||
},
|
||
errMsg: ''
|
||
}
|
||
},
|
||
async revokeMsg(msgId) {
|
||
//1. 先查到这条消息
|
||
let {
|
||
data: [msgData]
|
||
} = await db.collection('uni-im-msg').doc(msgId).get()
|
||
if (!msgData) {
|
||
throw new Error('无效的消息id')
|
||
}
|
||
let {
|
||
conversation_id,
|
||
from_uid,
|
||
appid: appId,
|
||
group_id,
|
||
create_time
|
||
} = msgData
|
||
|
||
// 判断是不是群主
|
||
let isGroupAdmin = false
|
||
if (group_id) {
|
||
let res = await db.collection('uni-im-group-member')
|
||
.where({
|
||
group_id,
|
||
user_id: this.current_uid
|
||
})
|
||
.get()
|
||
isGroupAdmin = res.data[0] && 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分钟不能撤回'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3. 改变云数据库中此消息的撤回状态
|
||
let res = await db.collection('uni-im-msg')
|
||
.doc(msgId)
|
||
.update({
|
||
"is_revoke": true
|
||
})
|
||
// 添加一个服务端任
|
||
const taskCreateTime = Date.now()
|
||
await db.collection('uni-im-task').add({
|
||
type: "revoke_msg",
|
||
user_id: [msgData.to_uid],
|
||
group_id: [group_id],
|
||
payload: {
|
||
msg_id: msgId,
|
||
conversation_id,
|
||
user_id: this.current_uid,
|
||
create_time: msgData.create_time
|
||
},
|
||
create_time: taskCreateTime
|
||
})
|
||
// console.log('res',res,this.current_uid)
|
||
if (res.updated) {
|
||
// 4. 通知客户端,所有收到此消息的会话撤回此消息
|
||
let pushParam = {
|
||
"user_id": msgData.to_uid,
|
||
"payload": {
|
||
type: "uni-im-revoke-msg",
|
||
data: {
|
||
msg_id: msgId,
|
||
create_time: msgData.create_time,
|
||
conversation_id,
|
||
user_id: this.current_uid,
|
||
taskCreateTime
|
||
}
|
||
},
|
||
title: '撤回消息', // "收到im消息,离线时显示的标题"
|
||
content: '撤回消息', // "收到im消息,离线时显示的标题"
|
||
settings: {
|
||
// ttl: 3 * 24 * 3600 * 1000,
|
||
strategy: {
|
||
default: 3
|
||
}
|
||
}
|
||
}
|
||
if (group_id) {
|
||
delete pushParam.user_id
|
||
pushParam.payload.data.group_id = group_id
|
||
return await uniCloud.importObject('uni-im-co').sendMsgToGroup({
|
||
pushParam,
|
||
appId
|
||
})
|
||
} else {
|
||
res = await this.sendPushMsg(pushParam, appId)
|
||
return res
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function getConversationId(param) {
|
||
return 'single' + '_' + md5(param.sort().toString())
|
||
}
|
||
|
||
function hideEmailStr(email) {
|
||
if (email == undefined) {
|
||
return ''
|
||
}
|
||
const content = email.split("@")
|
||
return content[0].substr(0, content[0].length - 2) + '**' + content[1]
|
||
}
|
||
|
||
function hideMobileStr(mobile) {
|
||
return mobile.substr(0, 3) + '****' + mobile.substr(-1 * 4)
|
||
}
|
||
|
||
|
||
function check_param(params, rule) {
|
||
rule.required.forEach(item => {
|
||
if (!params[item]) {
|
||
throw new Error('错误,参数:' + item + "的值不能为空")
|
||
}
|
||
})
|
||
for (let key in rule.type) {
|
||
if (key in rule.type) {
|
||
let val = params[key],
|
||
type = rule.type[key]
|
||
if (val && !type.includes(getType(val))) {
|
||
throw new Error('错误,参数:' + key + '的数据类型必须为:' + rule.type[key].join('或'))
|
||
}
|
||
}
|
||
}
|
||
|
||
function getType(data) {
|
||
return Object.prototype.toString.call(data).replace(/[\[\]]/g, '').split(' ')[1]
|
||
}
|
||
} |