235 lines
10 KiB
JavaScript
235 lines
10 KiB
JavaScript
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;
|