Files
autoAiWorkSys/api/middleware/job/managers/chatManager.js
张成 c45ea21c83 1
2025-12-30 16:18:28 +08:00

424 lines
13 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.
const aiServiceModule = require('../../../services/ai_service');
const logs = require('../../logProxy');
// 实例化AI服务
const aiService = aiServiceModule.getInstance();
/**
* 智能聊天管理模块
* 负责聊天内容生成、发送策略和效果监控
*/
class ChatManager {
constructor() {
this.chatHistory = new Map(); // 聊天历史记录
this.chatStrategies = new Map(); // 聊天策略配置
this.effectStats = new Map(); // 聊天效果统计
this.initDefaultStrategies();
}
/**
* 初始化默认聊天策略
*/
initDefaultStrategies() {
// 初次打招呼策略
this.chatStrategies.set('greeting', {
name: '初次打招呼',
description: '向HR发送初次打招呼消息',
template: 'greeting',
timing: 'immediate',
retryCount: 1,
retryInterval: 300000 // 5分钟
});
// 面试邀约策略
this.chatStrategies.set('interview', {
name: '面试邀约',
description: '发送面试邀约消息',
template: 'interview',
timing: 'after_greeting',
retryCount: 2,
retryInterval: 600000 // 10分钟
});
// 跟进沟通策略
this.chatStrategies.set('followup', {
name: '跟进沟通',
description: '跟进之前的沟通',
template: 'followup',
timing: 'after_interview',
retryCount: 1,
retryInterval: 86400000 // 24小时
});
}
/**
* 生成聊天内容
* @param {string} sn_code - 设备SN码
* @param {object} jobInfo - 岗位信息
* @param {object} resumeInfo - 简历信息
* @param {string} chatType - 聊天类型
* @param {object} context - 聊天上下文
* @returns {Promise<object>} 聊天内容
*/
async generateChatContent(sn_code, jobInfo, resumeInfo, chatType = 'greeting', context = {}) {
console.log(`[聊天管理] 开始生成设备 ${sn_code} 的聊天内容,类型: ${chatType}`);
// 获取聊天策略
const strategy = this.chatStrategies.get(chatType);
if (!strategy) {
throw new Error(`未找到聊天类型 ${chatType} 的策略配置`);
}
// 二期规划AI 生成聊天内容暂时禁用,使用默认模板
// const chatContent = await aiService.generateChatContent(jobInfo, resumeInfo, chatType);
// if (!chatContent.success) {
// console.error(`[聊天管理] AI生成聊天内容失败:`, chatContent.error);
// throw new Error(chatContent.error);
// }
console.log(`[聊天管理] AI生成已禁用二期规划使用默认聊天模板`);
const chatContent = this.generateDefaultChatContent(jobInfo, resumeInfo, chatType);
const result = {
sn_code: sn_code,
jobInfo: jobInfo,
chatType: chatType,
strategy: strategy,
content: chatContent.content,
context: context,
timestamp: Date.now()
};
// 记录聊天历史
this.recordChatHistory(sn_code, result);
console.log(`[聊天管理] 聊天内容生成成功:`, result);
return result;
}
/**
* 发送聊天消息
* @param {string} sn_code - 设备SN码
* @param {object} mqttClient - MQTT客户端
* @param {object} chatData - 聊天数据
* @returns {Promise<object>} 发送结果
*/
async sendChatMessage(sn_code, mqttClient, chatData) {
console.log(`[聊天管理] 开始发送聊天消息到设备 ${sn_code}`);
// 构建发送指令
const sendData = {
platform: 'boss',
action: 'send_chat_message',
data: {
jobId: chatData.jobInfo.jobId,
companyId: chatData.jobInfo.companyId,
message: chatData.content,
chatType: chatData.chatType
}
};
// 发送MQTT指令
const response = await mqttClient.publishAndWait(sn_code, sendData);
if (!response || response.code !== 200) {
// 更新聊天状态
this.updateChatStatus(sn_code, chatData, 'failed', response);
console.error(`[聊天管理] 聊天消息发送失败:`, response);
throw new Error(response?.message || '聊天消息发送失败');
}
// 更新聊天状态
this.updateChatStatus(sn_code, chatData, 'sent', response);
// 记录效果统计
this.recordChatEffect(sn_code, chatData, 'sent');
console.log(`[聊天管理] 聊天消息发送成功:`, response);
return response;
}
/**
* 生成面试邀约
* @param {string} sn_code - 设备SN码
* @param {object} jobInfo - 岗位信息
* @param {object} chatHistory - 聊天历史
* @returns {Promise<object>} 面试邀约内容
*/
async generateInterviewInvitation(sn_code, jobInfo, chatHistory) {
console.log(`[聊天管理] 开始生成设备 ${sn_code} 的面试邀约`);
console.log(`[聊天管理] AI生成已禁用二期规划使用默认模板`);
// 二期规划AI 生成面试邀约暂时禁用,使用默认模板
// const invitation = await aiService.generateInterviewInvitation(jobInfo, chatHistory);
// if (!invitation.success) {
// console.error(`[聊天管理] AI生成面试邀约失败:`, invitation.error);
// throw new Error(invitation.error);
// }
const invitation = this.generateDefaultInterviewInvitation(jobInfo);
const result = {
sn_code: sn_code,
jobInfo: jobInfo,
chatType: 'interview',
content: invitation.content,
timestamp: Date.now()
};
// 记录聊天历史
this.recordChatHistory(sn_code, result);
console.log(`[聊天管理] 面试邀约生成成功:`, result);
return result;
}
/**
* 记录聊天历史
* @param {string} sn_code - 设备SN码
* @param {object} chatData - 聊天数据
*/
recordChatHistory(sn_code, chatData) {
if (!this.chatHistory.has(sn_code)) {
this.chatHistory.set(sn_code, []);
}
const history = this.chatHistory.get(sn_code);
history.push({
...chatData,
id: Date.now() + Math.random(),
status: 'generated'
});
// 限制历史记录数量
if (history.length > 100) {
history.splice(0, history.length - 100);
}
}
/**
* 更新聊天状态
* @param {string} sn_code - 设备SN码
* @param {object} chatData - 聊天数据
* @param {string} status - 新状态
* @param {object} response - 响应数据
*/
updateChatStatus(sn_code, chatData, status, response = {}) {
if (!this.chatHistory.has(sn_code)) {
return;
}
const history = this.chatHistory.get(sn_code);
const chatRecord = history.find(record =>
record.timestamp === chatData.timestamp &&
record.chatType === chatData.chatType
);
if (chatRecord) {
chatRecord.status = status;
chatRecord.response = response;
}
}
/**
* 记录聊天效果
* @param {string} sn_code - 设备SN码
* @param {object} chatData - 聊天数据
* @param {string} action - 动作类型
*/
recordChatEffect(sn_code, chatData, action) {
if (!this.effectStats.has(sn_code)) {
this.effectStats.set(sn_code, {
totalSent: 0,
totalReplied: 0,
totalInterview: 0,
replyRate: 0,
interviewRate: 0,
lastUpdate: Date.now()
});
}
const stats = this.effectStats.get(sn_code);
if (action === 'sent') {
stats.totalSent++;
} else if (action === 'replied') {
stats.totalReplied++;
} else if (action === 'interview') {
stats.totalInterview++;
}
// 计算比率
if (stats.totalSent > 0) {
stats.replyRate = (stats.totalReplied / stats.totalSent * 100).toFixed(2);
stats.interviewRate = (stats.totalInterview / stats.totalSent * 100).toFixed(2);
}
stats.lastUpdate = Date.now();
}
/**
* 获取聊天历史
* @param {string} sn_code - 设备SN码
* @param {object} filters - 过滤条件
* @returns {Array} 聊天历史
*/
getChatHistory(sn_code, filters = {}) {
if (!this.chatHistory.has(sn_code)) {
return [];
}
let history = this.chatHistory.get(sn_code);
// 应用过滤条件
if (filters.chatType) {
history = history.filter(record => record.chatType === filters.chatType);
}
if (filters.status) {
history = history.filter(record => record.status === filters.status);
}
if (filters.startTime) {
history = history.filter(record => record.timestamp >= filters.startTime);
}
if (filters.endTime) {
history = history.filter(record => record.timestamp <= filters.endTime);
}
// 按时间倒序排列
return history.sort((a, b) => b.timestamp - a.timestamp);
}
/**
* 获取聊天效果统计
* @param {string} sn_code - 设备SN码
* @returns {object} 效果统计
*/
getChatEffectStats(sn_code) {
if (!this.effectStats.has(sn_code)) {
return {
totalSent: 0,
totalReplied: 0,
totalInterview: 0,
replyRate: 0,
interviewRate: 0,
lastUpdate: Date.now()
};
}
return this.effectStats.get(sn_code);
}
/**
* 设置聊天策略
* @param {string} chatType - 聊天类型
* @param {object} strategy - 策略配置
*/
setChatStrategy(chatType, strategy) {
this.chatStrategies.set(chatType, {
...this.chatStrategies.get(chatType),
...strategy
});
console.log(`[聊天管理] 更新聊天策略 ${chatType}:`, strategy);
}
/**
* 清理过期数据
*/
cleanup() {
const now = Date.now();
const expireTime = 30 * 24 * 3600000; // 30天
// 清理过期的聊天历史
for (const [sn_code, history] of this.chatHistory.entries()) {
const filteredHistory = history.filter(record =>
now - record.timestamp < expireTime
);
if (filteredHistory.length === 0) {
this.chatHistory.delete(sn_code);
} else {
this.chatHistory.set(sn_code, filteredHistory);
}
}
// 清理过期的效果统计
for (const [sn_code, stats] of this.effectStats.entries()) {
if (now - stats.lastUpdate > expireTime) {
this.effectStats.delete(sn_code);
}
}
console.log(`[聊天管理] 数据清理完成`);
}
/**
* 生成默认聊天内容(替代 AI 生成)
* @param {object} jobInfo - 岗位信息
* @param {object} resumeInfo - 简历信息
* @param {string} chatType - 聊天类型
* @returns {object} 聊天内容
*/
generateDefaultChatContent(jobInfo, resumeInfo, chatType) {
const templates = {
greeting: '您好,我对这个岗位很感兴趣,希望能进一步了解。',
interview: '感谢您的回复,我很期待与您进一步沟通。',
followup: '您好,想了解一下这个岗位的最新进展。'
};
const content = templates[chatType] || templates.greeting;
return {
success: true,
content: content,
chatType: chatType
};
}
/**
* 生成默认面试邀约(替代 AI 生成)
* @param {object} jobInfo - 岗位信息
* @returns {object} 面试邀约内容
*/
generateDefaultInterviewInvitation(jobInfo) {
return {
success: true,
content: '感谢您的邀请,我很期待与您面谈。请问方便的时间是什么时候?',
jobTitle: jobInfo.jobTitle || '该岗位',
companyName: jobInfo.companyName || '贵公司'
};
}
/**
* 获取聊天列表
* @param {string} sn_code - 设备SN码
* @param {object} mqttClient - MQTT客户端
* @param {object} params - 参数
* @returns {Promise<object>} 聊天列表
*/
async get_chat_list(sn_code, mqttClient, params = {}) {
const { platform = 'boss', pageCount = 3 } = params;
console.log(`[聊天管理] 开始获取设备 ${sn_code} 的聊天列表`);
// 通过MQTT指令获取聊天列表
const response = await mqttClient.publishAndWait(sn_code, {
platform,
action: "get_chat_list",
data: { pageCount }
});
if (!response || response.code !== 200) {
console.error(`[聊天管理] 获取聊天列表失败:`, response);
throw new Error('获取聊天列表失败');
}
console.log(`[聊天管理] 成功获取聊天列表`);
return response.data;
}
}
module.exports = new ChatManager();