Files
autoAiWorkSys/api/middleware/schedule/handlers/chatHandler.js
张成 a40219c7e4 1
2026-02-28 17:38:45 +08:00

235 lines
10 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 BaseHandler = require('./baseHandler');
const ConfigManager = require('../services/configManager');
const command = require('../core/command');
const config = require('../infrastructure/config');
const chatManager = require('../../job/managers/chatManager');
const db = require('../../dbProxy');
/**
* 自动沟通处理器
* 负责自动回复 HR 消息。auto_chat 是任务,其下按指令执行:获取列表 → 获取详情 →(若需回复)发送消息
*/
class ChatHandler extends BaseHandler {
/**
* 处理自动沟通任务
* @param {object} task - 任务对象
* @returns {Promise<object>} 执行结果
*/
async handle(task) {
return await this.execute(task, async () => {
return await this.doChat(task);
}, {
checkAuth: true,
checkOnline: true,
recordDeviceMetrics: true
});
}
/**
* 执行沟通逻辑:先下发「获取列表」指令,再对每个会话下发「获取详情」→(若需回复)「发送消息」指令
*/
async doChat(task) {
const { sn_code, taskParams } = task;
const platform = taskParams.platform || 'boss';
console.log(`[自动沟通] 开始 - 设备: ${sn_code}`);
const accountConfig = await this.getAccountConfig(sn_code, ['platform_type', 'chat_strategy']);
if (!accountConfig) {
return { chatCount: 0, message: '未找到账户配置' };
}
const chatStrategy = ConfigManager.parseChatStrategy(accountConfig.chat_strategy);
const timeRange = ConfigManager.getTimeRange(chatStrategy);
if (timeRange) {
const timeRangeValidator = require('../services/timeRangeValidator');
const timeCheck = timeRangeValidator.checkTimeRange(timeRange);
if (!timeCheck.allowed) {
return { chatCount: 0, message: timeCheck.reason };
}
}
const platform_type = platform || accountConfig.platform_type || 'boss';
const page_count = chatStrategy.page_count || 3;
// 1. 下发「获取列表」指令
const list_command = {
command_type: 'get_chat_list',
command_name: '获取聊天列表',
command_params: { platform: platform_type, pageCount: page_count },
priority: config.getTaskPriority('auto_chat') || 6
};
const list_exec = await command.executeCommands(task.id, [list_command], this.mqttClient);
const list_result = list_exec?.results?.[0]?.result;
const friend_list = Array.isArray(list_result?.friendList) ? list_result.friendList : [];
if (friend_list.length === 0) {
console.log(`[自动沟通] 完成 - 设备: ${sn_code},无会话`);
return { chatCount: 0, message: '没有可沟通的会话', detail: { total_contacts: 0 } };
}
this._syncFriendListToChatRecords(sn_code, platform_type, friend_list);
let replied_count = 0;
const details = [];
for (const friend of friend_list) {
if (!friend.friendId) continue;
const item = await this._processOneFriend(task, friend, sn_code, platform_type);
details.push(item);
if (item.replied) replied_count++;
}
console.log(`[自动沟通] 完成 - 设备: ${sn_code},会话 ${friend_list.length},回复 ${replied_count}`);
return {
chatCount: replied_count,
message: '自动沟通完成',
detail: {
total_contacts: friend_list.length,
replied_count,
details
}
};
}
/** 将会话列表同步到 chat_records内部 catch 仅打日志 */
async _syncFriendListToChatRecords(sn_code, platform_type, friend_list) {
try {
const chatRecordsModel = db.getModel('chat_records');
for (const friend of friend_list) {
const friend_id = friend.friendId;
if (friend_id == null) continue;
const encryptId = friend.encryptFriendId || '';
const existing = await chatRecordsModel.findOne({
where: { sn_code, platform: platform_type, encryptFriendId: encryptId }
});
const baseData = {
sn_code,
platform: platform_type,
friendId: friend_id,
encryptFriendId: encryptId,
name: friend.name || '',
updateTime: friend.updateTime != null ? friend.updateTime : null,
brandName: friend.brandName || '',
jobName: friend.jobName || '',
jobCity: friend.jobCity || '',
positionName: friend.positionName || '',
bossTitle: friend.bossTitle || '',
friendSource: friend.friendSource != null ? friend.friendSource : 0,
jobTypeDesc: friend.jobTypeDesc || '',
waterLevel: friend.waterLevel != null ? friend.waterLevel : 0
};
if (existing) await existing.update(baseData);
else await chatRecordsModel.create(baseData);
}
console.log(`[自动沟通] 已同步 ${friend_list.length} 条会话到 chat_records`);
} catch (e) {
console.warn('[自动沟通] 同步聊天会话列表到 chat_records 失败:', e.message);
}
}
/**
* 落库 chat_message入参为解析后格式 { variant, messages, data, job }
* 有 messages 则每条一条记录;仅会话时 mid=0 存 { data, job }
*/
async _saveChatMessagesToDb(parsed, friend, sn_code, platform_type) {
if (!parsed) return;
const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
const has_session = parsed.data != null || parsed.job != null;
if (messages.length === 0 && !has_session) return;
try {
const chatMessageModel = db.getModel('chat_message');
const friend_id = friend.friendId;
const encrypt_id = friend.encryptFriendId || '';
const fetch_time = new Date();
const base = { sn_code, platform: platform_type, friendId: friend_id, encryptFriendId: encrypt_id, fetch_time };
if (messages.length > 0) {
for (const msg of messages) {
const mid = msg.mid != null ? msg.mid : 0;
const existing = await chatMessageModel.findOne({
where: { sn_code, platform: platform_type, friendId: friend_id, mid }
});
const row = { ...base, mid, message_data: msg };
if (existing) await existing.update(row);
else await chatMessageModel.create(row);
}
} else {
const boss_payload = { data: parsed.data || null, job: parsed.job || null };
const existing = await chatMessageModel.findOne({
where: { sn_code, platform: platform_type, friendId: friend_id, mid: 0 }
});
const row = { ...base, mid: 0, message_data: boss_payload };
if (existing) await existing.update(row);
else await chatMessageModel.create(row);
}
} catch (e) {
console.warn('[自动沟通] 写入 chat_message 失败:', e.message);
}
}
/**
* 处理单个会话:获取详情 → 落库 → 判断是否回复 → 若需则发送消息
* @returns {{ friendId: number, replied: boolean, reason: string|null }}
*/
async _processOneFriend(task, friend, sn_code, platform_type) {
const friend_id = friend.friendId;
try {
const detail_command = {
command_type: 'get_chat_detail',
command_name: '获取聊天详情',
command_params: { platform: platform_type, friendId: friend_id },
priority: config.getTaskPriority('auto_chat') || 6
};
const detail_exec = await command.executeCommands(task.id, [detail_command], this.mqttClient);
const result = detail_exec?.results?.[0]?.result;
const parsed = chatManager.parseDetailResponse(result || {});
await this._saveChatMessagesToDb(parsed, friend, sn_code, platform_type);
const decision = await chatManager.getReplyContentFromDetail(parsed || {}, {
sn_code,
platform: platform_type,
friendId: friend_id,
encryptFriendId: friend.encryptFriendId || ''
});
if (decision.replied) {
const action = decision.action || 'text';
const content = decision.reply_content || '';
const actionMessages = {
send_resume: [{ type: 'send_resume', content }],
exchange_wechat: [{ type: 'exchange_wechat', content }],
exchange_phone: [{ type: 'exchange_phone', content }]
};
const messages = actionMessages[action] || [{ type: 'text', content }];
const actionNames = { send_resume: '发送简历', exchange_wechat: '换微信', exchange_phone: '换电话' };
const send_command = {
command_type: 'send_chat_message',
command_name: actionNames[action] || '发送聊天消息',
command_params: {
platform: platform_type,
friendId: friend_id,
messages,
chatType: 'reply'
},
priority: config.getTaskPriority('auto_chat') || 6
};
await command.executeCommands(task.id, [send_command], this.mqttClient);
}
return {
friendId: friend_id,
replied: !!decision.replied,
reason: decision.reason || null
};
} catch (err) {
return { friendId: friend_id, replied: false, reason: err.message || '处理失败' };
}
}
}
module.exports = ChatHandler;