// 云对象教程: 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]
	}
}