470 lines
16 KiB
JavaScript
470 lines
16 KiB
JavaScript
|
|
const dayjs = require("dayjs");
|
|||
|
|
const logger = require("../../utils/logger");
|
|||
|
|
const api = require("../../api");
|
|||
|
|
|
|||
|
|
let db = null;
|
|||
|
|
exports.main = async (content, mongodb) => {
|
|||
|
|
db = mongodb;
|
|||
|
|
switch (content.type) {
|
|||
|
|
case "batchUpdateCustomer":
|
|||
|
|
return await this.batchUpdateCustomer(content);
|
|||
|
|
case "searchCorpCustomer":
|
|||
|
|
return await this.searchCorpCustomer(content);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
exports.searchCorpCustomer = async function (context) {
|
|||
|
|
if (!context.corpId) return { success: false, message: "机构id不能为空" };
|
|||
|
|
try {
|
|||
|
|
const query = { corpId: context.corpId };
|
|||
|
|
const page = parseInt(context.page) > 0 ? parseInt(context.page) : 1;
|
|||
|
|
const pageSize = parseInt(context.pageSize) > 0 ? parseInt(context.pageSize) : 10;
|
|||
|
|
const andList = []; // 并且的查询条件列表
|
|||
|
|
// 客户姓名筛选
|
|||
|
|
if (typeof context.name === "string" && context.name.trim()) {
|
|||
|
|
query.name = { $regex: ".*" + context.name.trim(), $options: "i" };
|
|||
|
|
}
|
|||
|
|
// 手机号筛选
|
|||
|
|
if (typeof context.mobile === "string" && context.mobile.trim()) {
|
|||
|
|
query.mobile = { $regex: ".*" + context.mobile.trim(), $options: "i" };
|
|||
|
|
}
|
|||
|
|
// 性别筛选
|
|||
|
|
if (typeof context.sex === "string" && context.sex.trim()) {
|
|||
|
|
query.sex = { $regex: ".*" + context.sex.trim(), $options: "i" };
|
|||
|
|
}
|
|||
|
|
// 客户阶段筛选
|
|||
|
|
if (Array.isArray(context.customerStage) && context.customerStage.length) {
|
|||
|
|
query.customerStage = { $in: context.customerStage }
|
|||
|
|
}
|
|||
|
|
// 客户来源筛选
|
|||
|
|
if (Array.isArray(context.customerSource) && context.customerSource.length) {
|
|||
|
|
query.customerSource = { $in: context.customerSource }
|
|||
|
|
}
|
|||
|
|
// 标签筛选
|
|||
|
|
if (Array.isArray(context.tagIds) && context.tagIds.length) {
|
|||
|
|
query.tagIds = { $in: context.tagIds }
|
|||
|
|
}
|
|||
|
|
// 添加类型筛选
|
|||
|
|
if (context.addMethod) {
|
|||
|
|
query.addMethod = context.addMethod;
|
|||
|
|
}
|
|||
|
|
// 创建人类型筛选
|
|||
|
|
if (context.creators && context.creators.length) {
|
|||
|
|
query.creator = { $in: context.creators };
|
|||
|
|
}
|
|||
|
|
// 根据外部联系人id筛选
|
|||
|
|
if (context.externalUserId) {
|
|||
|
|
query["externalUserId"] = context.externalUserId;
|
|||
|
|
}
|
|||
|
|
// 团队筛选
|
|||
|
|
if (context.teamId === "NO_TEAM") {
|
|||
|
|
query.teamId = { $in: [[], null, ""] }
|
|||
|
|
} else if (context.teamId === "HAS_TEAM") {
|
|||
|
|
query.teamId = { $nin: [[], null, ""] };
|
|||
|
|
} else if (Array.isArray(context.teamId)) {
|
|||
|
|
query.teamId = { $in: context.teamId };
|
|||
|
|
} else if (context.teamId) {
|
|||
|
|
query.teamId = context.teamId
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 筛选年龄区间
|
|||
|
|
const minAge = parseInt(context.minAge);
|
|||
|
|
const maxAge = parseInt(context.maxAge);
|
|||
|
|
if (minAge >= 0 && maxAge >= 0) {
|
|||
|
|
const minAgeStamp = dayjs().subtract(minAge, "year").endOf("day").valueOf();
|
|||
|
|
const maxAgeStamp = dayjs().subtract(maxAge + 1, "year").startOf("day").valueOf();
|
|||
|
|
andList.push({ $or: [{ age: { $gte: minAge, $lte: maxAge } }, { birthdayStamp: { $gt: maxAgeStamp, $lte: minAgeStamp } }] })
|
|||
|
|
} else if (minAge >= 0) {
|
|||
|
|
const minAgeStamp = dayjs().subtract(minAge, "year").endOf("day").valueOf();
|
|||
|
|
andList.push({ $or: [{ age: { $gte: minAge } }, { birthdayStamp: { $lte: minAgeStamp } }] })
|
|||
|
|
} else if (maxAge >= 0) {
|
|||
|
|
const maxAgeStamp = dayjs().subtract(maxAge + 1, "year").startOf("day").valueOf();
|
|||
|
|
andList.push({ $or: [{ age: { $lte: maxAge } }, { birthdayStamp: { $gte: maxAgeStamp } }] })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 责任人筛选
|
|||
|
|
if (context.customerType === "myCustomer") {
|
|||
|
|
// 查询我的客户
|
|||
|
|
query["personResponsibles"] = {
|
|||
|
|
$elemMatch: {
|
|||
|
|
corpUserId: context.userId,
|
|||
|
|
teamId: context.teamId,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
} else if (Array.isArray(context.personResponsibles) && context.personResponsibles.length) {
|
|||
|
|
const personResponsibles = context.personResponsibles.filter(i => i !== "NO"); //过滤无责任人
|
|||
|
|
const noPersonResponsible = context.personResponsibles.includes("NO"); //是否有 无责任人选项
|
|||
|
|
const orList = [];
|
|||
|
|
if (personResponsibles.length) {
|
|||
|
|
orList.push({
|
|||
|
|
$elemMatch: {
|
|||
|
|
corpUserId: { $in: personResponsibles },
|
|||
|
|
teamId: Array.isArray(context.teamId)
|
|||
|
|
? { $in: context.teamId }
|
|||
|
|
: context.teamId,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
if (noPersonResponsible) {
|
|||
|
|
orList.push({
|
|||
|
|
$not: {
|
|||
|
|
$elemMatch: {
|
|||
|
|
teamId: Array.isArray(context.teamId)
|
|||
|
|
? { $in: context.teamId }
|
|||
|
|
: context.teamId,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
if (orList.length === 1) {
|
|||
|
|
query["personResponsibles"] = orList[0];
|
|||
|
|
} else if (orList.length > 1) {
|
|||
|
|
andList.push({
|
|||
|
|
$or: orList.map(i => ({
|
|||
|
|
personResponsibles: i
|
|||
|
|
}))
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 网店报备责任人筛选
|
|||
|
|
if (context.reportRersonResponsibles) {
|
|||
|
|
query["personResponsibles"] = {
|
|||
|
|
$elemMatch: {
|
|||
|
|
corpUserId: { $in: context.reportRersonResponsibles },
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 筛选是否分组
|
|||
|
|
if (context.hasGroup === "YES") {
|
|||
|
|
const groupIds = await getGroupIds(context.corpId, context.teamId);
|
|||
|
|
query["groupIds"] = { $in: groupIds };
|
|||
|
|
} else if (context.hasGroup === "NO") {
|
|||
|
|
const groupIds = await getGroupIds(context.corpId, context.teamId);
|
|||
|
|
query["groupIds"] = { $nin: groupIds };
|
|||
|
|
}
|
|||
|
|
if (Array.isArray(context.groupIds)) {
|
|||
|
|
query.groupIds = { $in: context.groupIds }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建时间 筛选
|
|||
|
|
const createTimeQuery = getTimeRange(context.startCreateTime, context.endCreateTime);
|
|||
|
|
if (createTimeQuery) {
|
|||
|
|
query.createTime = createTimeQuery;
|
|||
|
|
}
|
|||
|
|
// 最近到院时间筛选
|
|||
|
|
const arriveTimeQuery = getTimeRange(context.startArriveTime, context.endArriveTime);
|
|||
|
|
if (arriveTimeQuery)
|
|||
|
|
query.lastVisitTime = arriveTimeQuery;
|
|||
|
|
// 最近服务时间筛选
|
|||
|
|
const serviceTimeQuery = getTimeRange(context.startServiceTime, context.endServiceTime);
|
|||
|
|
if (serviceTimeQuery) {
|
|||
|
|
query.serviceTime = serviceTimeQuery;
|
|||
|
|
}
|
|||
|
|
if (context.serviceTime === "NO") {
|
|||
|
|
query.serviceTime = { $or: [{ $exists: false }, { $eq: "" }] };
|
|||
|
|
}
|
|||
|
|
// 入院时间筛选
|
|||
|
|
if (Array.isArray(context.inHospitalDates) && context.inHospitalDates.length === 2) {
|
|||
|
|
const inHospitalTimesQuery = getTimeRange(...context.inHospitalDates);
|
|||
|
|
if (inHospitalTimesQuery) {
|
|||
|
|
query.inHospitalTimes = inHospitalTimesQuery;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 根据意向项目id筛选
|
|||
|
|
if (Array.isArray(context.projectIds) && context.projectIds.length) {
|
|||
|
|
query["projects._id"] = { $in: context.projectIds };
|
|||
|
|
}
|
|||
|
|
if (context.reportTeamId) {
|
|||
|
|
query.reportTeamId = context.reportTeamId
|
|||
|
|
};
|
|||
|
|
if (Array.isArray(context.reportUserIds) && context.reportUserIds) {
|
|||
|
|
query.reportUserId = { $in: context.reportUserIds };
|
|||
|
|
}
|
|||
|
|
const allQuery = andList.length ? { $and: [...andList, query] } : query;
|
|||
|
|
// if ($or.length) query["$or"] = $or;
|
|||
|
|
const total = await db.collection("member").countDocuments(allQuery);
|
|||
|
|
let listQuery = [
|
|||
|
|
{ $match: allQuery },
|
|||
|
|
{ $sort: { createTime: -1 } },
|
|||
|
|
{ $skip: pageSize * (page - 1) },
|
|||
|
|
{ $limit: pageSize },
|
|||
|
|
];
|
|||
|
|
// 查询是否有回访计划
|
|||
|
|
if (context.showHasPlan === "YES") {
|
|||
|
|
listQuery.push(
|
|||
|
|
{
|
|||
|
|
$lookup: {
|
|||
|
|
from: "to-do-events",
|
|||
|
|
localField: "_id",
|
|||
|
|
foreignField: "customerId",
|
|||
|
|
as: "todo",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
$addFields: {
|
|||
|
|
hasPlan: {
|
|||
|
|
$cond: {
|
|||
|
|
if: {
|
|||
|
|
$and: [
|
|||
|
|
{ $in: ["untreated", "$todo.eventStatus"] },
|
|||
|
|
{
|
|||
|
|
$in: [context.createTeamId, "$todo.executeTeamId"],
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
then: true,
|
|||
|
|
else: false,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
$project: {
|
|||
|
|
// 第六阶段:选择返回的字段
|
|||
|
|
todo: 0,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
// 查询消费金额
|
|||
|
|
if (context.showConsumeAmount === "YES") {
|
|||
|
|
listQuery.push({
|
|||
|
|
$lookup: {
|
|||
|
|
from: "consume-record",
|
|||
|
|
localField: "_id",
|
|||
|
|
foreignField: "customerId",
|
|||
|
|
as: "consumeRecord",
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
const list = await db.collection("member").aggregate(listQuery).toArray();
|
|||
|
|
const pages = Math.ceil(total / pageSize);
|
|||
|
|
if (context.showProjectName === "YES") {
|
|||
|
|
const projectIds = [];
|
|||
|
|
list.forEach((item) => {
|
|||
|
|
item.projectIds = Array.isArray(item.projects)
|
|||
|
|
? item.projects
|
|||
|
|
.map((project) => (project && project._id ? project._id : ""))
|
|||
|
|
.filter(Boolean)
|
|||
|
|
: [];
|
|||
|
|
projectIds.push(...item.projectIds);
|
|||
|
|
});
|
|||
|
|
const res = await api.getCorpApi({
|
|||
|
|
type: "getProjectNames",
|
|||
|
|
corpId: context.corpId,
|
|||
|
|
ids: projectIds,
|
|||
|
|
});
|
|||
|
|
const m = new Map();
|
|||
|
|
const projects = res && Array.isArray(res.data) ? res.data : [];
|
|||
|
|
projects.forEach((item) => {
|
|||
|
|
if (item._id && item.projectName) {
|
|||
|
|
m.set(item._id, item.projectName);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
list.forEach((item) => {
|
|||
|
|
item.projectNames = item.projectIds
|
|||
|
|
.map((id) => m.get(id))
|
|||
|
|
.filter(Boolean);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
return { success: true, total, list, pages };
|
|||
|
|
} catch (error) {
|
|||
|
|
return {
|
|||
|
|
success: false,
|
|||
|
|
message: error.message,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
async function getGroupIds(corpId, teamId) {
|
|||
|
|
try {
|
|||
|
|
// 1. 查询条件:根据机构ID和团队ID筛选对应的分组数据
|
|||
|
|
const query = { corpId, teamId };
|
|||
|
|
|
|||
|
|
// 2. 执行查询:获取符合条件的分组数据,使用 MongoDB 原生查询语法
|
|||
|
|
const result = await db
|
|||
|
|
.collection("group")
|
|||
|
|
.find(query, { projection: { _id: 1 } })
|
|||
|
|
.toArray();
|
|||
|
|
|
|||
|
|
// 3. 提取分组的ID并返回:映射结果集合,提取每个分组的ID字段
|
|||
|
|
const groupIds = result.map((item) => item._id);
|
|||
|
|
|
|||
|
|
return groupIds; // 返回分组ID列表
|
|||
|
|
} catch (error) {
|
|||
|
|
// 4. 错误处理:如果查询发生错误,捕获异常并打印错误日志
|
|||
|
|
logger.error("Error getting group IDs:", error);
|
|||
|
|
return []; // 返回空数组,表示查询失败或无数据
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 批量更新客户信息
|
|||
|
|
* 暂支持 customerSource 客户来源及关联的推荐人
|
|||
|
|
* 暂支持 tagIds 客户标签
|
|||
|
|
* 暂支持 customerStage 客户阶段
|
|||
|
|
* @param {*} context
|
|||
|
|
* @returns
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
exports.batchUpdateCustomer = async (context) => {
|
|||
|
|
const { corpId, customerIds, modifyField } = context;
|
|||
|
|
// 1. 验证输入参数
|
|||
|
|
if (!corpId) return { success: false, message: "机构id不能为空" };
|
|||
|
|
if (!Array.isArray(customerIds) || customerIds.length === 0)
|
|||
|
|
return { success: false, message: "客户id不能为空" };
|
|||
|
|
if (!["customerSource", "tagIds", "customerStage"].includes(modifyField)) {
|
|||
|
|
return { success: false, message: "修改字段不存在或者修改字段不支持" };
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
// 2. 批量修改客户来源
|
|||
|
|
if (modifyField === "customerSource") {
|
|||
|
|
const {
|
|||
|
|
customerSource,
|
|||
|
|
reference,
|
|||
|
|
referenceCustomerId,
|
|||
|
|
referenceType,
|
|||
|
|
referenceUserId,
|
|||
|
|
} = context;
|
|||
|
|
|
|||
|
|
// 2.1. 验证客户来源相关参数
|
|||
|
|
if (
|
|||
|
|
!Array.isArray(customerSource) ||
|
|||
|
|
[reference, referenceCustomerId, referenceType, referenceUserId].some(
|
|||
|
|
(i) => typeof i !== "string"
|
|||
|
|
)
|
|||
|
|
) {
|
|||
|
|
return { success: false, message: "客户来源参数错误" };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2.2. 使用MongoDB原生查询批量更新
|
|||
|
|
const updateResult = await db.collection("member").updateMany(
|
|||
|
|
{ corpId, _id: { $in: customerIds } }, // 查询条件
|
|||
|
|
{
|
|||
|
|
$set: {
|
|||
|
|
customerSource,
|
|||
|
|
reference,
|
|||
|
|
referenceCustomerId,
|
|||
|
|
referenceType,
|
|||
|
|
referenceUserId,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (updateResult.modifiedCount > 0) {
|
|||
|
|
return { success: true, message: "修改成功" };
|
|||
|
|
} else {
|
|||
|
|
return { success: false, message: "没有客户需要更新" };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 批量修改客户标签
|
|||
|
|
else if (modifyField === "tagIds") {
|
|||
|
|
const { tagIds, tagType } = context;
|
|||
|
|
|
|||
|
|
// 3.1. 验证标签参数
|
|||
|
|
if (!Array.isArray(tagIds))
|
|||
|
|
return { success: false, message: "标签类型错误" };
|
|||
|
|
|
|||
|
|
if (tagType === "cover") {
|
|||
|
|
// 3.2. 覆盖原有标签
|
|||
|
|
const updateResult = await db
|
|||
|
|
.collection("member")
|
|||
|
|
.updateMany(
|
|||
|
|
{ corpId, _id: { $in: customerIds } },
|
|||
|
|
{ $set: { tagIds } }
|
|||
|
|
);
|
|||
|
|
return updateResult.modifiedCount > 0
|
|||
|
|
? { success: true, message: "修改成功" }
|
|||
|
|
: { success: false, message: "没有客户需要更新" };
|
|||
|
|
} else if (tagType === "append") {
|
|||
|
|
// 3.3. 追加标签
|
|||
|
|
if (tagIds.length === 0) return { success: true, message: "修改成功" };
|
|||
|
|
|
|||
|
|
const customerList = await db
|
|||
|
|
.collection("member")
|
|||
|
|
.find({ corpId, _id: { $in: customerIds } })
|
|||
|
|
// .project({ _id: 1, tagIds: 1 })
|
|||
|
|
.toArray();
|
|||
|
|
|
|||
|
|
if (customerList.length === 0)
|
|||
|
|
return { success: false, message: "客户不存在" };
|
|||
|
|
|
|||
|
|
console.log("customerList", customerList);
|
|||
|
|
// 合并标签并更新
|
|||
|
|
const updatedCustomerList = customerList.map((customer) => {
|
|||
|
|
const existingTagIds = Array.isArray(customer.tagIds)
|
|||
|
|
? customer.tagIds
|
|||
|
|
: [];
|
|||
|
|
const newTagIds = Array.from(new Set([...existingTagIds, ...tagIds]));
|
|||
|
|
return { _id: customer._id, tagIds: newTagIds };
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log("updatedCustomerList", updatedCustomerList);
|
|||
|
|
|
|||
|
|
// 批量更新客户标签
|
|||
|
|
const bulkOperations = updatedCustomerList.map((customer) => ({
|
|||
|
|
updateOne: {
|
|||
|
|
filter: { _id: customer._id },
|
|||
|
|
update: { $set: { tagIds: customer.tagIds || [] } },
|
|||
|
|
},
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
const bulkWriteResult = await db
|
|||
|
|
.collection("member")
|
|||
|
|
.bulkWrite(bulkOperations);
|
|||
|
|
|
|||
|
|
return bulkWriteResult.modifiedCount > 0
|
|||
|
|
? { success: true, message: "修改成功" }
|
|||
|
|
: { success: false, message: "没有客户需要更新" };
|
|||
|
|
} else {
|
|||
|
|
return { success: false, message: "标签修改类型错误" };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 4. 批量修改客户阶段
|
|||
|
|
else if (modifyField === "customerStage") {
|
|||
|
|
const { customerStage } = context;
|
|||
|
|
|
|||
|
|
// 4.1. 验证客户阶段参数
|
|||
|
|
if (typeof customerStage !== "string")
|
|||
|
|
return { success: false, message: "客户阶段参数错误" };
|
|||
|
|
|
|||
|
|
// 4.2. 使用MongoDB原生查询批量更新
|
|||
|
|
const updateResult = await db
|
|||
|
|
.collection("member")
|
|||
|
|
.updateMany(
|
|||
|
|
{ corpId, _id: { $in: customerIds } },
|
|||
|
|
{ $set: { customerStage } }
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return updateResult.modifiedCount > 0
|
|||
|
|
? { success: true, message: "修改成功" }
|
|||
|
|
: { success: false, message: "没有客户需要更新" };
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
// 错误处理:如果发生错误,返回错误信息
|
|||
|
|
logger.error("批量更新客户时发生错误:", error);
|
|||
|
|
return {
|
|||
|
|
success: false,
|
|||
|
|
message: error.message,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
function getTimeRange(startTime, endTime) {
|
|||
|
|
const startTimeStamp = startTime && dayjs(startTime).isValid() ? dayjs(startTime).valueOf() : 0;
|
|||
|
|
const endTimeStamp = endTime && dayjs(endTime).isValid() ? dayjs(endTime).valueOf() : 0;
|
|||
|
|
if (startTimeStamp && endTimeStamp) {
|
|||
|
|
return { $gte: startTimeStamp, $lte: endTimeStamp }
|
|||
|
|
}
|
|||
|
|
if (startTimeStamp) {
|
|||
|
|
return { $gte: startTimeStamp }
|
|||
|
|
}
|
|||
|
|
if (endTimeStamp) {
|
|||
|
|
return { $lte: endTimeStamp }
|
|||
|
|
}
|
|||
|
|
return ''
|
|||
|
|
}
|