const request = require("../request"); const utils = require("../utils"); const information = require("../externalcontact-info"); const token = require("../token"); const dayjs = require("dayjs"); const api = require("../../api"); exports.main = async (context) => { switch (context.type) { case "editExternalContact": return await exports.editExternalContact(context); case "transferExternalcontact": return await exports.transferExternalcontact(context); case "removeExternalContact": return await exports.removeExternalContact(context); case "asyncExternalContactRemark": return await exports.asyncExternalContactRemark(context); case "batchGetBehaviorData": return await exports.batchGetBehaviorData(context); case "getCorpAllStaffUseBehaviorDataToday": return await exports.getCorpAllStaffUseBehaviorDataToday(context); case "getAllCorpYesterdayBehaviorData": return await exports.getAllCorpYesterdayBehaviorData(context); default: { return { success: false, message: "未找到对应的操作类型", }; } } }; exports.removeExternalContact = async (context) => { return true; }; // handoverUserId 是 string 离职成员的userid // externalUserIds 是 array 客户的external_userid列表,每次最多支持100个客户 // takeoverUserId 是 string 接替成员的userid exports.transferExternalcontact = async (context) => { const { corpId, externalUserIds = [], handoverUserId, takeoverUserId, permanentCode, } = context; const accessToken = await token.getToken({ corpId, permanentCode, }); let url = `https://qyapi.weixin.qq.com/cgi-bin/externalcontact/transfer_customer?access_token=${accessToken}`; let params = { external_userid: externalUserIds, handover_userid: handoverUserId, takeover_userid: takeoverUserId, }; let { errcode, errmsg } = await request.main(url, params, "POST"); if (errcode !== 0) { return { context, success: false, message: errmsg, errcode, }; } return { success: true, message: "转移客户成功", }; }; /** * 编辑外部联系人 * @param {string} corpId 企业ID * @param {string} externalUserId 外部联系人的userid * @param {string} userId 企业成员的userid * @param {string} env 云开发环境ID */ exports.editExternalContact = async ({ corpId, externalUserId, userId }) => { let res = await information.getNameByexternalUserId({ corpId, externalUserId, type: "getNameByexternalUserId", }); if (!res || !res.data) return; let { follow_user, external_contact } = res.data; let tags = []; const user = Array.isArray(follow_user) && follow_user.find((item) => item.userid === userId); let remark = external_contact.name; if (user) remark = user.remark; follow_user.forEach((item) => { if (item.userid === userId && item.tags.length > 0) { item.tags.forEach((tag) => { tags.push(tag.tag_id); }); } }); console.log("编辑外部联系人tags", tags); console.log("编辑外部联系人", res.data); console.log("remark", remark); return await api.getMemberApi({ type: "asyncCustomerTag", corpId, name: remark, externalUserId, userId, tags, }); }; /** * 同步备注名 */ /** * 同步备注名 */ exports.asyncExternalContactRemark = async ({ corpId, externalUserId, list = [], permanentCode = "", }) => { const accessToken = await token.getToken({ corpId, permanentCode, }); if (!externalUserId) return { success: false, message: "externalUserId不能为空", }; console.log("accessToken", accessToken); let res = await information.getNameByexternalUserId({ corpId, externalUserId, type: "getNameByexternalUserId", }); console.log("外部联系人信息", res); let { follow_user } = res.data; let userIds = follow_user.map((item) => item.userid); if (userIds.length === 0) { return { success: false, message: "该客户不是本机构的外部联系人", }; } // 把 list 排序 本人在前,其他人在后 list.sort((a, b) => { if (a.relationship === "本人") { return -1; } else { return 1; } }); // 逗号隔开 let mySelf = list .filter((item) => item.relationship === "本人") .map((item) => item.name) .join("、"); let other = list .filter((item) => item.relationship !== "本人") .map((item) => item.name) .join("、"); let remark = ""; if (mySelf) remark = `${mySelf}(本人)`; if (other) remark = remark ? `${remark},${other}(家属)` : `${other}(家属)`; const handler = async (userId) => { let url = `https://qyapi.weixin.qq.com/cgi-bin/externalcontact/remark?access_token=${accessToken}`; let params = { userid: userId, external_userid: externalUserId, remark, }; await request.main(url, params, "POST"); }; await utils.processInBatches(userIds, handler, 10); return { success: true, message: "同步备注名成功", }; }; exports.batchGetBehaviorData = async ({ dates, permanentCode, accessToken, corpId, }) => { if (!accessToken) accessToken = await token.getToken({ corpId, permanentCode }); let startTime = "", endTime = ""; if (dates && Array.isArray(dates) && dates.length === 2) { startTime = dayjs(dates[0]).startOf("day").unix(); endTime = dayjs(dates[1]).endOf("day").unix(); } console.log("startTime", startTime); console.log("endTime", endTime); const { data } = await api.getCorpApi({ corpId, permanentCode, accessToken, type: "getOpenedAccount", }); const userIds = data && Array.isArray(data) ? data.map((i) => i.userid) : []; const query = { corpId, permanentCode, startTime, endTime, }; const taskFn = (userId) => { return addBehaviorStatistics({ ...query, userId }); }; await utils.executeWithConcurrency(userIds, taskFn); return { success: true, message: "批量获取成功", }; }; async function addBehaviorStatistics({ userId, corpId, permanentCode, startTime, endTime, }) { const { success, data } = await exports.getUseBehaviorData({ startTime, endTime, userIds: [userId], corpId, permanentCode, }); if (success) { const list = data .filter( (i) => i.newContactCount !== 0 || i.newApplyCnt !== 0 || i.chatCnt !== 0 || i.negativeFeedbackCnt !== 0 || i.avgReplyTime !== 0 || i.replyPercentage !== 0 || i.messageCnt !== 0 ) .map((i) => { return { ...i, corpId, userId, }; }); await addBehaviorStatisticsInBatches(list); return { success: true, message: "获取成功", userId, }; } else { return data; } } async function addBehaviorStatisticsInBatches(data, maxConcurrency = 10) { const results = []; let index = 0; while (index < data.length) { const batch = data.slice(index, index + maxConcurrency); // 获取当前批次 const promises = batch.map((doc) => api.getCorpApi({ type: "addBehaviorStatistics", data: doc, }) ); // 等待当前批次完成 const batchResults = await Promise.all(promises); results.push(...batchResults); // 更新索引,进入下一个批次 index += maxConcurrency; } return results; } /** * * @param * new_apply_cnt 发起申请数,成员通过「搜索手机号」、「扫一扫」、「从微信好友中添加」、「从群聊中添加」、「添加共享、分配给我的客户」、「添加单向、双向删除好友关系的好友」、「从新的联系人推荐中添加」等渠道主动向客户发起的好友申请数量。 new_contact_cnt 新增客户数,成员新添加的客户数量。 chat_cnt 聊天总数, 成员有主动发送过消息的单聊总数。 message_cnt 发送消息数,成员在单聊中发送的消息总数。 reply_percentage 已回复聊天占比,浮点型,客户主动发起聊天后,成员在一个自然日内有回复过消息的聊天数/客户主动发起的聊天数比例,不包括群聊,仅在确有聊天时返回。 avg_reply_time 平均首次回复时长,单位为分钟,即客户主动发起聊天后,成员在一个自然日内首次回复的时长间隔为首次回复时长,所有聊天的首次回复总时长/已回复的聊天总数即为平均首次回复时长,不包括群聊,仅在确有聊天时返回。 negative_feedback_cnt 删除/拉黑成员的客户数,即将成员删除或加入黑名单的客户数。 * @returns */ exports.getUseBehaviorData = async ({ corpId, userIds, startTime, endTime, accessToken, permanentCode, }) => { try { // 如果没有传递 accessToken,则获取一个新的 token if (!accessToken) accessToken = await token.getToken({ corpId, permanentCode }); // 构建请求 URL 和请求参数 const url = `https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_user_behavior_data?access_token=${accessToken}`; const params = { userid: userIds, start_time: startTime, end_time: endTime, }; // 发送 POST 请求 const response = await request.main(url, params, "POST"); // 检查请求结果 if (response.errcode === 0) { // 假设 errcode 为 0 表示成功 let list = response.behavior_data; if (list && Array.isArray(list)) { list = list.map((i) => { return { statTime: i.stat_time, // 时间 newContactCount: i.new_contact_cnt || 0, newApplyCnt: i.new_apply_cnt || 0, chatCnt: i.chat_cnt || 0, negativeFeedbackCnt: i.negative_feedback_cnt || 0, avgReplyTime: i.avg_reply_time || 0, replyPercentage: i.reply_percentage || 0, messageCnt: i.message_cnt || 0, }; }); } return { success: true, data: list, // 假设接口返回的数据字段是 behavior_data message: "请求成功", }; } else { return { success: false, errcode: response.errcode, message: response.errmsg || "请求失败", }; } } catch (err) { console.error("请求失败:", err); // 打印错误日志以便调试 return { success: false, message: "请求失败,系统错误", }; } }; exports.getAllCorpYesterdayBehaviorData = async () => { // 获取所以机构 try { const corpRes = await api.getCorpApi({ type: "getCorpList", }); const corpIds = corpRes.data && Array.isArray(corpRes.data) ? corpRes.data.map((i) => i.corpId) : []; const yesterdayTime = dayjs().subtract(1, "day").format("YYYY-MM-DD"); const dates = [yesterdayTime, yesterdayTime]; const taskFn = (corpId) => { return this.batchGetBehaviorData({ dates, corpId }); }; await utils.executeWithConcurrency(corpIds, taskFn); return { success: true, message: "批量获取成功", }; } catch (err) { return { success: false, message: "批量获取失败", }; } }; exports.getCorpAllStaffUseBehaviorDataToday = async ({ corpId, userIds, dates, }) => { let accessToken; try { // 获取 accessToken accessToken = await token.getToken({ corpId }); } catch (error) { console.error("Error getting access token:", error); return { success: false, message: "获取访问令牌失败" }; // 获取令牌失败,返回错误信息 } const maxConcurrency = 10; // 设置最大并发数 const results = []; // 存储所有的结果 let index = 0; // 设置查询的开始和结束时间(单位:秒) const startTime = dayjs().startOf("day").unix(); const endTime = dayjs().endOf("day").unix(); try { while (index < userIds.length) { const batch = userIds.slice(index, index + maxConcurrency); // 当前批次的用户ID const promises = batch.map(async (userId) => { try { const { data } = await exports.getUseBehaviorData({ corpId, userIds: [userId], startTime, endTime, accessToken, }); const item = data && Array.isArray(data) ? data[0] : {}; return { userId, ...item }; } catch (error) { console.error(`Error fetching data for userId: ${userId}`, error); return []; // 失败时返回空数组,继续处理其他用户 } }); // 等待当前批次完成 const batchResults = await Promise.all(promises); results.push(...batchResults); // 合并结果 // 更新索引,进入下一个批次 index += maxConcurrency; } // 返回最终结果 return { success: true, data: results, message: "获取成功", }; } catch (error) { console.error("Error in processing the batch requests:", error); return { success: false, message: "批量请求处理失败" }; // 捕获批量处理过程中的异常 } };