2026-07-27 11:28:33 +08:00

1331 lines
36 KiB
JavaScript

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,
};
}