const dayjs = require("dayjs"); const cate = require("./sop-cate.js"); const medicalRecord = require("../medical-record"); const corpGroup = require("../corp-group"); const utils = require("../utils.js"); const appFunction = require("../app-function"); const common = require("../../common.js"); const e = require("connect-timeout"); let db = null; exports.main = async (content, DB) => { db = DB; switch (content.type) { case "getSopTaskList": return await exports.getSopTaskList(content); case "getSopTask": return await exports.getSopTask(content); case "updateSopTask": return await exports.updateSopTask(content); case "updateSopTaskField": return await exports.updateSopTaskField(content); case "removeSopTask": return await exports.removeSopTask(content); case "executeSopTask": return await exports.executeSopTask(content); case "getSopFilterRule": return await exports.getSopFilterRule(content); case "createSopTask": return await exports.createSopTask(content); case "updateSopTaskExecuteUser": return await exports.updateSopTaskExecuteUser(content); case "triggerSopTask": return await exports.triggerSopTask(content); case "removeCustomerSopTask": return await exports.removeCustomerSopTask(content); case "getCustomerSopTask": return await exports.getCustomerSopTask(content); case "createCustomerSopTask": return await exports.createCustomerSopTask(content); case "customerSopTaskTrigger": return await exports.customerSopTaskTrigger(content); case "customerSopTaskAutoGroup": return await customerSopTaskAutoGroup(content); case "addSopCate": case "updateSopCate": case "deleteSopCate": case "getSopCateList": case "sortSopCate": return await cate.main(content, db); default: return { success: false, message: "未找到方法", }; } }; exports.getSopTask = async (content) => { const { corpId, sopTaskId } = content; if (!corpId || !sopTaskId) return { success: false, message: "参数错误" }; const result = await db .collection("sop-tasks") .findOne({ _id: sopTaskId, corpId }); return { success: Boolean(result), message: `获取sop任务${Boolean(result) ? "成功" : "失败"}`, timeStamp: Date.now(), data: result, }; }; exports.getSopTaskList = async (content) => { const { corpId, sopName, page = 1, pageSize = 10, eventType, sopExecuteStatus, classIds, } = content; let query = { corpId }; if (classIds) query.classId = { $in: Array.isArray(classIds) ? classIds : [] }; if (sopName) query.sopName = { $regex: sopName, $options: "i" }; if (eventType) query.eventType = eventType; if (sopExecuteStatus === "executing") { query.executeStartTime = { $lte: Date.now() }; query.$or = [ { executeEndTime: { $gte: Date.now() } }, { executeEndTime: { $eq: "" } }, ]; query.sopEnableStatus = "enable"; } else if (sopExecuteStatus === "unexecuted") { query.executeStartTime = { $gte: Date.now() }; } else if (sopExecuteStatus === "closed") { query.executeEndTime = { $lte: Date.now() }; } const total = await db.collection("sop-tasks").countDocuments(query); const pages = Math.ceil(total / pageSize); const data = await db .collection("sop-tasks") .find(query) .sort({ createTime: -1 }) .skip((page - 1) * pageSize) .limit(pageSize) .toArray(); return { success: true, total, pages, size: pageSize, timeStamp: Date.now(), message: "获取sop任务列表成功", list: data, }; }; /** * 更新sop任务 * @param {*} content * @returns */ exports.updateSopTask = async (content) => { let { corpId, sopTaskId, ...rest } = content; const { executeTeams, executeJobs } = rest; // 根据 executeTeams 和 executeJobs 获取执行人员 if (executeTeams || executeJobs) { const teamIds = executeTeams.map((item) => item.teamId); rest.executeUserIds = await appFunction.getCorpMemberByTeamsAndJobs({ corpId, teamIds, jobs: executeJobs, }); } try { const res = await db .collection("sop-tasks") .updateOne({ _id: sopTaskId }, { $set: rest }); return { success: true, message: "更新sop任务成功", res, }; } catch (error) { return { success: false, message: "更新sop任务失败", }; } }; /** * 创建sop任务 * @param {string} corpId 机构id * @param {string} createUserId 创建人 * @param {string} sopName sop名称 * @param {string} sopContent sop内容 * @param {string} classId 分类id * @param {string} eventType 事件类型 * @param {string} executeMethod 执行方式 groupmsg-群发 todo-待办 tag-标签 * @param {string} sendContent 发送内容 * @param {string} fileList 发送文件 * @param {string} creatorUserId 创建人 * @param {string} executeStartTime 开始执行时间 * @param {string} executeEndTime 结束执行时间 * @param {string} executeTeams 执行团队 * @param {string} executeJobs 执行职位 * @param {array} filterConditions 筛选条件 * @param {number} triggerInterval 间隔时间 * @param {string} sopEnableStatus sop启用状态 enable-启用 disable-禁用 * @returns */ exports.createSopTask = async (content) => { const { corpId, createUserId, sopName, eventType, sopContent, classId, executeTeams, executeJobs, executeStartTime, executeEndTime = "", filterConditions, executeMethod, sendContent, fileList, creatorUserId, triggerInterval, sopEnableStatus = "enable", newTagIds, removeTagIds, executeType, enableSendContent, groups, archiveLink, } = content; // 根据 executeTeams 和 executeJobs 获取执行人员 const teamIds = executeTeams.map((item) => item.teamId); const executeUserIds = await appFunction.getCorpMemberByTeamsAndJobs({ corpId, teamIds, jobs: executeJobs, }); try { await db.collection("sop-tasks").insertOne({ _id: common.generateRandomString(24), corpId, createUserId, sopName, eventType, sopContent, classId, executeTeams, executeJobs, executeStartTime: executeStartTime && dayjs(executeStartTime).isValid() ? dayjs(executeStartTime).valueOf() : Date.now(), // 未设置开始执行时间 默认立即执行 executeEndTime: executeEndTime && dayjs(executeEndTime).isValid() ? dayjs(executeEndTime).valueOf() : "", // 未设置结束执行时间 默认永久执行 filterConditions, executeMethod, sendContent, fileList, executeUserIds, creatorUserId, triggerInterval, sopEnableStatus, newTagIds, removeTagIds, executeType, enableSendContent, groups, archiveLink, createTime: dayjs().valueOf(), }); return { success: true, message: "创建sop任务成功", }; } catch (error) { return { success: false, message: error.message, }; } }; /** * 删除sop任务 * @param {*} content * @returns */ exports.removeSopTask = async (content) => { const { sopTaskId } = content; await db.collection("sop-tasks").deleteOne({ _id: sopTaskId }); return { success: true, message: "删除sop任务成功", }; }; /** * 获取sop筛选规则 */ exports.getSopFilterRule = async (content) => { const { corpId } = content; const data = await db .collection("sop-filter-conditions") .find({ corpId: { $in: [corpId, "default"] } }) .toArray(); if (data.length === 0) { return { success: false, message: "未找到筛选规则", }; } else if (data.length === 1) { return { success: true, data: data[0], message: "获取筛选规则成功", }; } else { const defaultRule = data.find((item) => item.corpId === corpId); return { success: true, data: defaultRule || data[0], message: "获取筛选规则成功", }; } }; /** * 更新sop任务的执行人员 */ exports.updateSopTaskExecuteUser = async (content) => { const { corpId, teamId } = content; // 获取本机构的所有sop任务 const taskList = await db .collection("sop-tasks") .find({ corpId, executeTeams: { $elemMatch: { teamId } }, }) .project({ _id: 1, executeTeams: 1, executeJobs: 1, executeUserIds: 1, }) .toArray(); for (let task of taskList) { const { executeTeams, executeJobs, _id } = task; const teamIds = executeTeams.map((item) => item.teamId); const executeUserIds = await appFunction.getCorpMemberByTeamsAndJobs({ corpId, teamIds, jobs: executeJobs, }); await db .collection("sop-tasks") .updateOne({ _id }, { $set: { executeUserIds } }); } return { success: true, message: "更新执行人员成功", }; }; /** * 触发sop任务 */ exports.triggerSopTask = async (content) => { let { corpId, customerId, medicalRecord = {}, archive = {}, healthIndicators, } = content; console.log("触发sop任务", content); if (!customerId) return; const archiveSopIndicators = [ "age", "createTime", "birthday", "tagIds", "intentedProject", "customerSource", "diagnosis", "physicalExaminationTemplate", ]; const medicalIndicators = [ "outpatientDiagnosis", "inhospitalDiagnosis", "physicalExaminationTemplate", "visitTime", "inhosDate", "outhosDate", "operationDate", ]; const medicalKeys = Object.keys(medicalRecord); const archiveKeys = Object.keys(archive); if ( medicalKeys.length > 0 && !medicalIndicators.some((item) => medicalIndicators.includes(item)) ) return; if ( archiveKeys.length > 0 && !archiveSopIndicators.some((item) => archiveKeys.includes(item)) ) return; const archiveData = await db .collection("member") .find({ corpId, _id: customerId }) .toArray(); archive = archiveData[0]; let executeSopTaskRes = await this.executeSopTask({ corpId, customerId, medicalRecord, archive, healthIndicators, }); return { success: true, message: "触发sop任务成功", executeSopTaskRes, }; }; /** * 获取客户健康指标 */ async function customerHealthIndicators({ corpId, memberId, healthFilterConditions, healthIndicators = {}, }) { if ( Object.keys(healthIndicators).length === 0 && Object.keys(healthFilterConditions).length > 0 && !healthFilterConditions["healthIndexUpdate"] && healthFilterConditions["healthIndexUpdate"] !== 0 ) { const { success, list } = await medicalRecord.main( { corpId, memberId, type: "getHealthIndicators", }, db ); if (success && list) { for (let item of list) { healthIndicators[item.type] = item.value; } } } return healthIndicators; } /** * 执行sop任务 */ exports.executeSopTask = async (content) => { let { corpId, customerId, medicalRecord, archive = {}, healthIndicators, } = content; const { externalUserId: customerUserId, personResponsibles, name: customerName, } = archive; console.log("archive", archive); if (!personResponsibles || personResponsibles.length === 0) return { success: false, message: "未找到责任人" }; const sopTaskQuery = { corpId, executeStartTime: { $lte: dayjs().valueOf() }, $or: [ { executeEndTime: { $gte: dayjs().valueOf() } }, { executeEndTime: "" }, { executeEndTime: null }, { executeEndTime: { $exists: false } }, ], sopEnableStatus: { $ne: "disable" }, }; const sopTask = await db .collection("sop-tasks") .find(sopTaskQuery) .limit(1000) .toArray(); if (!Array.isArray(sopTask) || sopTask.length === 0) return; for (let i of sopTask) { // 如果执行人员为空,则跳过 let newHealthIndicators = {}; if (i.executeUserIds.length === 0) continue; const { executeUserIds, executeTeams, filterConditions = [], eventType, _id: sopTaskId, sendContent, fileList, executeMethod, triggerInterval, sopContent, newTagIds, removeTagIds, sopName, groups, } = i; if (executeMethod === "groupmsg" && !customerUserId) continue; const executeTeamIds = executeTeams.map((item) => item.teamId); const item = personResponsibles.find( (item) => executeUserIds.includes(item.corpUserId) && executeTeamIds.includes(item.teamId) ); console.log( "item", item, personResponsibles, executeUserIds, executeTeamIds ); // 如果执行人员不是当前客户的责任人,则跳过 if (!item) continue; const executeUserId = item.corpUserId; const executeTeamId = item.teamId; const executeTeamName = executeTeams.find( (item) => item.teamId === executeTeamId ).name; // 匹配筛选条件 if (filterConditions.length === 0) continue; console.log("filterConditions", filterConditions); const conditions = parseFilterConditions(filterConditions); const { archiveFilterConditions = {}, medicalFilterConditions = {}, healthFilterConditions = {}, } = conditions; console.log("conditions", conditions); newHealthIndicators = await customerHealthIndicators({ corpId, memberId: customerId, healthFilterConditions, healthIndicators, }); let { isMatch, executeTime, key } = matchFilterConditions({ archiveFilterConditions, medicalFilterConditions, healthFilterConditions, medicalRecord, archive, healthIndicators: newHealthIndicators, }); console.log("isMatch", isMatch, executeTime, key); if (!isMatch) continue; // 创建sop任务 await exports.createCustomerSopTask({ corpId, eventType, executeMethod, sendContent, fileList, executeUserId, executeTeamId, executeTeamName, customerId, customerName, customerUserId, sopTaskId, executeTime, newTagIds, removeTagIds, triggerInterval, sopContent, sopName, groups, }); } }; function matchFilterConditions({ archiveFilterConditions = {}, medicalFilterConditions = {}, healthFilterConditions = {}, medicalRecord = {}, archive = {}, healthIndicators, }) { let isMatch = true; let executeTime = ""; // 档案模版字段时间类型的字段 const medicalTimeType = [ "visitTime", "inhosDate", "outhosDate", "operationDate", "inspectTime", ]; if (Object.keys(archiveFilterConditions).length !== 0) { // 判断archiveFilterConditions是否为空 for (let key in archiveFilterConditions) { const filterItem = archiveFilterConditions[key]; if (archiveFilterConditions.hasOwnProperty(key)) { // 判断年龄是否在范围内 if (key === "age") { const { min, max } = filterItem; const age = parseInt(archive.age, 10); if (isNaN(age) || age < min || age > max) { return { isMatch: false, key, }; } } // 判断是否是创建时间 // 没有更新时间 则证明这条数据是建档数据 if (key === "createTimeAfter" && !archive.updateTime) { console.log("createTimeAfter", archive, filterItem); if ( !archive.createTime || archive.createTime < dayjs().startOf("day").valueOf() ) { return { isMatch: false, key, }; } else { executeTime = dayjs() .add(archiveFilterConditions[key], "day") .valueOf(); } } // 判断生日获取执行时间 if (key === "birthday" && typeof filterItem === "number") { if (!archive.birthday) { return { isMatch: false, key, }; } executeTime = dayjs().subtract(filterItem, "day").valueOf(); // executeTime 与当天年、月比较 } // 判断性别是否匹配 const sexObj = { male: "男", female: "女", }; if ( key === "sex" && (!filterItem || sexObj[filterItem] !== archive.sex) ) { return { isMatch: false, key, }; } // 判断是否包含标签 const tagIds = archive.tagIds; const includeTag = archiveFilterConditions.includeTag; const includeTagType = archiveFilterConditions.includeTagType; const excludeTagType = archiveFilterConditions.excludeTagType; const excludeTag = archiveFilterConditions.excludeTag; if ( key === "includeTag" && (!tagIds || (tagIds && includeTagType === "or" && !tagIds.some((item) => includeTag.includes(item))) || (tagIds && includeTagType === "and" && !includeTag.every((tag) => tagIds.includes(tag)))) ) { return { isMatch: false, key }; } if ( key === "excludeTag" && Array.isArray(excludeTag) && ((tagIds && excludeTagType === "or" && tagIds.some((item) => excludeTag.includes(item))) || (tagIds && excludeTagType === "and" && excludeTag.every((tag) => tagIds.includes(tag)))) ) { return { isMatch: false, key, }; } // 判断是否是创建时间 if (key === "createTimeAfter") { if (!archive.createTime) { return { isMatch: false, key, }; } executeTime = dayjs(archive.createTime) .add(filterItem, "day") .valueOf(); } // 判断是否是意向项目 if ( key === "intentedProject" && (!archive.intentedProject || !archiveFilterConditions.intentedProject.includes( archive.intentedProject )) ) { return { isMatch: false, key, }; } // 判断是否是客户来源 if ( key === "customerSource" && (!archive.customerSource || !archive.customerSource.some((item) => archiveFilterConditions.customerSource.includes(item) )) ) { return { isMatch: false, key, }; } } } } // 判断medicalFilterConditions是否为空 if (Object.keys(medicalFilterConditions).length !== 0) { // 获取最新的就诊记录包含所有字段 for (let key in medicalFilterConditions) { if (medicalFilterConditions.hasOwnProperty(key)) { console.log("key", key); const recordItem = medicalRecord[key]; // 就诊记录的字段 const medicalFilterItem = medicalFilterConditions[key]; // 筛选条件的字段 if (medicalTimeType.includes(key)) { executeTime = dayjs(recordItem) .add(medicalFilterItem, "day") .valueOf(); } if ( !recordItem || (typeof medicalFilterItem === "string" && medicalFilterItem !== recordItem) || (Array.isArray(medicalFilterItem) && Array.isArray(recordItem) && !recordItem.some((item) => medicalFilterItem.includes(item))) ) { return { isMatch: false, key, }; } } } } if (Object.keys(healthFilterConditions).length !== 0) { // 获取最新的健康指标包含所有字段 // 如果健康指标没有更新时间,则立马更新 // if (!healthFilterConditions['healthIndexUpdate']) executeTime = dayjs().valueOf(); for (let item in healthFilterConditions) { if (healthFilterConditions.hasOwnProperty(item)) { // 获取健康指标的字段 const value = healthFilterConditions[item]; if (item === "healthIndexUpdate") { executeTime = dayjs().add(value, "day").valueOf(); continue; } const healthFilterKeys = Object.keys(value); const healthItemKeys = Object.keys(healthIndicators); const incloudKeys = healthFilterKeys.filter((item) => healthItemKeys.includes(item) ); if (incloudKeys.length === 0) { return { isMatch: false, key: "incloudKeys" }; } let isMatch = false; // 获取健康指标的值 for (let key in value) { const healthFilterItem = value[key]; const healthItem = healthIndicators[key]; if ( healthItem && ((isObject(healthFilterItem) && healthItem >= healthFilterItem.min && healthItem <= healthFilterItem.max) || (Array.isArray(healthFilterItem) && Array.isArray(healthItem) && healthItem.some((item) => healthFilterItem.includes(item)))) ) { isMatch = true; } } if (!isMatch) return { isMatch: false }; } } } // 如果执行时间存在且小于当天 则不执行 if (executeTime && dayjs(executeTime).isBefore(dayjs().startOf("day"))) { console.log("执行时间小于当天, 不执行"); isMatch = false; } return { isMatch, executeTime, }; } function parseFilterConditions(filterConditionsArray) { const filterConditions = { archiveFilterConditions: {}, medicalFilterConditions: {}, healthFilterConditions: {}, }; filterConditionsArray.forEach((condition, index) => { const { theme, value, condition: cond } = condition; switch (theme) { case "includeTag": filterConditions.archiveFilterConditions["includeTag"] = value; filterConditions.archiveFilterConditions["includeTagType"] = cond; break; case "excludeTag": filterConditions.archiveFilterConditions["excludeTag"] = value; filterConditions.archiveFilterConditions["excludeTagType"] = cond; break; case "sex": filterConditions.archiveFilterConditions["sex"] = cond; break; case "birthdayTheme": case "intentedProject": case "customerSource": case "age": case "notArriveTheme": filterConditions.archiveFilterConditions[cond] = value; break; case "outpatientTheme": case "inhospitalTheme": case "operationTheme": case "physicalExaminationTheme": filterConditions.medicalFilterConditions[cond] = value; break; case "healthIndexTheme": // 健康指标 if (cond === "healthIndexAbnormal") { let conditions = {}; if (Array.isArray(value)) { value.forEach((item) => { const { condition, key, type, value: v } = item; conditions[condition] = v; }); } filterConditions.healthFilterConditions[index] = conditions; //健康指标更新时间 } else { filterConditions.healthFilterConditions[cond] = value; } break; default: break; } }); return filterConditions; } function isObject(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } /** * 执行sop任务 * @param {string} corpId 机构id * @param {string} eventType 事件类型 * @param {string} executeMethod 执行方法 * @param {string} sendContent 发送内容 * @param {string} fileList 发送文件 * @param {string} executeUserId 执行人 * @param {string} executeTeamId 执行团队id * @param {string} executeTeamName 执行团队名称 * @param {string} creatorUserId 创建人 * @param {string} customerId 客户id * @param {string} customerName 客户姓名 * @param {string} customerUserId 客户用户id * @param {string} sopTaskId sop任务id * @param {string} executeTime 执行时间 * @param {number} triggerInterval 触发限制 * @returns */ exports.createCustomerSopTask = async (content) => { let { corpId, eventType, executeMethod, sendContent, fileList, executeUserId, executeTeamId, executeTeamName, creatorUserId, customerId, customerName, customerUserId, sopTaskId, executeTime, triggerInterval, newTagIds, removeTagIds, sopContent, sopName, groups, } = content; if (executeTime && !triggerInterval) triggerInterval = -1; let { data } = await this.getCustomerSopTask({ corpId, customerId, sopTaskId, getType: "All", }); console.log("data", data); const today = dayjs().endOf("day").valueOf(); let taskData = data[0]; if (taskData && taskData.triggerInterval && taskData.triggerInterval != -1) { if (!taskData.triggerInterval) return { success: false, message: "任务已执行" }; let interval = dayjs(taskData.executeTime) .add(taskData.triggerInterval, "day") .valueOf(); if (today < interval) return { success: false, message: "任务还未过间隔时间,不生成" }; } let params = { _id: common.generateRandomString(24), corpId, eventType, executeMethod, sendContent, fileList, executeUserId, executeTeamId, executeTeamName, creatorUserId, customerId, customerName, customerUserId, sopTaskId, executeTime, triggerInterval, sopContent, executeStatus: "init", sopName, createTime: dayjs().valueOf(), }; // 执行任务 // 根据执行方式执行任务 // 标签 if ( executeMethod === "tag" && (!executeTime || (executeTime && dayjs(executeTime).isSame(dayjs(), "day"))) ) { await customerSopTaskAutoTag({ customerId, newTagIds, removeTagIds }); params.executeStatus = "executed"; } if ( executeMethod === "enterGroup" && (!executeTime || (executeTime && dayjs(executeTime).isSame(dayjs(), "day"))) ) { const { success, targetGroupId, message } = await customerSopTaskAutoGroup({ customerId, groups, executeUserId, corpId, executeTime, executeTeamId, executeTeamName, }); params.executeStatus = "executed"; params.groupId = targetGroupId; if (!success) { params.failReason = message; } } if ( executeMethod === "groupmsg" && (!executeTime || (executeTime && dayjs(executeTime).isSame(dayjs(), "day"))) ) { await createGroupmsgTask({ executeUserId, customers: [customerId], sendContent, fileList, sopTaskId, executeTeamIds: [executeTeamId], externalUserIds: [customerUserId], customerSopTaskIds: [sopTaskId], corpId, sopName, }); // 生成群发消息, 未执行 params.executeStatus = "unexecuted"; } if ( executeMethod === "todo" && (!executeTime || (executeTime && dayjs(executeTime).isSame(dayjs(), "day"))) ) { await customerSopTaskCreateTodo([params]); // 生成代码, 未执行 params.executeStatus = "unexecuted"; } if ( !executeTime || (executeTime && !dayjs(executeTime).isSame(dayjs(), "day")) ) { params.executeTime = dayjs().add(1, "day").valueOf(); } console.log("传入时间params", params); await db.collection("sop-customer-task").insertOne(params); return { success: true, message: "创建sop任务成功", }; }; exports.removeCustomerSopTask = async (content) => { const { corpId, customerId } = content; await db .collection("sop-customer-task") .deleteMany({ corpId, customerId, executeStatus: "unexecuted" }); return { success: true, message: "删除sop任务成功", }; }; exports.getCustomerSopTask = async (content) => { const { corpId, customerId, sopTaskId, executeUserId, executeStatus, dates, page = 1, pageSize = 10, getType = "", } = content; let query = { corpId, sopTaskId, }; if (executeUserId) query.executeUserId = executeUserId; if (executeStatus) query.executeStatus = executeStatus; if (customerId) query.customerId = customerId; if (dates && dates.length > 0) { let dateStart = dayjs(dates[0]).startOf("day").valueOf(); let dateEnd = dayjs(dates[1]).endOf("day").valueOf(); query.createTime = { $gte: dateStart, $lte: dateEnd }; } if (getType !== "All") query.executeStatus = { $ne: "init" }; const total = await db.collection("sop-customer-task").countDocuments(query); const pages = Math.ceil(total / pageSize); const data = await db .collection("sop-customer-task") .find(query) .sort({ createTime: -1 }) .skip((page - 1) * pageSize) .limit(pageSize) .toArray(); return { success: true, message: "获取sop任务成功", total, pages, data, }; }; exports.updateSopTaskField = async (ctx) => { if (!ctx.corpId || !ctx.id) return { success: false, message: "更新失败" }; try { const task = await db .collection("sop-tasks") .findOne({ corpId: ctx.corpId, _id: ctx.id }); if (!task) return { success: false, message: "未找到sop任务" }; let payload = null; if (ctx.action === "stop") payload = { sopEnableStatus: "disable" }; else if (ctx.action === "startRightNow") payload = { sopEnableStatus: "enable", executeStartTime: Date.now() }; else if (ctx.action === "start") payload = { sopEnableStatus: "enable" }; if (payload) { await db .collection("sop-tasks") .updateOne({ _id: ctx.id }, { $set: payload }); return { success: true, message: "更新成功" }; } return { success: false, message: "更新失败" }; } catch (e) { return { success: false, message: e.message || "更新失败" }; } }; /** * 档案sop任务触发 * @param {*} content */ exports.customerSopTaskTrigger = async () => { const fetchData = async (page, pageSize, db) => { return await db .collection("sop-customer-task") .find({ executeStatus: "init", executeTime: { $gte: dayjs().startOf("day").valueOf(), $lte: dayjs().endOf("day").valueOf(), }, }) .skip((page - 1) * pageSize) .limit(pageSize) .toArray(); }; const list = await utils.getAllData(fetchData, db); let toDoList = list.filter((i) => i.executeMethod === "todo"); let groupMsgList = list.filter((i) => i.executeMethod === "groupmsg"); let tagList = list.filter((i) => i.executeMethod === "tag"); let enterGroupList = list.filter((i) => i.executeMethod === "enterGroup"); if (toDoList.length > 0) await customerSopTaskCreateTodo(toDoList); if (groupMsgList.length > 0) await customerSopTaskCreateGroupMsg(groupMsgList); if (tagList.length > 0) { for (let item of tagList) { await customerSopTaskAutoTag(item); } } if (enterGroupList.length > 0) { for (let item of enterGroupList) { await customerSopTaskAutoGroup(item); } } const ids = list.map((i) => i._id); await db .collection("sop-customer-task") .updateMany( { _id: { $in: ids } }, { $set: { executeStatus: "unexecuted" } } ); return { success: true, message: "触发sop任务成功" }; }; // 获取待办类型的sop任务 async function customerSopTaskCreateTodo(list) { for (let item of list) { let { triggerInterval, executeMethod, _id, executeUserId, executeStatus, executeTeamId, executeTime, sopContent, executeTeamName, sendContent, sopName, fileList, ...rest } = item; rest.createTime = dayjs().valueOf(); rest.creatorUserId = "system"; rest.isFeedback = false; rest.eventStatus = "untreated"; rest.customerSopTaskId = _id; rest.executorUserId = executeUserId; rest.executeTeamId = executeTeamId; rest.plannedExecutionTime = executeTime; rest.planName = sopName; rest.taskContent = sopName + sopContent; rest.executeTeamName = executeTeamName; rest.sendContent = sendContent; rest.executeMethod = "todo"; rest.pannedEventSendFile = getTodoSendFile(fileList); rest.expireTime = dayjs().add(7, "day").endOf("day").valueOf(); await db.collection("to-do-events").insertOne({ _id: common.generateRandomString(24), ...rest, }); } } // 生成群发任务 async function customerSopTaskCreateGroupMsg(list) { if (list.length === 0) return; let newObj = groupBySopTaskIdAndExecuteUserId(list); for (const key in newObj) { if (newObj.hasOwnProperty(key)) { const array = newObj[key]; const customers = array.map((i) => i.customerId); const customerSopTaskIds = array.map((i) => i._id); const executeTeamIds = [...new Set(array.map((i) => i.executeTeamId))]; const externalUserIds = [...new Set(array.map((i) => i.customerUserId))]; const { sendContent, executeUserId, fileList, sopTaskId, corpId, sopName, } = array[0]; await createGroupmsgTask({ executeUserId, customers, sendContent, fileList, sopTaskId, executeTeamIds, customerSopTaskIds, corpId, externalUserIds, sopName, }); } } } async function createGroupmsgTask({ executeUserId, customers, sendContent, fileList, sopTaskId, executeTeamIds, customerSopTaskIds, corpId, externalUserIds, sopName, }) { const params = { sendType: "MESSAGE", executor: executeUserId, customers, content: sendContent, attachments: common.getAttachments(fileList, sopTaskId, executeUserId), startTaskDate: dayjs().valueOf(), endTaskDate: dayjs().add(3, "day").endOf("day").valueOf(), sendSource: "MINE", teamIds: executeTeamIds, creator: "system", createSounrce: "MINE", executeStatus: "doing", sopTaskId, customerSopTaskIds, corpId, externalUserIds, taskName: sopName, }; await appFunction.createGroupmsgTask({ params, corpId }); } function groupBySopTaskIdAndExecuteUserId(list) { return list.reduce((acc, item) => { const key = `${item.sopTaskId}-${item.executeUserId}`; if (!acc[key]) { acc[key] = []; } acc[key].push(item); return acc; }, {}); } function getTodoSendFile(fileList) { let file = Array.isArray(fileList) && fileList[0] ? fileList[0].file : {}; if (!file) return {}; let { name, type, url, surveryId } = file; let query = { name, type, url }; if (surveryId) query.surveryId = surveryId; return query; } // 自动打标签 async function customerSopTaskAutoTag({ customerId, newTagIds, removeTagIds }) { if (!customerId) return; let customerData = await db.collection("member").findOne({ _id: customerId }); if (!customerData) return; let { tagIds = [] } = customerData; if (Array.isArray(newTagIds) && newTagIds.length > 0) tagIds = [...new Set([...tagIds, ...newTagIds])]; if (Array.isArray(removeTagIds) && removeTagIds.length > 0) tagIds = tagIds.filter((i) => !removeTagIds.includes(i)); await db .collection("member") .updateOne({ _id: customerId }, { $set: { tagIds } }); } // 自动进入分组 async function customerSopTaskAutoGroup({ groups, corpId, customerId, executeTime, executeUserId, executeTeamId, executeTeamName, }) { if (!Array.isArray(groups) || groups.length === 0) return; let selectedGroupId = ""; if (groups.length === 1) { selectedGroupId = groups[0].groupId; } else { const randomValue = Math.floor(Math.random() * 101); for (const { groupId, percent } of groups) { if (randomValue < percent) { selectedGroupId = groupId; break; } } if (!selectedGroupId) { selectedGroupId = groups[groups.length - 1].groupId; } } let res = await corpGroup.main( { type: "customerEnterGroup", corpId, customerId, targetGroupId: selectedGroupId, executeTeamId, executeTeamName, executeUserId, planExecutionTime: executeTime, }, db ); return { ...res, targetGroupId: selectedGroupId, }; }