ykt-wxapp/utils/send-message-helper.js
2026-03-05 11:00:32 +08:00

452 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 消息发送助手 - 统一处理不同类型消息的发送
*/
import { globalTimChatManager } from './tim-chat.js';
import api from './api.js';
import { toast } from './widget.js';
const env = __VITE_ENV__;
function nowTs() {
return Date.now();
}
async function tryAddServiceRecord(payload) {
try {
const res = await api('addServiceRecord', payload);
if (!res?.success) {
console.warn('写入服务记录失败:', res?.message || res);
}
} catch (e) {
console.error('写入服务记录异常:', e);
}
}
function canWriteServiceRecord(options = {}) {
return Boolean(options?.corpId && options?.userId && options?.customerId);
}
function normalizeServiceRecordBase(options = {}) {
return {
corpId: options.corpId,
executorUserId: String(options.userId || ''),
creatorUserId: String(options.userId || ''),
customerId: String(options.customerId || ''),
customerName: String(options.customerName || ''),
executeTeamId: String(options.teamId || options.executeTeamId || ''),
executionTime: nowTs(),
externalUserId: String(options.externalUserId || options.customerUserId || ''),
};
}
/**
* 发送文字消息
* @param {string} content - 文字内容
* @returns {Promise<boolean>} 发送是否成功
*/
export async function sendTextMessage(content) {
if (!content || !content.trim()) {
toast('文字内容不能为空');
return false;
}
try {
if (!globalTimChatManager?.isLoggedIn) {
toast('IM系统未就绪请稍后重试');
return false;
}
// 直接调用 tim-chat 的 sendTextMessage 方法
const result = await globalTimChatManager.sendTextMessage(content.trim());
if (result?.success) {
return true;
} else {
toast(result?.error || '发送文字消息失败');
return false;
}
} catch (error) {
console.error('发送文字消息异常:', error);
toast('发送文字消息失败');
return false;
}
}
/**
* 发送图片消息
* @param {string|File} imageSource - 图片URL字符串或文件对象
* @param {string} imageName - 图片名称(可选)
* @returns {Promise<boolean>} 发送是否成功
*/
export async function sendImageMessage(imageSource, imageName = '图片') {
if (!imageSource) {
toast('图片不能为空');
return false;
}
try {
if (!globalTimChatManager?.isLoggedIn) {
toast('IM系统未就绪请稍后重试');
return false;
}
// 如果是URL字符串需要先下载转换为文件对象
let imageFile = imageSource;
if (typeof imageSource === 'string') {
imageFile = await downloadImageAsFile(imageSource, imageName);
if (!imageFile) {
toast('图片下载失败');
return false;
}
}
const result = await globalTimChatManager.sendImageMessage(imageFile);
if (result?.success) {
return true;
} else {
toast(result?.error || '发送图片消息失败');
return false;
}
} catch (error) {
console.error('发送图片消息异常:', error);
toast('发送图片消息失败');
return false;
}
}
/**
* 将图片URL下载转换为文件对象小程序环境
* @param {string} imageUrl - 图片URL
* @param {string} fileName - 文件名
* @returns {Promise<Object|null>} 文件对象或null
*/
async function downloadImageAsFile(imageUrl, fileName = '图片') {
return new Promise((resolve) => {
uni.downloadFile({
url: imageUrl,
success: (res) => {
if (res.statusCode === 200) {
// 返回包含 tempFiles 数组的对象,模拟 uni.chooseMedia 的返回格式
const fileObj = {
tempFiles: [{
tempFilePath: res.tempFilePath,
path: res.tempFilePath,
size: 0,
type: 'image'
}],
// 也保留直接的 tempFilePath 以兼容其他代码
tempFilePath: res.tempFilePath
};
console.log('图片下载成功,文件对象:', fileObj);
resolve(fileObj);
} else {
console.error('下载图片失败,状态码:', res.statusCode);
resolve(null);
}
},
fail: (error) => {
console.error('下载图片失败:', error);
resolve(null);
}
});
});
}
/**
* 发送宣教文章消息
* @param {Object} article - 文章对象 { _id, title, cover, url }
* @param {Object} options - 额外选项 { groupId, patientId, corpId }
* @returns {Promise<boolean>} 发送是否成功
*/
export async function sendArticleMessage(article, options = {}) {
if (!article || !article._id) {
toast('文章信息不完整');
return false;
}
try {
if (!globalTimChatManager?.isLoggedIn) {
toast('IM系统未就绪请稍后重试');
return false;
}
// 构建自定义消息 - 直接传递对象sendCustomMessage会处理JSON序列化
const customMessageData = {
type: 'article',
title: article.title || '宣教文章',
articleId: article._id,
cover: article.cover || '',
desc: article.subtitle || '点击查看详情',
url: article.url || '',
messageType: 'article',
};
if (options.articleId && options.userId && options.customerId && options.corpId) {
const params = {
articleId: options.articleId,
userId: options.userId,
customerId: options.customerId,
corpId: options.corpId,
uniqueRecord: 'YES'
};
if (options.teamId) {
params.teamId = options.teamId;
}
const res = await api('addArticleSendRecord', params);
if (!res || !res.success) {
toast('发送文章失败');
return
}
customMessageData.sendId = res.data;
}
// 发送自定义消息
const result = await globalTimChatManager.sendCustomMessage(customMessageData);
if (result?.success) {
// 写入服务记录留痕(异步,不阻塞)
if (canWriteServiceRecord(options)) {
const base = normalizeServiceRecordBase(options);
tryAddServiceRecord({
...base,
eventType: 'ContentReminder',
taskContent: `推送文章:${article.title || '宣教文章'}`,
pannedEventSendFile: {
type: 'article',
articleId: article._id || options.articleId || '',
title: article.title || '',
url: article.url || '',
},
pannedEventName: '宣教发送',
});
}
return true;
} else {
toast(result?.error || '发送文章消息失败');
return false;
}
} catch (error) {
console.error('发送文章消息异常:', error);
toast('发送文章消息失败');
return false;
}
}
/**
* 发送问卷消息
* @param {Object} survey - 问卷对象 { _id, name, surveryId, url, createBy }
* @param {Object} options - 额外选项 { userId, customerId, customerName, corpId }
* @returns {Promise<boolean>} 发送是否成功
*/
export async function sendSurveyMessage(survey, options = {}) {
if (!survey || !survey._id) {
toast('问卷信息不完整');
return false;
}
try {
if (!globalTimChatManager?.isLoggedIn) {
toast('IM系统未就绪请稍后重试');
return false;
}
// 生成发送ID
const sendSurveyId = generateRandomString(10);
const recordParams = {
corpId: options.corpId,
userId: options.userId,
surveryId: survey._id,
memberId: options.customerId,
customer: options.customerName,
sendSurveyId: sendSurveyId,
};
if (options.teamId) {
recordParams.teamId = options.teamId;
}
// 创建问卷记录
const createRecordRes = await api('createSurveyRecord', recordParams);
if (!createRecordRes?.success) {
toast(createRecordRes?.message || '创建问卷记录失败');
return false;
}
const answerId = createRecordRes?.id || '';
// 生成问卷链接
const surveyLink = generateSendLink(
survey,
answerId,
options.customerId,
options.customerName,
sendSurveyId,
{
corpId: options.corpId,
userId: options.userId
}
);
// 构建自定义消息
const customMessageData = buildSurveyMessage(survey, surveyLink);
// 发送自定义消息 - 直接传递消息对象,不再进行额外序列化
const result = await globalTimChatManager.sendCustomMessage(customMessageData);
if (result?.success) {
// 写入服务记录留痕(异步,不阻塞)
if (canWriteServiceRecord(options)) {
const base = normalizeServiceRecordBase(options);
tryAddServiceRecord({
...base,
eventType: 'questionnaire',
taskContent: `推送问卷:${survey.name || '问卷'}`,
pannedEventSendFile: {
type: 'questionnaire',
surveryId: survey._id || survey.surveryId || '',
name: survey.name || '',
url: surveyLink || '',
},
pannedEventName: '问卷调查',
});
}
return true;
} else {
toast(result?.error || '发送问卷消息失败');
return false;
}
} catch (error) {
console.error('发送问卷消息异常:', error);
toast('发送问卷消息失败');
return false;
}
}
/**
* 生成随机字符串
* @param {number} length - 字符串长度
* @returns {string} 随机字符串
*/
function generateRandomString(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
/**
* 生成问卷发送链接
* @param {Object} survey - 问卷对象
* @param {string} answerId - 答卷ID
* @param {string} customerId - 客户ID
* @param {string} customerName - 客户名称
* @param {string} sendSurveyId - 发送问卷ID
* @param {Object} context - 上下文信息 { corpId, userId, env }
* @returns {string} 问卷链接
*/
function generateSendLink(survey, answerId, customerId, customerName, sendSurveyId, context = {}) {
const { corpId, userId } = context;
const isSystem = survey.createBy === 'system';
let url = '';
if (isSystem) {
// 系统问卷:使用 VITE_SURVEY_URL
url = `${env?.MP_SURVEY_URL}?corpId=${corpId}&surveryId=${survey.surveryId}&memberId=${customerId}&sendSurveyId=${sendSurveyId}&userId=${userId}`;
} else {
// 自定义问卷:使用 VITE_PATIENT_PAGE_BASE_URL
url = `${env?.MP_PATIENT_PAGE_BASE_URL}pages/survery/fill?corpId=${corpId}&surveryId=${survey._id}&memberId=${customerId}&unionid=unionid&answerId=${answerId}&name=${customerName || ''}`;
}
// 如果已有链接,直接使用并拼接额外参数
if (survey.url && survey.url.trim()) {
const separator = survey.url.includes('?') ? '&' : '?';
const params = [];
if (answerId) params.push(`answerId=${answerId}`);
if (sendSurveyId) params.push(`sendSurveyId=${sendSurveyId}`);
if (userId) params.push(`userId=${userId}`);
if (customerId) params.push(`memberId=${customerId}`);
if (params.length > 0) {
url = `${survey.url}${separator}${params.join('&')}`;
} else {
url = survey.url;
}
}
return url;
}
/**
* 构建问卷自定义消息
* @param {Object} survey - 问卷对象
* @param {string} surveyLink - 问卷链接
* @returns {Object} 自定义消息对象
*/
function buildSurveyMessage(survey, surveyLink) {
return {
type: 'survey',
title: survey.name || '填写问卷',
desc: '请填写问卷',
url: surveyLink,
imgUrl:
'https://796f-youcan-clouddev-1-8ewcqf31dbb2b5-1317294507.tcb.qcloud.la/other/19-%E9%97%AE%E5%8D%B7.png?sign=55a4cd77c418b2c548b65792a2cf6bce&t=1701328694',
messageType: 'survey',
};
}
/**
* 处理回访任务消息发送
* @param {Object} messages - 消息数组
* @param {Object} context - 上下文信息 { userId, customerId, customerName, corpId }
* @returns {Promise<boolean>} 是否全部发送成功
*/
export async function handleFollowUpMessages(messages, context = {}) {
if (!Array.isArray(messages) || messages.length === 0) {
toast('没有要发送的消息');
return false;
}
try {
let allSuccess = true;
for (const msg of messages) {
let success = false;
if (msg.type === 'text') {
success = await sendTextMessage(msg.content);
} else if (msg.type === 'image') {
success = await sendImageMessage(msg.content, msg.name);
} else if (msg.type === 'article') {
success = await sendArticleMessage(msg.content, {
articleId: msg.content.articleId,
userId: context.userId,
customerId: context.customerId,
corpId: context.corpId,
});
} else if (msg.type === 'questionnaire') {
success = await sendSurveyMessage(msg.content, {
userId: context.userId,
customerId: context.customerId,
customerName: context.customerName,
corpId: context.corpId
});
}
if (!success) {
allSuccess = false;
// 继续发送其他消息,不中断
}
}
return allSuccess;
} catch (error) {
console.error('处理回访任务消息异常:', error);
toast('消息发送过程中出现错误');
return false;
}
}