存客宝应用接口初始化
This commit is contained in:
115
application/job/AccountListJob.php
Normal file
115
application/job/AccountListJob.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\AccountController;
|
||||
|
||||
class AccountListJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processAccountList($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('公司账号列表任务执行成功,页码:' . $data['pageIndex']);
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('公司账号列表任务执行失败,已超过重试次数,页码:' . $data['pageIndex']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('公司账号列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $data['pageIndex']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('公司账号列表任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理公司账号列表获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processAccountList($data, $attempts)
|
||||
{
|
||||
// 获取参数
|
||||
$pageIndex = isset($data['pageIndex']) ? $data['pageIndex'] : 0;
|
||||
$pageSize = isset($data['pageSize']) ? $data['pageSize'] : 100;
|
||||
|
||||
Log::info('开始获取公司账号列表,页码:' . $pageIndex . ',页大小:' . $pageSize);
|
||||
|
||||
// 实例化控制器
|
||||
$accountController = new AccountController();
|
||||
|
||||
// 构建请求参数
|
||||
$params = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 设置请求信息
|
||||
$request = request();
|
||||
$request->withGet($params);
|
||||
|
||||
// 调用公司账号列表获取方法
|
||||
$result = $accountController->getlist(['pageIndex' => $pageIndex,'pageSize' => $pageSize],true);
|
||||
$response = json_decode($result,true);
|
||||
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data['results']) > 0) {
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
$this->addNextPageToQueue($nextPageIndex, $pageSize);
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取公司账号列表失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下一页任务到队列
|
||||
* @param int $pageIndex 页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function addNextPageToQueue($pageIndex, $pageSize)
|
||||
{
|
||||
$data = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 account_list
|
||||
Queue::push(self::class, $data, 'account_list');
|
||||
}
|
||||
}
|
||||
96
application/job/AllotChatroomJob.php
Normal file
96
application/job/AllotChatroomJob.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\command\AllotChatroomCommand;
|
||||
use think\facade\Log;
|
||||
use think\facade\Cache;
|
||||
use think\Queue;
|
||||
use app\api\controller\AutomaticAssign;
|
||||
|
||||
class AllotChatroomJob
|
||||
{
|
||||
/**
|
||||
* 队列执行方法
|
||||
* @param $job 队列任务
|
||||
* @param $data 数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire($job, $data)
|
||||
{
|
||||
try {
|
||||
// 获取数据
|
||||
$toAccountId = $data['toAccountId'];
|
||||
$wechatAccountKeyword = $data['wechatAccountKeyword'];
|
||||
$isDeleted = $data['isDeleted'];
|
||||
$jobId = isset($data['jobId']) ? $data['jobId'] : '';
|
||||
$queueLockKey = isset($data['queueLockKey']) ? $data['queueLockKey'] : '';
|
||||
|
||||
// 记录日志
|
||||
Log::info('开始处理微信群聊自动分配任务: ' . json_encode([
|
||||
'toAccountId' => $toAccountId,
|
||||
'wechatAccountKeyword' => $wechatAccountKeyword,
|
||||
'isDeleted' => $isDeleted,
|
||||
'jobId' => $jobId,
|
||||
'queueLockKey' => $queueLockKey
|
||||
]));
|
||||
|
||||
// 如果没有提供队列锁键,生成一个
|
||||
if (empty($queueLockKey)) {
|
||||
$queueLockKey = "queue_lock:allot_chatroom:{$wechatAccountKeyword}";
|
||||
}
|
||||
|
||||
// 实例化控制器
|
||||
$automaticAssignController = new AutomaticAssign();
|
||||
|
||||
// 调用微信群聊自动分配方法
|
||||
$result = $automaticAssignController->autoAllotWechatChatroom($toAccountId, $wechatAccountKeyword, $isDeleted);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 1) {
|
||||
Log::info("微信群聊自动分配成功: toAccountId={$toAccountId}, wechatAccountKeyword={$wechatAccountKeyword}");
|
||||
|
||||
// 释放队列锁
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("任务完成,释放队列锁: {$queueLockKey}");
|
||||
|
||||
$job->delete();
|
||||
return true;
|
||||
} else {
|
||||
// API调用出错,记录错误
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error("微信群聊自动分配失败: {$errorMsg}");
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务并释放队列锁
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于错误多次尝试失败,释放队列锁: {$queueLockKey}");
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning("微信群聊自动分配任务执行失败,重试次数: " . $job->attempts());
|
||||
$job->release(10); // 延迟10秒后重试
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录错误并释放队列锁
|
||||
Log::error('微信群聊自动分配任务异常: ' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(10);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
96
application/job/AllotFriendJob.php
Normal file
96
application/job/AllotFriendJob.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\command\AllotFriendCommand;
|
||||
use think\facade\Log;
|
||||
use think\facade\Cache;
|
||||
use think\Queue;
|
||||
use app\api\controller\AutomaticAssign;
|
||||
|
||||
class AllotFriendJob
|
||||
{
|
||||
/**
|
||||
* 队列执行方法
|
||||
* @param $job 队列任务
|
||||
* @param $data 数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire($job, $data)
|
||||
{
|
||||
try {
|
||||
// 获取数据
|
||||
$toAccountId = $data['toAccountId'];
|
||||
$wechatAccountKeyword = $data['wechatAccountKeyword'];
|
||||
$isDeleted = $data['isDeleted'];
|
||||
$jobId = isset($data['jobId']) ? $data['jobId'] : '';
|
||||
$queueLockKey = isset($data['queueLockKey']) ? $data['queueLockKey'] : '';
|
||||
|
||||
// 记录日志
|
||||
Log::info('开始处理微信好友自动分配任务: ' . json_encode([
|
||||
'toAccountId' => $toAccountId,
|
||||
'wechatAccountKeyword' => $wechatAccountKeyword,
|
||||
'isDeleted' => $isDeleted,
|
||||
'jobId' => $jobId,
|
||||
'queueLockKey' => $queueLockKey
|
||||
]));
|
||||
|
||||
// 如果没有提供队列锁键,生成一个
|
||||
if (empty($queueLockKey)) {
|
||||
$queueLockKey = "queue_lock:allot_friends:{$wechatAccountKeyword}";
|
||||
}
|
||||
|
||||
// 实例化控制器
|
||||
$automaticAssignController = new AutomaticAssign();
|
||||
|
||||
// 调用微信好友自动分配方法
|
||||
$result = $automaticAssignController->autoAllotWechatFriend($toAccountId, $wechatAccountKeyword, $isDeleted);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 1) {
|
||||
Log::info("微信好友自动分配成功: toAccountId={$toAccountId}, wechatAccountKeyword={$wechatAccountKeyword}");
|
||||
|
||||
// 释放队列锁
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("任务完成,释放队列锁: {$queueLockKey}");
|
||||
|
||||
$job->delete();
|
||||
return true;
|
||||
} else {
|
||||
// API调用出错,记录错误
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error("微信好友自动分配失败: {$errorMsg}");
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务并释放队列锁
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于错误多次尝试失败,释放队列锁: {$queueLockKey}");
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning("微信好友自动分配任务执行失败,重试次数: " . $job->attempts());
|
||||
$job->release(10); // 延迟10秒后重试
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录错误并释放队列锁
|
||||
Log::error('微信好友自动分配任务异常: ' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(10);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
application/job/AllotRuleListJob.php
Normal file
75
application/job/AllotRuleListJob.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\AllotRuleController;
|
||||
|
||||
class AllotRuleListJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processAllotRuleList($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('分配规则列表任务执行成功');
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('分配规则列表任务执行失败,已超过重试次数');
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('分配规则列表任务执行失败,重试次数:' . $job->attempts());
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('分配规则列表任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理分配规则列表获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processAllotRuleList($data, $attempts)
|
||||
{
|
||||
Log::info('开始获取分配规则列表');
|
||||
|
||||
// 实例化控制器
|
||||
$allotRuleController = new AllotRuleController();
|
||||
|
||||
// 调用分配规则列表获取方法
|
||||
$result = $allotRuleController->getAllRules([], true);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
Log::info('获取分配规则列表成功,共获取 ' . (isset($response['data']) ? count($response['data']) : 0) . ' 条记录');
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取分配规则列表失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
application/job/AutoCreateAllotRulesJob.php
Normal file
75
application/job/AutoCreateAllotRulesJob.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\AllotRuleController;
|
||||
|
||||
class AutoCreateAllotRulesJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processAutoCreateAllotRules($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('自动创建分配规则任务执行成功,时间:' . date('Y-m-d H:i:s'));
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('自动创建分配规则任务执行失败,已超过重试次数');
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('自动创建分配规则任务执行失败,重试次数:' . $job->attempts());
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('自动创建分配规则任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自动创建分配规则
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processAutoCreateAllotRules($data, $attempts)
|
||||
{
|
||||
Log::info('开始执行自动创建分配规则任务,重试次数:' . $attempts);
|
||||
|
||||
// 实例化控制器
|
||||
$allotRuleController = new AllotRuleController();
|
||||
|
||||
// 调用自动创建分配规则方法
|
||||
$result = $allotRuleController->autoCreateAllotRules([], true);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
// 判断是否成功
|
||||
if (isset($response['code']) && $response['code'] == 200) {
|
||||
Log::info('自动创建分配规则成功:' . json_encode($response['data']));
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('自动创建分配规则失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
118
application/job/CallRecordingListJob.php
Normal file
118
application/job/CallRecordingListJob.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\CallRecordingController;
|
||||
|
||||
class CallRecordingListJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processCallRecordingList($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('通话记录列表任务执行成功,页码:' . $data['pageIndex']);
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('通话记录列表任务执行失败,已超过重试次数,页码:' . $data['pageIndex']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('通话记录列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $data['pageIndex']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('通话记录列表任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理通话记录列表获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processCallRecordingList($data, $attempts)
|
||||
{
|
||||
// 获取参数
|
||||
$pageIndex = isset($data['pageIndex']) ? $data['pageIndex'] : 0;
|
||||
$pageSize = isset($data['pageSize']) ? $data['pageSize'] : 100;
|
||||
|
||||
Log::info('开始获取通话记录列表,页码:' . $pageIndex . ',页大小:' . $pageSize);
|
||||
|
||||
// 实例化控制器
|
||||
$callRecordingController = new CallRecordingController();
|
||||
|
||||
// 构建请求参数
|
||||
$params = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize,
|
||||
'keyword' => '',
|
||||
'isCallOut' => '',
|
||||
'secondMin' => 0,
|
||||
'secondMax' => 99999,
|
||||
'departmentIds' => '',
|
||||
'from' => date('Y-m-d 00:00:00', strtotime('-100 days')),
|
||||
'to' => date('Y-m-d 23:59:59'),
|
||||
'departmentId' => ''
|
||||
];
|
||||
|
||||
// 调用通话记录列表获取方法
|
||||
$result = $callRecordingController->getlist($params, true);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && isset($data['results']) && count($data['results']) > 0) {
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
$this->addNextPageToQueue($nextPageIndex, $pageSize);
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取通话记录列表失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下一页任务到队列
|
||||
* @param int $pageIndex 页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function addNextPageToQueue($pageIndex, $pageSize)
|
||||
{
|
||||
$data = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 call_recording_list
|
||||
Queue::push(self::class, $data, 'call_recording_list');
|
||||
}
|
||||
}
|
||||
90
application/job/ContentCollectJob.php
Normal file
90
application/job/ContentCollectJob.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\cunkebao\controller\ContentLibraryController;
|
||||
|
||||
class ContentCollectJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processContentCollect($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
// 去除成功日志,减少日志空间消耗
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('内容采集任务执行失败,已超过重试次数,内容库ID:' . $data['libraryId']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('内容采集任务执行失败,重试次数:' . $job->attempts() . ',内容库ID:' . $data['libraryId']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('内容采集任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理内容采集
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processContentCollect($data, $attempts)
|
||||
{
|
||||
try {
|
||||
$controller = new ContentLibraryController();
|
||||
$result = $controller->collectMoments();
|
||||
$response = json_decode($result, true);
|
||||
|
||||
if ($response['code'] == 200) {
|
||||
// 记录详细的采集结果
|
||||
if (!empty($response['data'])) {
|
||||
foreach ($response['data'] as $result) {
|
||||
if ($result['status'] == 'success') {
|
||||
Log::info(sprintf(
|
||||
'内容库[%s]采集成功: %s',
|
||||
$result['library_name'],
|
||||
$result['message']
|
||||
));
|
||||
} else {
|
||||
Log::warning(sprintf(
|
||||
'内容库[%s]采集失败: %s',
|
||||
$result['library_name'],
|
||||
$result['message']
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Log::error('内容采集失败:' . ($response['msg'] ?? '未知错误'));
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('内容采集处理异常:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
115
application/job/DepartmentListJob.php
Normal file
115
application/job/DepartmentListJob.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\AccountController;
|
||||
|
||||
class DepartmentListJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processDepartmentList($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('部门列表任务执行成功,页码:' . $data['pageIndex']);
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('部门列表任务执行失败,已超过重试次数,页码:' . $data['pageIndex']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('部门列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $data['pageIndex']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('部门列表任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理部门列表获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processDepartmentList($data, $attempts)
|
||||
{
|
||||
// 获取参数
|
||||
$pageIndex = isset($data['pageIndex']) ? $data['pageIndex'] : 0;
|
||||
$pageSize = isset($data['pageSize']) ? $data['pageSize'] : 100;
|
||||
|
||||
Log::info('开始获取部门列表,页码:' . $pageIndex . ',页大小:' . $pageSize);
|
||||
|
||||
// 实例化控制器
|
||||
$accountController = new AccountController();
|
||||
|
||||
// 构建请求参数
|
||||
$params = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 设置请求信息
|
||||
$request = request();
|
||||
$request->withGet($params);
|
||||
|
||||
// 调用公司账号列表获取方法
|
||||
$result = $accountController->getDepartmentList(true);
|
||||
$response = json_decode($result,true);
|
||||
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data['results']) > 0) {
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
$this->addNextPageToQueue($nextPageIndex, $pageSize);
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取部门列表失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下一页任务到队列
|
||||
* @param int $pageIndex 页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function addNextPageToQueue($pageIndex, $pageSize)
|
||||
{
|
||||
$data = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 account_list
|
||||
Queue::push(self::class, $data, 'department_list');
|
||||
}
|
||||
}
|
||||
155
application/job/DeviceListJob.php
Normal file
155
application/job/DeviceListJob.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\command\DeviceListCommand;
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use think\facade\Cache;
|
||||
use app\api\controller\DeviceController;
|
||||
|
||||
class DeviceListJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 获取数据
|
||||
$pageIndex = $data['pageIndex'];
|
||||
$pageSize = $data['pageSize'];
|
||||
$isDel = $data['isDel'];
|
||||
$jobId = isset($data['jobId']) ? $data['jobId'] : '';
|
||||
$cacheKey = isset($data['cacheKey']) ? $data['cacheKey'] : '';
|
||||
$queueLockKey = isset($data['queueLockKey']) ? $data['queueLockKey'] : '';
|
||||
|
||||
// 记录日志
|
||||
Log::info('开始处理设备列表任务: ' . json_encode([
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize,
|
||||
'isDel' => $isDel,
|
||||
'jobId' => $jobId,
|
||||
'cacheKey' => $cacheKey,
|
||||
'queueLockKey' => $queueLockKey
|
||||
]));
|
||||
|
||||
// 如果没有提供缓存键,根据删除状态和任务ID生成一个
|
||||
if (empty($cacheKey)) {
|
||||
$cacheKeyPrefix = "devicePage:" . ($jobId ?: date('YmdHis') . rand(1000, 9999));
|
||||
$cacheKeySuffix = $isDel === '' ? '' : ":{$isDel}";
|
||||
$cacheKey = $cacheKeyPrefix . $cacheKeySuffix;
|
||||
}
|
||||
|
||||
// 如果没有提供队列锁键,生成一个
|
||||
if (empty($queueLockKey)) {
|
||||
$queueLockKey = "queue_lock:device_list:{$isDel}";
|
||||
}
|
||||
|
||||
// 实例化控制器
|
||||
$deviceController = new DeviceController();
|
||||
|
||||
// 设置请求信息
|
||||
$request = request();
|
||||
$request->withGet([
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
]);
|
||||
|
||||
// 调用设备列表获取方法,传入isDel参数
|
||||
$result = $deviceController->getlist(['pageIndex' => $pageIndex, 'pageSize' => $pageSize], true, $isDel);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
$dataCount = count($data['results']);
|
||||
$totalCount = $data['total'];
|
||||
|
||||
Log::info("设备列表获取成功,当前页:{$pageIndex},获取数量:{$dataCount},总数量:{$totalCount}");
|
||||
|
||||
// 计算是否还有下一页
|
||||
$hasNextPage = ($pageIndex + 1) * $pageSize < $totalCount;
|
||||
|
||||
if ($hasNextPage) {
|
||||
// 缓存页码信息,设置有效期1天
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
Cache::set($cacheKey, $nextPageIndex, 600);
|
||||
Log::info("更新缓存页码: {$nextPageIndex}, 缓存键: {$cacheKey}");
|
||||
|
||||
// 添加下一页任务到队列
|
||||
$command = new DeviceListCommand();
|
||||
$command->addToQueue($nextPageIndex, $pageSize, $isDel, $jobId, $cacheKey, $queueLockKey);
|
||||
Log::info("已添加下一页任务到队列: 页码 {$nextPageIndex}");
|
||||
} else {
|
||||
// 处理完所有页面,重置页码并释放队列锁
|
||||
Cache::set($cacheKey, 0, 600);
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("所有设备列表页面处理完毕,重置页码为0,释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
$job->delete();
|
||||
return true;
|
||||
} else {
|
||||
// API调用出错,记录错误并释放队列锁
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error("设备列表获取失败: " . $errorMsg);
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务并释放队列锁
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于错误释放队列锁: {$queueLockKey}");
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('设备列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $pageIndex);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录错误并释放队列锁
|
||||
Log::error('设备列表任务处理异常: ' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取删除状态的文本描述
|
||||
* @param string $isDel 删除状态
|
||||
* @return string 删除状态文本
|
||||
*/
|
||||
protected function getDeleteTypeText($isDel)
|
||||
{
|
||||
switch ($isDel) {
|
||||
case '0':
|
||||
case 0:
|
||||
return '未删除(unDeleted)';
|
||||
case '1':
|
||||
case 1:
|
||||
return '已删除(deleted)';
|
||||
case '2':
|
||||
case 2:
|
||||
return '已停用(deletedAndStop)';
|
||||
default:
|
||||
return '全部';
|
||||
}
|
||||
}
|
||||
}
|
||||
123
application/job/FriendTaskJob.php
Normal file
123
application/job/FriendTaskJob.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use think\facade\Cache;
|
||||
use app\api\controller\FriendTaskController;
|
||||
|
||||
class FriendTaskJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processFriendTask($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('添加好友任务执行成功,页码:' . $data['pageIndex']);
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('添加好友任务执行失败,已超过重试次数,页码:' . $data['pageIndex']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('添加好友任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $data['pageIndex']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('添加好友任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理添加好友任务获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processFriendTask($data, $attempts)
|
||||
{
|
||||
// 获取参数
|
||||
$pageIndex = isset($data['pageIndex']) ? $data['pageIndex'] : 0;
|
||||
$pageSize = isset($data['pageSize']) ? $data['pageSize'] : 100;
|
||||
|
||||
Log::info('开始获取添加好友任务,页码:' . $pageIndex . ',页大小:' . $pageSize);
|
||||
|
||||
// 实例化控制器
|
||||
$friendTaskController = new FriendTaskController();
|
||||
|
||||
// 构建请求参数
|
||||
$params = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 设置请求信息
|
||||
$request = request();
|
||||
$request->withGet($params);
|
||||
|
||||
// 调用添加好友任务获取方法
|
||||
$result = $friendTaskController->getlist($pageIndex, $pageSize, true);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data['results']) > 0 && $pageIndex < 2) {
|
||||
// 更新缓存中的页码,设置10分钟过期
|
||||
Cache::set('friendTaskPage', $pageIndex + 1, 600);
|
||||
Log::info('更新缓存,下一页页码:' . ($pageIndex + 1) . ',缓存时间:10分钟');
|
||||
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
$this->addNextPageToQueue($nextPageIndex, $pageSize);
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
} else {
|
||||
// 没有下一页,重置缓存,设置10分钟过期
|
||||
Cache::set('friendTaskPage', 0, 600);
|
||||
Log::info('获取完成,重置缓存,缓存时间:10分钟');
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取添加好友任务失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下一页任务到队列
|
||||
* @param int $pageIndex 页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function addNextPageToQueue($pageIndex, $pageSize)
|
||||
{
|
||||
$data = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 friend_task
|
||||
Queue::push(self::class, $data, 'friend_task');
|
||||
}
|
||||
}
|
||||
145
application/job/GroupFriendsJob.php
Normal file
145
application/job/GroupFriendsJob.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use think\facade\Cache;
|
||||
use app\api\controller\WechatChatroomController;
|
||||
use app\api\model\WechatChatroomModel;
|
||||
use think\Db;
|
||||
|
||||
class GroupFriendsJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processGroupFriendsList($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('微信群好友列表任务执行成功,页码:' . $data['pageIndex']);
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('微信群好友列表任务执行失败,已超过重试次数,页码:' . $data['pageIndex']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('微信群好友列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $data['pageIndex']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('微信群好友列表任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理微信群好友列表获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processGroupFriendsList($data, $attempts)
|
||||
{
|
||||
// 获取参数
|
||||
$pageIndex = isset($data['pageIndex']) ? $data['pageIndex'] : 0;
|
||||
$pageSize = isset($data['pageSize']) ? $data['pageSize'] : 100;
|
||||
|
||||
Log::info('开始获取微信群好友列表,页码:' . $pageIndex . ',页大小:' . $pageSize);
|
||||
|
||||
try {
|
||||
// 从数据库获取未删除的群聊列表
|
||||
$chatrooms = WechatChatroomModel::where('isDeleted', 0)
|
||||
->page($pageIndex + 1, $pageSize)
|
||||
->order('id', 'desc')
|
||||
->select();
|
||||
|
||||
if (empty($chatrooms)) {
|
||||
Log::info('未找到需要处理的群聊数据,页码:' . $pageIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 实例化控制器
|
||||
$wechatChatroomController = new WechatChatroomController();
|
||||
|
||||
// 遍历每个群聊,获取其成员
|
||||
foreach ($chatrooms as $chatroom) {
|
||||
try {
|
||||
// 调用获取群成员列表方法
|
||||
$result = $wechatChatroomController->listChatroomMember($chatroom['id'],$chatroom['chatroomId'],true);
|
||||
$response = is_string($result) ? json_decode($result, true) : $result;
|
||||
|
||||
// 判断是否成功
|
||||
if (is_array($response) && isset($response['code']) && $response['code'] == 200) {
|
||||
//Log::info('成功获取群 ' . $chatroom['chatroomId'] . ' 的成员列表');
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取群 ' . $chatroom['chatroomId'] . ' 的成员列表失败:' . $errorMsg);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('获取群 ' . $chatroom['chatroomId'] . ' 的成员列表异常:' . $e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Log::info('群成员获取完成,成功:' . $successCount . ',失败:' . $failCount);
|
||||
|
||||
// 计算总数量
|
||||
$totalCount = WechatChatroomModel::where('isDeleted', 0)->count();
|
||||
$processedCount = ($pageIndex + 1) * $pageSize;
|
||||
|
||||
// 判断是否有下一页
|
||||
if ($processedCount < $totalCount) {
|
||||
// 更新缓存中的页码,设置一天过期
|
||||
Cache::set('groupFriendsPage', $pageIndex + 1, 600);
|
||||
//Log::info('更新缓存,下一页页码:' . ($pageIndex + 1) . ',缓存时间:1天');
|
||||
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
$this->addNextPageToQueue($nextPageIndex, $pageSize);
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
} else {
|
||||
// 没有下一页,重置缓存,设置一天过期
|
||||
Cache::set('groupFriendsPage', 0, 600);
|
||||
Log::info('获取完成,重置缓存,缓存时间:1天');
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('获取微信群好友列表处理失败:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下一页任务到队列
|
||||
* @param int $pageIndex 页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function addNextPageToQueue($pageIndex, $pageSize)
|
||||
{
|
||||
$data = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 group_friends
|
||||
Queue::push(self::class, $data, 'group_friends');
|
||||
}
|
||||
}
|
||||
115
application/job/MessageChatroomListJob.php
Normal file
115
application/job/MessageChatroomListJob.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\MessageController;
|
||||
|
||||
class MessageChatroomListJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processMessageChatroomList($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('微信群聊消息列表任务执行成功,页码:' . $data['pageIndex']);
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('微信群聊消息列表任务执行失败,已超过重试次数,页码:' . $data['pageIndex']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('微信群聊消息列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $data['pageIndex']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('微信群聊消息列表任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理微信群聊消息列表获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processMessageChatroomList($data, $attempts)
|
||||
{
|
||||
// 获取参数
|
||||
$pageIndex = isset($data['pageIndex']) ? $data['pageIndex'] : 0;
|
||||
$pageSize = isset($data['pageSize']) ? $data['pageSize'] : 100;
|
||||
|
||||
Log::info('开始获取微信群聊消息列表,页码:' . $pageIndex . ',页大小:' . $pageSize);
|
||||
|
||||
// 实例化控制器
|
||||
$messageController = new MessageController();
|
||||
|
||||
// 构建请求参数
|
||||
$params = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 设置请求信息
|
||||
$request = request();
|
||||
$request->withGet($params);
|
||||
|
||||
// 调用添加好友任务获取方法
|
||||
$result = $messageController->getChatroomList($pageIndex,$pageSize,true);
|
||||
$response = json_decode($result,true);
|
||||
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data['results']) > 0) {
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
$this->addNextPageToQueue($nextPageIndex, $pageSize);
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取微信群聊消息列表失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下一页任务到队列
|
||||
* @param int $pageIndex 页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function addNextPageToQueue($pageIndex, $pageSize)
|
||||
{
|
||||
$data = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 message_chatroom_list
|
||||
Queue::push(self::class, $data, 'message_chatroom_list');
|
||||
}
|
||||
}
|
||||
117
application/job/MessageFriendsListJob.php
Normal file
117
application/job/MessageFriendsListJob.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\MessageController;
|
||||
|
||||
class MessageFriendsListJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processMessageFriendsList($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
// 去除成功日志,减少日志空间消耗
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('好友消息列表任务执行失败,已超过重试次数,页码:' . $data['pageIndex']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('好友消息列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $data['pageIndex']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('好友消息列表任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理好友消息列表获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processMessageFriendsList($data, $attempts)
|
||||
{
|
||||
// 获取参数
|
||||
$pageIndex = isset($data['pageIndex']) ? $data['pageIndex'] : 0;
|
||||
$pageSize = isset($data['pageSize']) ? $data['pageSize'] : 100;
|
||||
|
||||
// 去除开始日志,减少日志空间消耗
|
||||
|
||||
// 实例化控制器
|
||||
$messageController = new MessageController();
|
||||
|
||||
// 构建请求参数
|
||||
$params = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 设置请求信息
|
||||
$request = request();
|
||||
$request->withGet($params);
|
||||
|
||||
|
||||
|
||||
// 调用添加好友任务获取方法
|
||||
$result = $messageController->getFriendsList($pageIndex,$pageSize,true);
|
||||
$response = json_decode($result,true);
|
||||
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data) > 0) {
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
$this->addNextPageToQueue($nextPageIndex, $pageSize);
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取好友消息列表失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下一页任务到队列
|
||||
* @param int $pageIndex 页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function addNextPageToQueue($pageIndex, $pageSize)
|
||||
{
|
||||
$data = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 message_friends_list
|
||||
Queue::push(self::class, $data, 'message_friends_list');
|
||||
}
|
||||
}
|
||||
152
application/job/OwnMomentsCollectJob.php
Normal file
152
application/job/OwnMomentsCollectJob.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use think\Db;
|
||||
use think\facade\Env;
|
||||
use app\api\controller\WebSocketController;
|
||||
|
||||
class OwnMomentsCollectJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processOwnMomentsCollect($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('自己朋友圈采集任务执行失败,已超过重试次数');
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('自己朋友圈采集任务执行失败,重试次数:' . $job->attempts());
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('自己朋友圈采集任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自己朋友圈采集
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processOwnMomentsCollect($data, $attempts)
|
||||
{
|
||||
try {
|
||||
// 获取在线微信账号列表(只获取在线微信)
|
||||
$onlineWechatAccounts = $this->getOnlineWechatAccounts();
|
||||
|
||||
if (empty($onlineWechatAccounts)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取API账号配置
|
||||
$username = Env::get('api.username2', '');
|
||||
$password = Env::get('api.password2', '');
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
Log::error('API账号配置缺失,无法执行朋友圈采集');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取账号ID
|
||||
$toAccountId = Db::name('users')->where('account', $username)->value('s2_accountId');
|
||||
if (empty($toAccountId)) {
|
||||
Log::error('未找到API账号对应的账号ID');
|
||||
return false;
|
||||
}
|
||||
|
||||
$successCount = 0;
|
||||
$failCount = 0;
|
||||
|
||||
// 遍历每个在线微信账号,采集自己的朋友圈
|
||||
foreach ($onlineWechatAccounts as $account) {
|
||||
try {
|
||||
$wechatAccountId = $account['id'];
|
||||
$wechatId = $account['wechatId'];
|
||||
|
||||
// 创建WebSocket控制器实例
|
||||
$webSocket = new WebSocketController([
|
||||
'userName' => $username,
|
||||
'password' => $password,
|
||||
'accountId' => $toAccountId
|
||||
]);
|
||||
|
||||
// 采集自己的朋友圈(wechatFriendId传0或空,表示采集自己的朋友圈)
|
||||
$result = $webSocket->getMoments([
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'wechatFriendId' => 0, // 0表示采集自己的朋友圈
|
||||
'count' => 10 // 每次采集10条
|
||||
]);
|
||||
|
||||
$resultData = json_decode($result, true);
|
||||
if (!empty($resultData) && $resultData['code'] == 200) {
|
||||
$successCount++;
|
||||
} else {
|
||||
$failCount++;
|
||||
Log::warning("微信账号 {$wechatId} 朋友圈采集失败:" . ($resultData['msg'] ?? '未知错误'));
|
||||
}
|
||||
|
||||
// 避免请求过于频繁,每个账号之间稍作延迟
|
||||
usleep(500000); // 延迟0.5秒
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$failCount++;
|
||||
Log::error("采集微信账号 {$account['wechatId']} 朋友圈异常:" . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Log::info("自己朋友圈采集任务完成,成功:{$successCount},失败:{$failCount}");
|
||||
return true;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('自己朋友圈采集处理异常:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取在线微信账号列表
|
||||
* @return array
|
||||
*/
|
||||
protected function getOnlineWechatAccounts()
|
||||
{
|
||||
try {
|
||||
// 查询在线微信账号(deviceAlive=1 且 wechatAlive=1)
|
||||
$accounts = Db::table('s2_wechat_account')
|
||||
->where('deviceAlive', 1)
|
||||
->where('wechatAlive', 1)
|
||||
->field('id, wechatId, nickname, alias')
|
||||
->select();
|
||||
|
||||
return $accounts ?: [];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('获取在线微信账号列表失败:' . $e->getMessage());
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
70
application/job/SyncAllFriendsJob.php
Normal file
70
application/job/SyncAllFriendsJob.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
use think\Db;
|
||||
|
||||
class SyncAllFriendsJob
|
||||
{
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
$wechatId = 'Lytiao1';
|
||||
$pageIndex = $data['pageIndex'];
|
||||
$pageSize = $data['pageSize'];
|
||||
$preFriendId = isset($data['preFriendId']) ? $data['preFriendId'] : '';
|
||||
$queueLockKey = $data['queueLockKey'];
|
||||
$jobId = $data['jobId'];
|
||||
|
||||
$controller = new \app\api\controller\WechatFriendController();
|
||||
Log::info('开始同步微信id: ' . $wechatId . ',第' . $pageIndex . '页,preFriendId: ' . $preFriendId);
|
||||
$result = $controller->getlist([
|
||||
'wechatAccountKeyword' => $wechatId,
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize,
|
||||
'preFriendId' => $preFriendId
|
||||
], true);
|
||||
|
||||
if (is_string($result)) {
|
||||
$result = json_decode($result, true);
|
||||
}
|
||||
|
||||
if ($result['code'] == 200) {
|
||||
$friends = $result['data']['list'] ?? $result['data'];
|
||||
if (is_array($friends) && count($friends) == $pageSize) {
|
||||
$lastFriendId = $friends[count($friends) - 1]['id'];
|
||||
// 还有下一页,重新入队
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
\think\Queue::push(self::class, [
|
||||
'wechatId' => $wechatId,
|
||||
'pageIndex' => $nextPageIndex,
|
||||
'pageSize' => $pageSize,
|
||||
'preFriendId' => $lastFriendId,
|
||||
'jobId' => $jobId,
|
||||
'queueLockKey' => $queueLockKey
|
||||
], $job->getQueue());
|
||||
Log::info("微信id: {$wechatId} 下一页任务已入队,pageIndex: {$nextPageIndex},preFriendId: {$lastFriendId}");
|
||||
}
|
||||
}
|
||||
|
||||
$job->delete();
|
||||
Log::info('同步微信id: ' . $wechatId . ' 第' . $pageIndex . '页任务执行成功');
|
||||
// 释放锁逻辑可在所有账号所有分页都完成后处理
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('同步所有好友任务异常:' . $e->getMessage());
|
||||
if (!empty($data['queueLockKey'])) {
|
||||
\think\facade\Cache::rm($data['queueLockKey']);
|
||||
}
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(\think\facade\Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
application/job/SyncContentJob.php
Normal file
66
application/job/SyncContentJob.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace app\job;
|
||||
|
||||
use app\chukebao\model\Reply;
|
||||
use app\chukebao\model\ReplyGroup;
|
||||
use think\Db;
|
||||
use think\queue\Job;
|
||||
|
||||
class SyncContentJob
|
||||
{
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
|
||||
$ddd= Db::table('s2_wechat_friend')->where('ownerWechatId','wxid_h7nsh7vxseyn29')->select();
|
||||
foreach ($ddd as $v) {
|
||||
$d = Db::table('ck_task_customer')->where('task_id','167')->where('phone',$v['wechatId'])->find();
|
||||
if (!empty($d) && !in_array($d['status'],[4,5])) {
|
||||
Db::table('ck_task_customer')->where('id',$d['id'])->update(['status'=>5]);
|
||||
}
|
||||
}
|
||||
|
||||
exit_data(111);
|
||||
|
||||
return true;
|
||||
/*$ddd= '[{"id":21909,"groupName":"私域运营招聘","sortIndex":240426546,"parentId":0,"replyType":1,"children":[],"replys":null},{"id":8074,"groupName":"存客宝新客户介绍","sortIndex":230,"parentId":0,"replyType":1,"children":[{"id":8081,"groupName":"客户系统上线准备","sortIndex":1,"parentId":8074,"replyType":1,"children":[],"replys":null}],"replys":null},{"id":10441,"groupName":"BOSS直聘","sortIndex":229,"parentId":0,"replyType":1,"children":[],"replys":null},{"id":15111,"groupName":"点了码新客户了解","sortIndex":228,"parentId":0,"replyType":1,"children":[],"replys":null},{"id":12732,"groupName":"测试","sortIndex":219,"parentId":0,"replyType":1,"children":[],"replys":null},{"id":8814,"groupName":"封单话术","sortIndex":176,"parentId":0,"replyType":1,"children":[],"replys":null},{"id":8216,"groupName":"私域合伙人","sortIndex":172,"parentId":0,"replyType":1,"children":[],"replys":null},{"id":6476,"groupName":"新客户了解","sortIndex":119,"parentId":0,"replyType":1,"children":[],"replys":null}]';
|
||||
$ddd=json_decode($ddd,1);*/
|
||||
|
||||
$ddd = ReplyGroup::where('companyId',2778)->where('companyId','<>',21898)->where('isDel',0)->select()->toArray();
|
||||
|
||||
|
||||
$authorization = 'OE7kh6Dsw_0SqqH1FTAPCB2ewCQDhx7VvPw6PrsE_p9tcRKbtlFsZau8kjk2NQ829Yah90KhTh0C_35ek569uRQgM_gC0NtKzfRPDDoqMIUE5mI6AO_hm0dm-xDJqhAFYkXHCdXnJYzQZxWS5dleJCIwtQxgRuIzIbr-_G_5C-7DeLEOSt2vi1oGPleLt00QGQ1WYVYqoHYrbPGMghMQpWIbgk5qNcUCeANlLJ_s7QFC3QzArU95_YiK0HlhU81hZqr8kI_5lmdrRBoR-yNIlyhySLRCmEZYGzOxCiUHL3uFHYZA1VnLBAVbryNj5DElZjMgwA';
|
||||
// 设置请求头
|
||||
$headerData = ['client:system'];
|
||||
$header = setHeader($headerData, $authorization, 'json');
|
||||
|
||||
|
||||
|
||||
foreach ($ddd as $key => $value) {
|
||||
$data = [];
|
||||
// 发送请求获取公司账号列表
|
||||
$result = requestCurl('https://s2.siyuguanli.com:9991/api/Reply/listReply?groupId='.$value['id'], '', 'GET', $header,'json');
|
||||
$response = handleApiResponse($result);
|
||||
foreach ($response as $k => $v) {
|
||||
$data[] = [
|
||||
'groupId' => $v['groupId'],
|
||||
'userId' => $value['userId'],
|
||||
'title' => $v['title'],
|
||||
'msgType' => $v['msgType'],
|
||||
'content' => $v['content'],
|
||||
'createTime' => strtotime($v['createTime']),
|
||||
'lastUpdateTime' => strtotime($v['lastUpdateTime']),
|
||||
'sortIndex' => 50
|
||||
];
|
||||
}
|
||||
$Reply = new Reply();
|
||||
$Reply->insertAll($data);
|
||||
}
|
||||
|
||||
|
||||
exit_data(11111);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
101
application/job/WechatChatroomJob.php
Normal file
101
application/job/WechatChatroomJob.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\command\WechatChatroomCommand;
|
||||
use think\facade\Log;
|
||||
use think\facade\Cache;
|
||||
use think\Queue;
|
||||
use app\api\controller\WechatChatroomController;
|
||||
|
||||
class WechatChatroomJob
|
||||
{
|
||||
/**
|
||||
* 队列执行方法
|
||||
* @param $data 数据
|
||||
* @return array|bool
|
||||
*/
|
||||
public function fire($job, $data)
|
||||
{
|
||||
try {
|
||||
// 获取数据
|
||||
$pageIndex = $data['pageIndex'];
|
||||
$pageSize = $data['pageSize'];
|
||||
$isDel = $data['isDel'];
|
||||
$jobId = isset($data['jobId']) ? $data['jobId'] : '';
|
||||
$cacheKey = isset($data['cacheKey']) ? $data['cacheKey'] : '';
|
||||
$queueLockKey = isset($data['queueLockKey']) ? $data['queueLockKey'] : '';
|
||||
|
||||
// 记录日志
|
||||
Log::info('开始处理微信聊天室列表任务: ' . json_encode([
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize,
|
||||
'isDel' => $isDel,
|
||||
'jobId' => $jobId,
|
||||
'cacheKey' => $cacheKey,
|
||||
'queueLockKey' => $queueLockKey
|
||||
]));
|
||||
|
||||
// 如果没有提供缓存键,根据删除状态和任务ID生成一个
|
||||
if (empty($cacheKey)) {
|
||||
$cacheKeyPrefix = "chatroomPage:" . ($jobId ?: date('YmdHis') . rand(1000, 9999));
|
||||
$cacheKeySuffix = $isDel === '' ? '' : ":{$isDel}";
|
||||
$cacheKey = $cacheKeyPrefix . $cacheKeySuffix;
|
||||
}
|
||||
|
||||
// 如果没有提供队列锁键,生成一个
|
||||
if (empty($queueLockKey)) {
|
||||
$queueLockKey = "queue_lock:wechat_chatroom:{$isDel}";
|
||||
}
|
||||
|
||||
// 调用业务逻辑获取微信聊天室列表
|
||||
$logic = new WechatChatroomController();
|
||||
$result = $logic->getlist(['pageIndex' => $pageIndex, 'pageSize' => $pageSize],true, $isDel);
|
||||
$response = json_decode($result, true);
|
||||
$data = $response['data'];
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data['results']) > 0 && empty($response['isUpdate'])) {
|
||||
$dataCount = count($data['results']);
|
||||
$totalCount = $data['total'];
|
||||
|
||||
// 计算是否还有下一页
|
||||
$hasNextPage = ($pageIndex + 1) * $pageSize < $totalCount;
|
||||
|
||||
if ($hasNextPage) {
|
||||
// 缓存页码信息,设置有效期1天
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
Cache::set($cacheKey, $nextPageIndex, 600);
|
||||
Log::info("更新缓存页码: {$nextPageIndex}, 缓存键: {$cacheKey}");
|
||||
|
||||
// 添加下一页任务到队列
|
||||
$command = new WechatChatroomCommand();
|
||||
$command->addToQueue($nextPageIndex, $pageSize, $isDel, $jobId, $cacheKey, $queueLockKey);
|
||||
Log::info("已添加下一页任务到队列: 页码 {$nextPageIndex}");
|
||||
} else {
|
||||
// 处理完所有页面,重置页码并释放队列锁
|
||||
Cache::set($cacheKey, 0, 600);
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("所有微信聊天室列表页面处理完毕,重置页码为0,释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
} else {
|
||||
// API调用出错,记录错误并释放队列锁
|
||||
Log::error("微信聊天室列表获取失败: " . $response['msg']);
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于错误释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
$job->delete();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录错误并释放队列锁
|
||||
Log::error('微信聊天室列表任务处理异常: ' . $e->getMessage());
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
$job->delete();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
169
application/job/WechatFriendJob.php
Normal file
169
application/job/WechatFriendJob.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\command\WechatFriendCommand;
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use think\facade\Cache;
|
||||
use app\api\controller\WechatFriendController;
|
||||
|
||||
class WechatFriendJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 获取数据
|
||||
$pageIndex = $data['pageIndex'];
|
||||
$pageSize = $data['pageSize'];
|
||||
$preFriendId = $data['preFriendId'];
|
||||
$isDel = $data['isDel'];
|
||||
$jobId = isset($data['jobId']) ? $data['jobId'] : '';
|
||||
$pageIndexCacheKey = isset($data['pageIndexCacheKey']) ? $data['pageIndexCacheKey'] : '';
|
||||
$preFriendIdCacheKey = isset($data['preFriendIdCacheKey']) ? $data['preFriendIdCacheKey'] : '';
|
||||
$queueLockKey = isset($data['queueLockKey']) ? $data['queueLockKey'] : '';
|
||||
|
||||
// 记录日志
|
||||
Log::info('开始处理微信好友列表任务: ' . json_encode([
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize,
|
||||
'preFriendId' => $preFriendId,
|
||||
'isDel' => $isDel,
|
||||
'jobId' => $jobId,
|
||||
'pageIndexCacheKey' => $pageIndexCacheKey,
|
||||
'preFriendIdCacheKey' => $preFriendIdCacheKey,
|
||||
'queueLockKey' => $queueLockKey
|
||||
]));
|
||||
|
||||
// 如果没有提供缓存键,根据删除状态和任务ID生成
|
||||
if (empty($pageIndexCacheKey)) {
|
||||
$cacheKeyPrefix = "friendsPage:" . ($jobId ?: date('YmdHis') . rand(1000, 9999));
|
||||
$cacheKeySuffix = $isDel === '' ? '' : ":{$isDel}";
|
||||
$pageIndexCacheKey = $cacheKeyPrefix . $cacheKeySuffix;
|
||||
}
|
||||
|
||||
if (empty($preFriendIdCacheKey)) {
|
||||
$cacheKeyPrefix = "preFriendId:" . ($jobId ?: date('YmdHis') . rand(1000, 9999));
|
||||
$cacheKeySuffix = $isDel === '' ? '' : ":{$isDel}";
|
||||
$preFriendIdCacheKey = $cacheKeyPrefix . $cacheKeySuffix;
|
||||
}
|
||||
|
||||
// 如果没有提供队列锁键,生成一个
|
||||
if (empty($queueLockKey)) {
|
||||
$queueLockKey = "queue_lock:wechat_friends:{$isDel}";
|
||||
}
|
||||
|
||||
// 实例化控制器
|
||||
$wechatFriendController = new WechatFriendController();
|
||||
|
||||
// 设置请求信息
|
||||
$request = request();
|
||||
$request->withGet([
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize,
|
||||
'preFriendId' => $preFriendId
|
||||
]);
|
||||
|
||||
// 调用微信好友列表获取方法,传入isDel参数
|
||||
$result = $wechatFriendController->getlist([
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize,
|
||||
'preFriendId' => $preFriendId,
|
||||
], true, $isDel);
|
||||
$response = json_decode($result, true);
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data) > 0 && empty($response['isUpdate'])) {
|
||||
// 获取最后一条记录的ID
|
||||
$lastFriendId = $data[count($data)-1]['id'];
|
||||
|
||||
// 更新缓存中的页码和最后一个好友ID,设置1天过期
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
Cache::set($pageIndexCacheKey, $nextPageIndex, 600);
|
||||
Cache::set($preFriendIdCacheKey, $lastFriendId, 600);
|
||||
|
||||
Log::info("更新缓存,下一页页码:{$nextPageIndex},最后好友ID:{$lastFriendId},缓存键: {$pageIndexCacheKey}, {$preFriendIdCacheKey}");
|
||||
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$command = new WechatFriendCommand();
|
||||
$command->addToQueue($nextPageIndex, $pageSize, $lastFriendId, $isDel, $jobId, $pageIndexCacheKey, $preFriendIdCacheKey, $queueLockKey);
|
||||
Log::info("已添加下一页任务到队列: 页码 {$nextPageIndex}");
|
||||
} else {
|
||||
// 没有下一页,重置缓存并释放队列锁
|
||||
Cache::set($pageIndexCacheKey, 0, 600);
|
||||
Cache::set($preFriendIdCacheKey, '', 600);
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("所有微信好友列表页面处理完毕,重置页码为0,释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
$job->delete();
|
||||
Log::info('微信好友列表任务执行成功,页码:' . $pageIndex . ',删除状态:' . $this->getDeleteStatusText($isDel));
|
||||
return true;
|
||||
} else {
|
||||
// API调用出错,记录错误
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取微信好友列表失败:' . $errorMsg);
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务并释放队列锁
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于错误释放队列锁: {$queueLockKey}");
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('微信好友列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $pageIndex);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录错误并释放队列锁
|
||||
Log::error('微信好友列表任务异常:' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取删除状态的文本描述
|
||||
* @param string $isDel 删除状态
|
||||
* @return string 状态文本描述
|
||||
*/
|
||||
protected function getDeleteStatusText($isDel)
|
||||
{
|
||||
switch ($isDel) {
|
||||
case '0':
|
||||
case 0:
|
||||
return '未删除(false)';
|
||||
case '1':
|
||||
case 1:
|
||||
return '已删除(true)';
|
||||
default:
|
||||
return '全部';
|
||||
}
|
||||
}
|
||||
}
|
||||
117
application/job/WechatListJob.php
Normal file
117
application/job/WechatListJob.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\WechatController;
|
||||
|
||||
class WechatListJob
|
||||
{
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
try {
|
||||
// 如果任务执行成功后删除任务
|
||||
if ($this->processWechatList($data, $job->attempts())) {
|
||||
$job->delete();
|
||||
Log::info('微信客服列表任务执行成功,页码:' . $data['pageIndex']);
|
||||
} else {
|
||||
if ($job->attempts() > 3) {
|
||||
// 超过重试次数,删除任务
|
||||
Log::error('微信客服列表任务执行失败,已超过重试次数,页码:' . $data['pageIndex']);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 任务失败,重新放回队列
|
||||
Log::warning('微信客服列表任务执行失败,重试次数:' . $job->attempts() . ',页码:' . $data['pageIndex']);
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 出现异常,记录日志
|
||||
Log::error('微信客服列表任务异常:' . $e->getMessage());
|
||||
if ($job->attempts() > 3) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理微信客服列表获取
|
||||
* @param array $data 任务数据
|
||||
* @param int $attempts 重试次数
|
||||
* @return bool
|
||||
*/
|
||||
protected function processWechatList($data, $attempts)
|
||||
{
|
||||
// 获取参数
|
||||
$pageIndex = isset($data['pageIndex']) ? $data['pageIndex'] : 0;
|
||||
$pageSize = isset($data['pageSize']) ? $data['pageSize'] : 1000;
|
||||
|
||||
Log::info('开始获取微信客服列表,页码:' . $pageIndex . ',页大小:' . $pageSize);
|
||||
|
||||
// 实例化控制器
|
||||
$wechatController = new WechatController();
|
||||
|
||||
// 构建请求参数
|
||||
$params = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 设置请求信息
|
||||
$request = request();
|
||||
$request->withGet($params);
|
||||
|
||||
|
||||
// 调用设备列表获取方法
|
||||
$result = $wechatController->getlist($pageIndex,$pageSize,true);
|
||||
$response = json_decode($result,true);
|
||||
|
||||
|
||||
|
||||
// 判断是否成功
|
||||
if ($response['code'] == 200) {
|
||||
$data = $response['data'];
|
||||
|
||||
// 判断是否有下一页
|
||||
if (!empty($data) && count($data['results']) > 0) {
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$nextPageIndex = $pageIndex + 1;
|
||||
$this->addNextPageToQueue($nextPageIndex, $pageSize);
|
||||
Log::info('添加下一页任务到队列,页码:' . $nextPageIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$errorMsg = isset($response['msg']) ? $response['msg'] : '未知错误';
|
||||
Log::error('获取微信客服列表失败:' . $errorMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下一页任务到队列
|
||||
* @param int $pageIndex 页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function addNextPageToQueue($pageIndex, $pageSize)
|
||||
{
|
||||
$data = [
|
||||
'pageIndex' => $pageIndex,
|
||||
'pageSize' => $pageSize
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 wechat_list
|
||||
Queue::push(self::class, $data, 'wechat_list');
|
||||
}
|
||||
}
|
||||
131
application/job/WechatMomentsJob.php
Normal file
131
application/job/WechatMomentsJob.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\facade\Cache;
|
||||
use think\Db;
|
||||
use app\command\WechatMomentsCommand;
|
||||
use app\api\controller\WebSocketController;
|
||||
use think\facade\Env;
|
||||
use app\api\controller\AutomaticAssign;
|
||||
|
||||
class WechatMomentsJob
|
||||
{
|
||||
protected $maxPages = 10; // 最大页数
|
||||
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$toAccountId = '';
|
||||
$username = Env::get('api.username2', '');
|
||||
$password = Env::get('api.password2', '');
|
||||
if (!empty($username) || !empty($password)) {
|
||||
$toAccountId = Db::name('users')->where('account',$username)->value('s2_accountId');
|
||||
}else{
|
||||
Log::error("没有账号配置");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$jobId = $data['jobId'] ?? '';
|
||||
$queueLockKey = $data['queueLockKey'] ?? '';
|
||||
Log::info("开始处理朋友圈采集任务,任务ID:{$jobId}");
|
||||
|
||||
// 获取好友列表
|
||||
$friends = $this->getFriends($data['pageIndex'], $data['pageSize']);
|
||||
if (empty($friends)) {
|
||||
Log::info("没有更多好友数据,任务完成");
|
||||
Cache::rm($queueLockKey);
|
||||
$job->delete();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($friends as $friend) {
|
||||
try {
|
||||
// 执行切换好友命令
|
||||
$automaticAssign = new AutomaticAssign();
|
||||
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $toAccountId], true);
|
||||
//存入缓存
|
||||
artificialAllotWechatFriend($friend);
|
||||
|
||||
// 执行采集朋友圈命令
|
||||
$webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]);
|
||||
$webSocket->getMoments(['wechatFriendId' => $friend['friendId'], 'wechatAccountId' => $friend['wechatAccountId']]);
|
||||
|
||||
// 处理完毕切换回原账号
|
||||
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true);
|
||||
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// 发生异常时也要切换回原账号
|
||||
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true);
|
||||
Log::error("采集好友 {$friend['id']} 的朋友圈失败:" . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否需要继续翻页
|
||||
if (count($friends) < $data['pageSize']) {
|
||||
// 如果返回的数据少于页面大小,说明已经没有更多数据了
|
||||
Log::info("朋友圈采集任务完成,没有更多数据");
|
||||
Cache::rm($queueLockKey);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 还有更多数据,继续处理下一页
|
||||
$data['pageIndex']++;
|
||||
if ($data['pageIndex'] > $this->maxPages) {
|
||||
Log::info("已达到最大页数限制 {$this->maxPages},任务完成");
|
||||
Cache::rm($data['pageIndexCacheKey']);
|
||||
Cache::rm($queueLockKey);
|
||||
$job->delete();
|
||||
} else {
|
||||
// 处理下一页
|
||||
Cache::set($data['pageIndexCacheKey'], $data['pageIndex']);
|
||||
|
||||
// 有下一页,将下一页任务添加到队列
|
||||
$command = new WechatMomentsCommand();
|
||||
$command->addToQueue($data['pageIndex'], $data['pageSize'], $jobId, $queueLockKey);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true);
|
||||
Log::error("朋友圈采集任务异常:" . $e->getMessage());
|
||||
Cache::rm($queueLockKey);
|
||||
$job->delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取账号的好友列表
|
||||
* @param int $accountId 账号ID
|
||||
* @return array
|
||||
*/
|
||||
private function getFriends($page = 1 ,$pageSize = 100)
|
||||
{
|
||||
$list = Db::table('s2_company_account')
|
||||
->alias('ca')
|
||||
->join(['s2_wechat_account' => 'wa'], 'ca.id = wa.deviceAccountId')
|
||||
->join(['s2_wechat_friend' => 'wf'], 'ca.id = wf.accountId AND wf.wechatAccountId = wa.id')
|
||||
->where([
|
||||
'ca.status' => 0,
|
||||
'wf.isDeleted' => 0,
|
||||
'wa.deviceAlive' => 1,
|
||||
'wa.wechatAlive' => 1
|
||||
])
|
||||
->field([
|
||||
'ca.id as accountId',
|
||||
'ca.userName',
|
||||
'wf.id as friendId',
|
||||
'wf.wechatId',
|
||||
'wf.wechatAccountId',
|
||||
'wa.wechatId as wechatAccountWechatId',
|
||||
'wa.currentDeviceId as deviceId'
|
||||
])->group('wf.wechatId')
|
||||
->order('wf.id DESC')
|
||||
->page($page, $pageSize)
|
||||
->select();
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
483
application/job/WorkbenchAutoLikeJob.php
Normal file
483
application/job/WorkbenchAutoLikeJob.php
Normal file
@@ -0,0 +1,483 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Env;
|
||||
use app\cunkebao\model\Workbench;
|
||||
use app\cunkebao\model\WorkbenchAutoLike;
|
||||
use think\Db;
|
||||
use app\api\controller\WebSocketController;
|
||||
use app\api\controller\AutomaticAssign;
|
||||
use app\api\controller\WechatFriendController;
|
||||
|
||||
class WorkbenchAutoLikeJob
|
||||
{
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$jobId = $data['jobId'] ?? '';
|
||||
$queueLockKey = $data['queueLockKey'] ?? '';
|
||||
try {
|
||||
$this->logJobStart($jobId, $queueLockKey);
|
||||
$workbenches = $this->getActiveWorkbenches();
|
||||
if (empty($workbenches)) {
|
||||
$this->handleEmptyWorkbenches($job, $queueLockKey);
|
||||
return true;
|
||||
}
|
||||
$this->processWorkbenches($workbenches);
|
||||
$this->handleJobSuccess($job, $queueLockKey);
|
||||
return true;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleJobError($e, $job, $queueLockKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃的工作台
|
||||
* @return \think\Collection
|
||||
*/
|
||||
protected function getActiveWorkbenches()
|
||||
{
|
||||
return Workbench::where([
|
||||
['status', '=', 1],
|
||||
['isDel', '=', 0],
|
||||
['type', '=', 1] // 只获取自动点赞类型的工作台
|
||||
])->order('id DESC')->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理工作台列表
|
||||
* @param \think\Collection $workbenches
|
||||
*/
|
||||
protected function processWorkbenches($workbenches)
|
||||
{
|
||||
foreach ($workbenches as $workbench) {
|
||||
try {
|
||||
$this->processSingleWorkbench($workbench);
|
||||
} catch (\Exception $e) {
|
||||
Log::error("处理工作台 {$workbench->id} 失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个工作台
|
||||
* @param Workbench $workbench
|
||||
*/
|
||||
protected function processSingleWorkbench($workbench)
|
||||
{
|
||||
$config = WorkbenchAutoLike::where('workbenchId', $workbench->id)->find();
|
||||
if (!$config) {
|
||||
Log::error("工作台 {$workbench->id} 配置获取失败");
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handleAutoLike($workbench, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自动点赞任务
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
*/
|
||||
protected function handleAutoLike($workbench, $config)
|
||||
{
|
||||
if (!$this->validateAutoLikeConfig($workbench, $config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证是否在点赞时间范围内
|
||||
if (!$this->isWithinLikeTimeRange($config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理分页获取好友列表
|
||||
$this->processAllFriends($workbench, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理所有好友分页
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @param int $page 当前页码
|
||||
* @param int $pageSize 每页大小
|
||||
*/
|
||||
protected function processAllFriends($workbench, $config, $page = 1, $pageSize = 100)
|
||||
{
|
||||
$friendList = $this->getFriendList($config, $page, $pageSize);
|
||||
|
||||
if (empty($friendList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接顺序处理所有好友
|
||||
foreach ($friendList as $friend) {
|
||||
// 验证是否达到点赞次数上限
|
||||
$likeCount = $this->getTodayLikeCount($workbench, $config, $friend['deviceId']);
|
||||
if ($likeCount >= $config['maxLikes']) {
|
||||
Log::info("工作台 {$workbench->id} 点赞次数已达上限");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 验证是否达到好友点赞次数上限
|
||||
$friendMaxLikes = Db::name('workbench_auto_like_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('wechatFriendId', $friend['friendId'])
|
||||
->count();
|
||||
|
||||
if ($friendMaxLikes < $config['friendMaxLikes']) {
|
||||
$this->processFriendMoments($workbench, $config, $friend);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前页数据量等于页大小,说明可能还有更多数据,继续处理下一页
|
||||
if (count($friendList) == $pageSize) {
|
||||
$this->processAllFriends($workbench, $config, $page + 1, $pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友列表
|
||||
* @param WorkbenchAutoLike $config 配置
|
||||
* @param int $page 页码
|
||||
* @param int $pageSize 每页大小
|
||||
* @return array
|
||||
*/
|
||||
protected function getFriendList($config, $page = 1, $pageSize = 100)
|
||||
{
|
||||
$friends = json_decode($config['friends'], true);
|
||||
$devices = json_decode($config['devices'], true);
|
||||
|
||||
$list = Db::table('s2_company_account')
|
||||
->alias('ca')
|
||||
->join(['s2_wechat_account' => 'wa'], 'ca.id = wa.deviceAccountId')
|
||||
->join(['s2_wechat_friend' => 'wf'], 'wf.wechatAccountId = wa.id')
|
||||
->join('workbench_auto_like_item wali', 'wali.wechatFriendId = wf.id AND wali.workbenchId = ' . $config['workbenchId'], 'left')
|
||||
->where([
|
||||
'ca.status' => 0,
|
||||
'wf.isDeleted' => 0,
|
||||
'wa.deviceAlive' => 1,
|
||||
'wa.wechatAlive' => 1
|
||||
])
|
||||
->whereIn('wa.currentDeviceId', $devices)
|
||||
->field([
|
||||
'ca.id as accountId',
|
||||
'ca.userName',
|
||||
'wf.id as friendId',
|
||||
'wf.wechatId',
|
||||
'wf.wechatAccountId',
|
||||
'wa.wechatId as wechatAccountWechatId',
|
||||
'wa.currentDeviceId as deviceId',
|
||||
'COUNT(wali.id) as like_count'
|
||||
]);
|
||||
|
||||
if (!empty($friends) && is_array($friends) && count($friends) > 0) {
|
||||
$list = $list->whereIn('wf.id', $friends);
|
||||
}
|
||||
|
||||
$list = $list->group('wf.wechatId')
|
||||
->having('like_count < ' . $config['friendMaxLikes'])
|
||||
->order('wf.id DESC')
|
||||
->page($page, $pageSize)
|
||||
->select();
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理好友朋友圈
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @param array $friend
|
||||
*/
|
||||
protected function processFriendMoments($workbench, $config, $friend)
|
||||
{
|
||||
$toAccountId = '';
|
||||
$username = Env::get('api.username2', '');
|
||||
$password = Env::get('api.password2', '');
|
||||
if (!empty($username) || !empty($password)) {
|
||||
$toAccountId = Db::name('users')->where('account',$username)->value('s2_accountId');
|
||||
}
|
||||
|
||||
try {
|
||||
// 执行切换好友命令
|
||||
$automaticAssign = new AutomaticAssign();
|
||||
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $toAccountId], true);
|
||||
//存入缓存
|
||||
artificialAllotWechatFriend($friend);
|
||||
// 创建WebSocket链接
|
||||
$webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]);
|
||||
|
||||
// 查询未点赞的朋友圈
|
||||
$moments = $this->getUnlikedMoments($friend['wechatId']);
|
||||
if (empty($moments) || count($moments) == 0) {
|
||||
//采集最新朋友圈
|
||||
$webSocket->getMoments(['wechatFriendId' => $friend['friendId'], 'wechatAccountId' => $friend['wechatAccountId']]);
|
||||
$moments = $this->getUnlikedMoments($friend['wechatId']);
|
||||
}
|
||||
|
||||
|
||||
if (empty($moments) || count($moments) == 0) {
|
||||
// 处理完毕切换回原账号
|
||||
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true);
|
||||
Log::info("好友 {$friend['friendId']} 没有需要点赞的朋友圈");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
foreach ($moments as $moment) {
|
||||
// 点赞朋友圈
|
||||
$this->likeMoment($workbench, $config, $friend, $moment, $webSocket);
|
||||
|
||||
if(!empty($config['enableFriendTags']) && !empty($config['friendTags'])){
|
||||
// 修改好友标签
|
||||
$labels = $this->getFriendLabels($friend);
|
||||
$labels[] = $config['friendTags'];
|
||||
$webSocket->modifyFriendLabel(['wechatFriendId' => $friend['friendId'], 'wechatAccountId' => $friend['wechatAccountId'], 'labels' => $labels]);
|
||||
|
||||
//更新用户标签
|
||||
$friendData = Db::table('s2_wechat_friend')->where('id', $friend['friendId'])->find();
|
||||
Db::table('s2_wechat_friend')->where('id', $friend['friendId'])->update(['labels' => json_encode($labels,256)]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 处理完毕切换回原账号
|
||||
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true);
|
||||
} catch (\Exception $e) {
|
||||
// 异常情况下也要确保切换回原账号
|
||||
$automaticAssign = new AutomaticAssign();
|
||||
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true);
|
||||
Log::error("处理好友 {$friend['friendId']} 朋友圈失败异常: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未点赞的朋友圈
|
||||
* @param int $wechatId
|
||||
* @return \think\Collection
|
||||
*/
|
||||
protected function getUnlikedMoments($wechatId)
|
||||
{
|
||||
return Db::table('s2_wechat_moments')
|
||||
->alias('wm')
|
||||
->join('workbench_auto_like_item wali', 'wali.momentsId = wm.id', 'left')
|
||||
->where([
|
||||
['wm.userName', '=', $wechatId],
|
||||
['wali.id', 'null', null]
|
||||
])
|
||||
->where('wm.update_time', '>=', time() - 86400)
|
||||
->field('wm.id, wm.snsId')
|
||||
->group('wali.wechatFriendId')
|
||||
->order('wm.createTime DESC')
|
||||
->page(1,1)
|
||||
->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞朋友圈
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @param array $friend
|
||||
* @param array $moment
|
||||
* @param WebSocketController $webSocket
|
||||
*/
|
||||
protected function likeMoment($workbench, $config, $friend, $moment, $webSocket)
|
||||
{
|
||||
try {
|
||||
$result = $webSocket->momentInteract([
|
||||
'snsId' => $moment['snsId'],
|
||||
'wechatAccountId' => $friend['wechatAccountId'],
|
||||
]);
|
||||
|
||||
$result = json_decode($result, true);
|
||||
|
||||
if ($result['code'] == 200) {
|
||||
$this->recordLike($workbench, $moment, $friend);
|
||||
|
||||
// 添加间隔时间
|
||||
if (!empty($config['interval'])) {
|
||||
sleep($config['interval']);
|
||||
}
|
||||
} else {
|
||||
Log::error("工作台 {$workbench->id} 点赞失败: " . ($result['msg'] ?? '未知错误'));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("工作台 {$workbench->id} 点赞异常: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录点赞
|
||||
* @param Workbench $workbench
|
||||
* @param array $moment
|
||||
* @param array $friend
|
||||
*/
|
||||
protected function recordLike($workbench, $moment, $friend)
|
||||
{
|
||||
Db::name('workbench_auto_like_item')->insert([
|
||||
'workbenchId' => $workbench->id,
|
||||
'deviceId' => $friend['deviceId'],
|
||||
'momentsId' => $moment['id'],
|
||||
'snsId' => $moment['snsId'],
|
||||
'wechatAccountId' => $friend['wechatAccountId'],
|
||||
'wechatFriendId' => $friend['friendId'],
|
||||
'createTime' => time()
|
||||
]);
|
||||
Log::info("工作台 {$workbench->id} 点赞成功: {$moment['snsId']}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友标签
|
||||
* @param array $friend
|
||||
* @return array
|
||||
*/
|
||||
protected function getFriendLabels($friend)
|
||||
{
|
||||
$wechatFriendController = new WechatFriendController();
|
||||
$result = $wechatFriendController->getlist([
|
||||
'friendKeyword' => $friend['wechatId'],
|
||||
'wechatAccountKeyword' => $friend['wechatAccountWechatId']
|
||||
], true);
|
||||
|
||||
$result = json_decode($result, true);
|
||||
$labels = [];
|
||||
|
||||
if(!empty($result['data'])){
|
||||
foreach($result['data'] as $item){
|
||||
$labels = array_merge($labels, $item['labels']);
|
||||
}
|
||||
}
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证自动点赞配置
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateAutoLikeConfig($workbench, $config)
|
||||
{
|
||||
$requiredFields = ['contentTypes', 'interval', 'maxLikes', 'startTime', 'endTime'];
|
||||
foreach ($requiredFields as $field) {
|
||||
if (empty($config[$field])) {
|
||||
Log::error("工作台 {$workbench->id} 配置字段 {$field} 为空");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今日点赞次数
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @return int
|
||||
*/
|
||||
protected function getTodayLikeCount($workbench, $config, $deviceId)
|
||||
{
|
||||
return Db::name('workbench_auto_like_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('deviceId', $deviceId)
|
||||
->whereTime('createTime', 'between', [
|
||||
strtotime(date('Y-m-d') . ' ' . $config['startTime'] . ':00'),
|
||||
strtotime(date('Y-m-d') . ' ' . $config['endTime'] . ':00')
|
||||
])
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否在点赞时间范围内
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @return bool
|
||||
*/
|
||||
protected function isWithinLikeTimeRange($config)
|
||||
{
|
||||
$currentTime = date('H:i');
|
||||
if ($currentTime < $config['startTime'] || $currentTime > $config['endTime']) {
|
||||
Log::info("当前时间 {$currentTime} 不在点赞时间范围内 ({$config['startTime']} - {$config['endTime']})");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录任务开始
|
||||
* @param string $jobId
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function logJobStart($jobId, $queueLockKey)
|
||||
{
|
||||
Log::info('开始处理工作台自动点赞任务: ' . json_encode([
|
||||
'jobId' => $jobId,
|
||||
'queueLockKey' => $queueLockKey
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务成功
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function handleJobSuccess($job, $queueLockKey)
|
||||
{
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info('工作台自动点赞任务执行成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务错误
|
||||
* @param \Exception $e
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function handleJobError(\Exception $e, $job, $queueLockKey)
|
||||
{
|
||||
Log::error('工作台自动点赞任务异常:' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理空工作台情况
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function handleEmptyWorkbenches(Job $job, $queueLockKey)
|
||||
{
|
||||
Log::info('没有需要处理的工作台自动点赞任务');
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
}
|
||||
}
|
||||
155
application/job/WorkbenchGroupCreateAdminFriendJob.php
Normal file
155
application/job/WorkbenchGroupCreateAdminFriendJob.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\api\controller\WebSocketController;
|
||||
use think\facade\Log;
|
||||
use think\facade\Env;
|
||||
use think\Db;
|
||||
use think\queue\Job;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* 工作台群创建-拉管理员好友任务
|
||||
* Class WorkbenchGroupCreateAdminFriendJob
|
||||
* @package app\job
|
||||
*/
|
||||
class WorkbenchGroupCreateAdminFriendJob
|
||||
{
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* 成员类型常量
|
||||
*/
|
||||
const MEMBER_TYPE_ADMIN_FRIEND = 4;
|
||||
|
||||
/**
|
||||
* 状态常量
|
||||
*/
|
||||
const STATUS_SUCCESS = 2;
|
||||
const STATUS_ADMIN_FRIEND_ADDED = 4;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$workbenchId = $data['workbenchId'] ?? 0;
|
||||
$wechatAccountId = $data['wechatAccountId'] ?? 0;
|
||||
$groupId = $data['groupId'] ?? 0;
|
||||
$chatroomId = $data['chatroomId'] ?? '';
|
||||
$adminFriendIds = $data['adminFriendIds'] ?? [];
|
||||
$poolUsers = $data['poolUsers'] ?? [];
|
||||
|
||||
try {
|
||||
if (empty($adminFriendIds) || empty($poolUsers)) {
|
||||
Log::info("管理员好友或流量池用户为空,跳过。工作台ID: {$workbenchId}");
|
||||
$job->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取管理员信息
|
||||
$adminFriends = Db::table('s2_wechat_friend')
|
||||
->where('id', 'in', $adminFriendIds)
|
||||
->column('id,wechatId,ownerWechatId');
|
||||
|
||||
if (empty($adminFriends)) {
|
||||
Log::warning("未找到管理员好友信息。工作台ID: {$workbenchId}");
|
||||
$job->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取微信账号信息
|
||||
$wechatAccount = Db::table('s2_wechat_account')->where('id', $wechatAccountId)->find();
|
||||
if (empty($wechatAccount)) {
|
||||
Log::error("未找到微信账号。微信账号ID: {$wechatAccountId}");
|
||||
$job->delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从流量池用户中查找每个管理员的好友
|
||||
// 管理员的好友:从s2_wechat_friend表中查找,ownerWechatId=管理员的wechatId,且wechatId在流量池用户中
|
||||
$allAdminFriendIds = [];
|
||||
foreach ($adminFriends as $adminFriend) {
|
||||
$adminWechatId = $adminFriend['wechatId'];
|
||||
|
||||
// 从好友表中查找该管理员的好友(在流量池用户中)
|
||||
$adminFriendsList = Db::table('s2_wechat_friend')
|
||||
->where('ownerWechatId', $adminWechatId)
|
||||
->whereIn('wechatId', $poolUsers)
|
||||
->column('id,wechatId');
|
||||
|
||||
if (!empty($adminFriendsList)) {
|
||||
$allAdminFriendIds = array_merge($allAdminFriendIds, array_keys($adminFriendsList));
|
||||
}
|
||||
}
|
||||
|
||||
$allAdminFriendIds = array_unique($allAdminFriendIds);
|
||||
|
||||
if (empty($allAdminFriendIds)) {
|
||||
Log::info("未找到管理员的好友,跳过拉人。工作台ID: {$workbenchId}");
|
||||
$job->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 初始化WebSocket
|
||||
$toAccountId = '';
|
||||
$username = Env::get('api.username2', '');
|
||||
$password = Env::get('api.password2', '');
|
||||
if (!empty($username) || !empty($password)) {
|
||||
$toAccountId = Db::name('users')->where('account', $username)->value('s2_accountId');
|
||||
}
|
||||
$webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]);
|
||||
|
||||
// 拉管理员好友进群
|
||||
$inviteResult = $webSocket->CmdChatroomInvite([
|
||||
'wechatChatroomId' => $groupId,
|
||||
'wechatFriendIds' => $allAdminFriendIds
|
||||
]);
|
||||
|
||||
// 记录管理员好友进群
|
||||
$installData = [];
|
||||
foreach ($allAdminFriendIds as $friendId) {
|
||||
$friendInfo = Db::table('s2_wechat_friend')->where('id', $friendId)->find();
|
||||
$installData[] = [
|
||||
'workbenchId' => $workbenchId,
|
||||
'friendId' => $friendId,
|
||||
'wechatId' => $friendInfo['wechatId'] ?? '',
|
||||
'groupId' => $groupId,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'status' => self::STATUS_ADMIN_FRIEND_ADDED,
|
||||
'memberType' => self::MEMBER_TYPE_ADMIN_FRIEND,
|
||||
'retryCount' => 0,
|
||||
'chatroomId' => $chatroomId,
|
||||
'createTime' => time(),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($installData)) {
|
||||
Db::name('workbench_group_create_item')->insertAll($installData);
|
||||
Log::info("管理员好友已拉入群。工作台ID: {$workbenchId}, 群ID: {$groupId}, 好友数: " . count($installData));
|
||||
}
|
||||
|
||||
$job->delete();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Log::error("拉管理员好友任务异常:{$e->getMessage()}");
|
||||
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
852
application/job/WorkbenchGroupCreateJob.php
Normal file
852
application/job/WorkbenchGroupCreateJob.php
Normal file
@@ -0,0 +1,852 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\api\controller\WebSocketController;
|
||||
use app\cunkebao\model\Workbench;
|
||||
use app\cunkebao\model\WorkbenchGroupCreate;
|
||||
use app\api\model\WechatFriendModel as WechatFriend;
|
||||
use app\api\model\WechatMomentsModel as WechatMoments;
|
||||
use app\common\model\DeviceWechatLogin as DeviceWechatLoginModel;
|
||||
use think\facade\Log;
|
||||
use think\facade\Env;
|
||||
use think\Db;
|
||||
use think\queue\Job;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\MomentsController as Moments;
|
||||
use Workerman\Lib\Timer;
|
||||
use app\api\controller\WechatController;
|
||||
use think\Queue;
|
||||
|
||||
/**
|
||||
* 工作台群创建任务
|
||||
* Class WorkbenchGroupCreateJob
|
||||
* @package app\job
|
||||
*/
|
||||
class WorkbenchGroupCreateJob
|
||||
{
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$jobId = $data['jobId'] ?? '';
|
||||
$queueLockKey = $data['queueLockKey'] ?? '';
|
||||
try {
|
||||
$this->logJobStart($jobId, $queueLockKey);
|
||||
$this->execute();
|
||||
$this->handleJobSuccess($job, $queueLockKey);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleJobError($e, $job, $queueLockKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 成员类型常量
|
||||
*/
|
||||
const MEMBER_TYPE_OWNER = 1; // 群主成员
|
||||
const MEMBER_TYPE_ADMIN = 2; // 管理员
|
||||
const MEMBER_TYPE_OWNER_FRIEND = 3; // 群主好友
|
||||
const MEMBER_TYPE_ADMIN_FRIEND = 4; // 管理员好友
|
||||
|
||||
/**
|
||||
* 状态常量
|
||||
*/
|
||||
const STATUS_PENDING = 0; // 待创建
|
||||
const STATUS_CREATING = 1; // 创建中
|
||||
const STATUS_SUCCESS = 2; // 创建成功
|
||||
const STATUS_FAILED = 3; // 创建失败
|
||||
const STATUS_ADMIN_FRIEND_ADDED = 4; // 管理员好友已拉入
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
// 1. 查询启用了建群功能的数据
|
||||
$workbenches = Workbench::where(['status' => 0, 'type' => 4, 'isDel' => 0,'id' => 354])->order('id desc')->select();
|
||||
|
||||
foreach ($workbenches as $workbench) {
|
||||
// 获取工作台配置
|
||||
$config = WorkbenchGroupCreate::where('workbenchId', $workbench->id)->find();
|
||||
if (!$config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析配置
|
||||
$config['poolGroups'] = json_decode($config['poolGroups'] ?? '[]', true) ?: [];
|
||||
$config['devices'] = json_decode($config['devices'] ?? '[]', true) ?: [];
|
||||
$config['wechatGroups'] = json_decode($config['wechatGroups'] ?? '[]', true) ?: [];
|
||||
$config['admins'] = json_decode($config['admins'] ?? '[]', true) ?: [];
|
||||
|
||||
// 检查时间限制
|
||||
if (!$this->isWithinTimeRange($config)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查每日建群数量限制
|
||||
if (!$this->checkDailyLimit($workbench->id, $config)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否有正在创建中的群,如果有则跳过(避免重复创建)
|
||||
$creatingCount = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('status', self::STATUS_CREATING)
|
||||
->where('groupId', '<>', null) // 有groupId的记录
|
||||
->group('groupId')
|
||||
->count();
|
||||
if ($creatingCount > 0) {
|
||||
Log::info("工作台ID: {$workbench->id} 有正在创建中的群({$creatingCount}个),跳过本次执行");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($config['devices'])) {
|
||||
continue;
|
||||
}
|
||||
// 获取群主成员(从设备中获取)
|
||||
$groupMember = [];
|
||||
$wechatIds = Db::name('device_wechat_login')
|
||||
->whereIn('deviceId', $config['devices'])
|
||||
->where('alive', DeviceWechatLoginModel::ALIVE_WECHAT_ACTIVE)
|
||||
->order('id desc')
|
||||
->column('wechatId');
|
||||
|
||||
if (empty($wechatIds)) {
|
||||
continue;
|
||||
}
|
||||
$groupMember = array_unique($wechatIds);
|
||||
|
||||
// 获取群主好友ID映射(所有群主的好友)
|
||||
$groupMemberWechatId = [];
|
||||
$groupMemberId = [];
|
||||
|
||||
foreach ($groupMember as $ownerWechatId) {
|
||||
$friends = Db::table('s2_wechat_friend')
|
||||
->where('ownerWechatId', $ownerWechatId)
|
||||
->whereIn('wechatId', $groupMember)
|
||||
->where('isDeleted', 0)
|
||||
->field('id,wechatId')
|
||||
->select();
|
||||
|
||||
foreach ($friends as $friend) {
|
||||
if (!isset($groupMemberWechatId[$friend['id']])) {
|
||||
$groupMemberWechatId[$friend['id']] = $friend['wechatId'];
|
||||
$groupMemberId[] = $friend['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果配置了wechatGroups,从指定的群组中获取成员
|
||||
if (!empty($config['wechatGroups'])) {
|
||||
$this->addGroupMembersFromWechatGroups($config['wechatGroups'], $groupMember, $groupMemberId, $groupMemberWechatId);
|
||||
}
|
||||
|
||||
if (empty($groupMemberId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取流量池用户(如果配置了流量池)
|
||||
$poolItem = [];
|
||||
if (!empty($config['poolGroups'])) {
|
||||
$poolItem = Db::name('traffic_source_package_item')
|
||||
->whereIn('packageId', $config['poolGroups'])
|
||||
->where('isDel', 0)
|
||||
->group('identifier')
|
||||
->column('identifier');
|
||||
}
|
||||
|
||||
// 如果既没有流量池也没有指定群组,跳过
|
||||
if (empty($poolItem) && empty($config['wechatGroups'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取已入群的用户(排除已成功入群的)
|
||||
$groupUser = [];
|
||||
if (!empty($poolItem)) {
|
||||
$groupUser = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('status', 'in', [self::STATUS_SUCCESS, self::STATUS_ADMIN_FRIEND_ADDED, self::STATUS_CREATING])
|
||||
->whereIn('wechatId', $poolItem)
|
||||
->group('wechatId')
|
||||
->column('wechatId');
|
||||
}
|
||||
|
||||
// 待入群的用户(从流量池中筛选)
|
||||
$joinUser = !empty($poolItem) ? array_diff($poolItem, $groupUser) : [];
|
||||
|
||||
// 如果流量池用户已用完或没有配置流量池,但配置了wechatGroups,至少创建一次(使用群主成员)
|
||||
if (empty($joinUser) && !empty($config['wechatGroups'])) {
|
||||
// 如果没有流量池用户,创建一个空批次,让processBatchUsers处理只有群主成员的情况
|
||||
$joinUser = []; // 空数组,但会继续执行
|
||||
}
|
||||
|
||||
// 如果既没有流量池用户也没有配置wechatGroups,跳过
|
||||
if (empty($joinUser) && empty($config['wechatGroups'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算随机群人数(不包含管理员,只减去群主成员数)
|
||||
// 群主成员数 = 群主好友ID数量
|
||||
$minGroupSize = max(2, $config['groupSizeMin']); // 至少2人才能建群
|
||||
$maxGroupSize = max($minGroupSize, $config['groupSizeMax']);
|
||||
$groupRandNum = mt_rand($minGroupSize, $maxGroupSize) - count($groupMemberId);
|
||||
if ($groupRandNum <= 0) {
|
||||
$groupRandNum = 1; // 至少需要1个成员
|
||||
}
|
||||
|
||||
// 分批处理待入群用户
|
||||
$addGroupUser = [];
|
||||
if (!empty($joinUser)) {
|
||||
$totalRows = count($joinUser);
|
||||
for ($i = 0; $i < $totalRows; $i += $groupRandNum) {
|
||||
$batchRows = array_slice($joinUser, $i, $groupRandNum);
|
||||
if (!empty($batchRows)) {
|
||||
$addGroupUser[] = $batchRows;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果没有流量池用户但配置了wechatGroups,创建一个空批次
|
||||
$addGroupUser[] = [];
|
||||
}
|
||||
// 初始化WebSocket
|
||||
$toAccountId = '';
|
||||
$username = Env::get('api.username2', '');
|
||||
$password = Env::get('api.password2', '');
|
||||
if (!empty($username) || !empty($password)) {
|
||||
$toAccountId = Db::name('users')->where('account', $username)->value('s2_accountId');
|
||||
}
|
||||
$webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]);
|
||||
|
||||
// 遍历每批用户
|
||||
foreach ($addGroupUser as $batchUsers) {
|
||||
$this->processBatchUsers($workbench, $config, $batchUsers, $groupMemberId, $groupMemberWechatId, $groupRandNum, $webSocket);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("工作台建群任务异常: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理一批用户
|
||||
* @param Workbench $workbench 工作台
|
||||
* @param array $config 配置
|
||||
* @param array $batchUsers 批次用户(微信ID数组,来自流量池)
|
||||
* @param array $groupMemberId 群主成员ID数组
|
||||
* @param array $groupMemberWechatId 群主成员微信ID映射
|
||||
* @param int $groupRandNum 随机群人数(不包含管理员)
|
||||
* @param WebSocketController $webSocket WebSocket实例
|
||||
*/
|
||||
protected function processBatchUsers($workbench, $config, $batchUsers, $groupMemberId, $groupMemberWechatId, $groupRandNum, $webSocket)
|
||||
{
|
||||
// 1. 获取群主微信ID列表(用于验证管理员)
|
||||
// 从群主成员的好友记录中提取所有群主的微信ID(ownerWechatId)
|
||||
$groupOwnerWechatIds = [];
|
||||
foreach ($groupMemberId as $memberId) {
|
||||
$member = Db::table('s2_wechat_friend')->where('id', $memberId)->find();
|
||||
if ($member && !in_array($member['ownerWechatId'], $groupOwnerWechatIds)) {
|
||||
$groupOwnerWechatIds[] = $member['ownerWechatId'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果从好友表获取不到,使用群主成员微信ID列表(作为备用)
|
||||
if (empty($groupOwnerWechatIds)) {
|
||||
$groupOwnerWechatIds = array_values(array_unique($groupMemberWechatId));
|
||||
}
|
||||
// 2. 验证并获取管理员好友ID(管理员必须是群主的好友)
|
||||
$adminFriendIds = [];
|
||||
$adminWechatIds = [];
|
||||
if (!empty($config['admins'])) {
|
||||
$adminFriends = Db::table('s2_wechat_friend')
|
||||
->where('id', 'in', $config['admins'])
|
||||
->field('id,wechatId,ownerWechatId')
|
||||
->select();
|
||||
|
||||
foreach ($adminFriends as $adminFriend) {
|
||||
// 验证:管理员必须是群主的好友
|
||||
if (in_array($adminFriend['ownerWechatId'], $groupOwnerWechatIds)) {
|
||||
$adminFriendIds[] = $adminFriend['id'];
|
||||
$adminWechatIds[$adminFriend['id']] = $adminFriend['wechatId'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3. 从流量池用户中筛选出是群主好友的用户(按微信账号分组)
|
||||
$ownerFriendIdsByAccount = [];
|
||||
$wechatIds = [];
|
||||
|
||||
// 如果batchUsers为空,说明没有流量池用户,但可能配置了wechatGroups
|
||||
// 这种情况下,使用群主成员作为基础,按账号分组
|
||||
if (empty($batchUsers)) {
|
||||
// 按账号分组群主成员
|
||||
foreach ($groupMemberId as $memberId) {
|
||||
$member = Db::table('s2_wechat_friend')->where('id', $memberId)->find();
|
||||
if ($member) {
|
||||
$accountWechatId = $member['ownerWechatId'];
|
||||
$account = Db::table('s2_wechat_account')
|
||||
->where('wechatId', $accountWechatId)
|
||||
->field('id')
|
||||
->find();
|
||||
|
||||
if ($account) {
|
||||
$wechatAccountId = $account['id'];
|
||||
if (!isset($ownerFriendIdsByAccount[$wechatAccountId])) {
|
||||
$ownerFriendIdsByAccount[$wechatAccountId] = [];
|
||||
}
|
||||
$ownerFriendIdsByAccount[$wechatAccountId][] = $memberId;
|
||||
$wechatIds[$memberId] = $groupMemberWechatId[$memberId] ?? '';
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 获取群主的好友关系(从流量池中筛选)
|
||||
$ownerFriends = Db::table('s2_wechat_friend')->alias('f')
|
||||
->join(['s2_wechat_account' => 'a'], 'f.wechatAccountId=a.id')
|
||||
->whereIn('f.wechatId', $batchUsers)
|
||||
->whereIn('a.wechatId', $groupOwnerWechatIds)
|
||||
->where('f.isDeleted', 0)
|
||||
->field('f.id,f.wechatId,a.id as wechatAccountId')
|
||||
->select();
|
||||
|
||||
if (empty($ownerFriends)) {
|
||||
Log::warning("未找到群主的好友,跳过。工作台ID: {$workbench->id}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 按微信账号分组群主好友
|
||||
foreach ($ownerFriends as $friend) {
|
||||
$wechatAccountId = $friend['wechatAccountId'];
|
||||
if (!isset($ownerFriendIdsByAccount[$wechatAccountId])) {
|
||||
$ownerFriendIdsByAccount[$wechatAccountId] = [];
|
||||
}
|
||||
$ownerFriendIdsByAccount[$wechatAccountId][] = $friend['id'];
|
||||
$wechatIds[$friend['id']] = $friend['wechatId'];
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到任何好友,跳过
|
||||
if (empty($ownerFriendIdsByAccount)) {
|
||||
Log::warning("未找到任何群主好友或成员,跳过。工作台ID: {$workbench->id}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 遍历每个微信账号,创建群
|
||||
foreach ($ownerFriendIdsByAccount as $wechatAccountId => $ownerFriendIds) {
|
||||
// 4.1 获取当前账号的管理员好友ID
|
||||
$currentAdminFriendIds = [];
|
||||
$accountWechatId = Db::table('s2_wechat_account')->where('id', $wechatAccountId)->value('wechatId');
|
||||
foreach ($adminFriendIds as $adminFriendId) {
|
||||
$adminFriend = Db::table('s2_wechat_friend')->where('id', $adminFriendId)->find();
|
||||
if ($adminFriend && $adminFriend['ownerWechatId'] == $accountWechatId) {
|
||||
$currentAdminFriendIds[] = $adminFriendId;
|
||||
$wechatIds[$adminFriendId] = $adminWechatIds[$adminFriendId];
|
||||
}
|
||||
}
|
||||
|
||||
// 4.2 获取当前账号的群主成员ID
|
||||
$currentGroupMemberIds = [];
|
||||
foreach ($groupMemberId as $memberId) {
|
||||
$member = Db::table('s2_wechat_friend')->where('id', $memberId)->find();
|
||||
if ($member && $member['ownerWechatId'] == $accountWechatId) {
|
||||
$currentGroupMemberIds[] = $memberId;
|
||||
if (!isset($wechatIds[$memberId])) {
|
||||
$wechatIds[$memberId] = $groupMemberWechatId[$memberId] ?? '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4.3 限制群主好友数量(按随机群人数)
|
||||
// 如果ownerFriendIds只包含群主成员(没有流量池用户),则不需要限制
|
||||
$limitedOwnerFriendIds = $ownerFriendIds;
|
||||
if (count($ownerFriendIds) > $groupRandNum) {
|
||||
$limitedOwnerFriendIds = array_slice($ownerFriendIds, 0, $groupRandNum);
|
||||
}
|
||||
|
||||
// 4.4 创建群:管理员 + 群主成员 + 群主好友(从流量池筛选)
|
||||
// 合并时去重,避免重复添加群主成员
|
||||
$createFriendIds = array_merge($currentAdminFriendIds, $currentGroupMemberIds);
|
||||
foreach ($limitedOwnerFriendIds as $friendId) {
|
||||
if (!in_array($friendId, $createFriendIds)) {
|
||||
$createFriendIds[] = $friendId;
|
||||
}
|
||||
}
|
||||
|
||||
// 微信建群至少需要2个人
|
||||
if (count($createFriendIds) < 2) {
|
||||
Log::warning("建群好友数量不足(至少需要2人),跳过。工作台ID: {$workbench->id}, 微信账号ID: {$wechatAccountId}, 当前人数: " . count($createFriendIds));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4.5 检查当前账号是否有正在创建中的群,如果有则跳过
|
||||
$creatingGroupCount = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('status', self::STATUS_CREATING)
|
||||
->where('groupId', '<>', null)
|
||||
->group('groupId')
|
||||
->count();
|
||||
|
||||
if ($creatingGroupCount > 0) {
|
||||
Log::info("工作台ID: {$workbench->id}, 微信账号ID: {$wechatAccountId} 有正在创建中的群({$creatingGroupCount}个),跳过本次创建");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4.6 生成群名称
|
||||
$existingGroupCount = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('status', self::STATUS_SUCCESS)
|
||||
->where('groupId', '<>', null) // 排除groupId为NULL的记录
|
||||
->group('groupId')
|
||||
->count();
|
||||
|
||||
$chatroomName = $existingGroupCount > 0
|
||||
? $config['groupNameTemplate'] . ($existingGroupCount + 1) . '群'
|
||||
: $config['groupNameTemplate'];
|
||||
|
||||
// 4.7 调用建群接口
|
||||
$createTime = time();
|
||||
$createResult = $webSocket->CmdChatroomCreate([
|
||||
'chatroomName' => $chatroomName,
|
||||
'wechatFriendIds' => $createFriendIds,
|
||||
'wechatAccountId' => $wechatAccountId
|
||||
]);
|
||||
|
||||
$createResultData = json_decode($createResult, true);
|
||||
|
||||
// 4.8 解析建群结果,获取群ID
|
||||
// chatroomId: varchar(64) - 微信的群聊ID(字符串)
|
||||
// groupId: int(10) - 数据库中的群组ID(整数)
|
||||
$chatroomId = null; // 微信群聊ID(字符串)
|
||||
$groupId = 0; // 数据库群组ID(整数)
|
||||
$tempGroupId = null; // 临时群标识,用于轮询查询
|
||||
|
||||
if (!empty($createResultData) && isset($createResultData['code']) && $createResultData['code'] == 200) {
|
||||
// 尝试从返回数据中获取群ID(根据实际API返回格式调整)
|
||||
if (isset($createResultData['data']['chatroomId'])) {
|
||||
// API返回的是chatroomId(字符串)
|
||||
$chatroomId = (string)$createResultData['data']['chatroomId'];
|
||||
// 通过chatroomId查询数据库获取groupId
|
||||
$group = Db::name('wechat_group')
|
||||
->where('chatroomId', $chatroomId)
|
||||
->where('deleteTime', 0)
|
||||
->find();
|
||||
if ($group) {
|
||||
$groupId = intval($group['id']);
|
||||
}
|
||||
} elseif (isset($createResultData['data']['id'])) {
|
||||
// API返回的是数据库ID(整数)
|
||||
$groupId = intval($createResultData['data']['id']);
|
||||
// 通过groupId查询chatroomId
|
||||
$group = Db::name('wechat_group')
|
||||
->where('id', $groupId)
|
||||
->where('deleteTime', 0)
|
||||
->find();
|
||||
if ($group && !empty($group['chatroomId'])) {
|
||||
$chatroomId = (string)$group['chatroomId'];
|
||||
}
|
||||
}
|
||||
// 如果有临时标识,保存用于轮询
|
||||
if (isset($createResultData['data']['tempId'])) {
|
||||
$tempGroupId = $createResultData['data']['tempId'];
|
||||
}
|
||||
}
|
||||
|
||||
// 4.9 如果建群接口没有立即返回群ID,进行同步轮询检查
|
||||
if ($groupId == 0) {
|
||||
// 获取账号的微信ID(群主微信ID)
|
||||
$accountWechatId = Db::table('s2_wechat_account')
|
||||
->where('id', $wechatAccountId)
|
||||
->value('wechatId');
|
||||
|
||||
if (!empty($accountWechatId)) {
|
||||
$pollResult = $this->pollGroupCreation($chatroomName, $accountWechatId, $wechatAccountId, $tempGroupId);
|
||||
if ($pollResult && is_array($pollResult)) {
|
||||
$groupId = intval($pollResult['groupId'] ?? 0);
|
||||
$chatroomId = !empty($pollResult['chatroomId']) ? (string)$pollResult['chatroomId'] : null;
|
||||
} elseif ($pollResult > 0) {
|
||||
// 兼容旧返回值(只返回groupId)
|
||||
$groupId =0;
|
||||
$chatroomId = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4.10 记录创建请求
|
||||
$installData = [];
|
||||
foreach ($createFriendIds as $friendId) {
|
||||
$memberType = in_array($friendId, $currentAdminFriendIds)
|
||||
? self::MEMBER_TYPE_ADMIN
|
||||
: (in_array($friendId, $currentGroupMemberIds) ? self::MEMBER_TYPE_OWNER : self::MEMBER_TYPE_OWNER_FRIEND);
|
||||
|
||||
$installData[] = [
|
||||
'workbenchId' => $workbench->id,
|
||||
'friendId' => $friendId,
|
||||
'wechatId' => $wechatIds[$friendId] ?? ($groupMemberWechatId[$friendId] ?? ''),
|
||||
'groupId' => $groupId > 0 ? $groupId : null, // int类型
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'status' => $groupId > 0 ? self::STATUS_SUCCESS : self::STATUS_FAILED,
|
||||
'memberType' => $memberType,
|
||||
'retryCount' => 0,
|
||||
'chatroomId' => $chatroomId, // varchar类型
|
||||
'createTime' => $createTime,
|
||||
];
|
||||
}
|
||||
Db::name('workbench_group_create_item')->insertAll($installData);
|
||||
|
||||
// 5. 如果群创建成功,拉管理员的好友进群
|
||||
// 注意:拉人接口需要chatroomId(字符串),而不是groupId(整数)
|
||||
if (!empty($chatroomId) && !empty($currentAdminFriendIds)) {
|
||||
$this->inviteAdminFriends($workbench, $config, $batchUsers, $currentAdminFriendIds, $chatroomId, $groupId, $wechatAccountId, $wechatIds, $createTime, $webSocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉管理员的好友进群
|
||||
* @param Workbench $workbench 工作台
|
||||
* @param array $config 配置
|
||||
* @param array $batchUsers 批次用户(流量池微信ID数组)
|
||||
* @param array $adminFriendIds 管理员好友ID数组
|
||||
* @param string $chatroomId 群聊ID(字符串,用于API调用)
|
||||
* @param int $groupId 数据库群组ID(整数)
|
||||
* @param int $wechatAccountId 微信账号ID
|
||||
* @param array $wechatIds 好友ID到微信ID的映射
|
||||
* @param int $createTime 创建时间
|
||||
* @param WebSocketController $webSocket WebSocket实例
|
||||
*/
|
||||
protected function inviteAdminFriends($workbench, $config, $batchUsers, $adminFriendIds, $chatroomId, $groupId, $wechatAccountId, $wechatIds, $createTime, $webSocket)
|
||||
{
|
||||
// 获取管理员的微信ID列表
|
||||
$adminWechatIds = [];
|
||||
foreach ($adminFriendIds as $adminFriendId) {
|
||||
if (isset($wechatIds[$adminFriendId])) {
|
||||
$adminWechatIds[] = $wechatIds[$adminFriendId];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($adminWechatIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从流量池用户中筛选出是管理员好友的用户
|
||||
$adminFriendsFromPool = Db::table('s2_wechat_friend')->alias('f')
|
||||
->join(['s2_wechat_account' => 'a'], 'f.wechatAccountId=a.id')
|
||||
->whereIn('f.wechatId', $batchUsers)
|
||||
->whereIn('a.wechatId', $adminWechatIds)
|
||||
->where('a.id', $wechatAccountId)
|
||||
->where('f.isDeleted', 0)
|
||||
->field('f.id,f.wechatId')
|
||||
->select();
|
||||
|
||||
if (empty($adminFriendsFromPool)) {
|
||||
Log::info("未找到管理员的好友,跳过拉人。工作台ID: {$workbench->id}, 群ID: {$chatroomId}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 提取好友ID列表
|
||||
$adminFriendIdsToInvite = [];
|
||||
foreach ($adminFriendsFromPool as $friend) {
|
||||
$adminFriendIdsToInvite[] = $friend['id'];
|
||||
$wechatIds[$friend['id']] = $friend['wechatId'];
|
||||
}
|
||||
|
||||
// 调用拉人接口(使用chatroomId字符串)
|
||||
$inviteResult = $webSocket->CmdChatroomInvite([
|
||||
'wechatChatroomId' => $chatroomId,
|
||||
'wechatFriendIds' => $adminFriendIdsToInvite
|
||||
]);
|
||||
|
||||
$inviteResultData = json_decode($inviteResult, true);
|
||||
$inviteSuccess = !empty($inviteResultData) && isset($inviteResultData['code']) && $inviteResultData['code'] == 200;
|
||||
|
||||
// 记录管理员好友拉入状态
|
||||
$adminFriendData = [];
|
||||
foreach ($adminFriendIdsToInvite as $friendId) {
|
||||
$adminFriendData[] = [
|
||||
'workbenchId' => $workbench->id,
|
||||
'friendId' => $friendId,
|
||||
'wechatId' => $wechatIds[$friendId] ?? '',
|
||||
'groupId' => $groupId > 0 ? $groupId : null, // int类型
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'status' => $inviteSuccess ? self::STATUS_ADMIN_FRIEND_ADDED : self::STATUS_FAILED,
|
||||
'memberType' => self::MEMBER_TYPE_ADMIN_FRIEND,
|
||||
'retryCount' => 0,
|
||||
'chatroomId' => $chatroomId, // varchar类型
|
||||
'createTime' => $createTime,
|
||||
];
|
||||
}
|
||||
Db::name('workbench_group_create_item')->insertAll($adminFriendData);
|
||||
|
||||
if ($inviteSuccess) {
|
||||
// 去除成功日志,减少日志空间消耗
|
||||
} else {
|
||||
Log::warning("管理员好友拉入失败。工作台ID: {$workbench->id}, 群组ID: {$groupId}, 群聊ID: {$chatroomId}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 轮询检查群是否创建成功
|
||||
* @param string $chatroomName 群名称
|
||||
* @param string $ownerWechatId 群主微信ID
|
||||
* @param int $wechatAccountId 微信账号ID
|
||||
* @param string|null $tempGroupId 临时群标识(如果有)
|
||||
* @return array|int 返回数组包含groupId和chatroomId,或只返回groupId(兼容旧代码),如果未找到返回0
|
||||
*/
|
||||
protected function pollGroupCreation($chatroomName, $ownerWechatId, $wechatAccountId, $tempGroupId = null)
|
||||
{
|
||||
$maxAttempts = 10; // 最多查询10次
|
||||
$interval = 5; // 每次间隔5秒
|
||||
|
||||
// 获取账号ID(accountId)和微信账号的微信ID(wechatAccountWechatId),用于查询s2_wechat_chatroom表
|
||||
$accountInfo = Db::table('s2_wechat_account')
|
||||
->where('id', $wechatAccountId)
|
||||
->field('id,wechatId')
|
||||
->find();
|
||||
|
||||
$accountId = $accountInfo['id'] ?? null;
|
||||
$wechatAccountWechatId = $accountInfo['wechatId'] ?? null;
|
||||
|
||||
if (empty($accountId) && empty($wechatAccountWechatId)) {
|
||||
Log::warning("无法获取账号ID和微信账号ID,跳过轮询。微信账号ID: {$wechatAccountId}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 获取授权信息(用于调用同步接口)
|
||||
$username = Env::get('api.username2', '');
|
||||
$password = Env::get('api.password2', '');
|
||||
|
||||
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
|
||||
// 等待5秒(第一次立即查询,后续等待)
|
||||
if ($attempt > 1) {
|
||||
sleep($interval);
|
||||
}
|
||||
|
||||
// 1. 先调用接口同步最新的群组信息
|
||||
try {
|
||||
$chatroomController = new \app\api\controller\WechatChatroomController();
|
||||
// 构建同步参数
|
||||
$syncData = [
|
||||
'wechatAccountKeyword' => $ownerWechatId, // 通过群主微信ID筛选
|
||||
'isDeleted' => false,
|
||||
'pageIndex' => 0,
|
||||
'pageSize' => 5 // 获取足够多的数据
|
||||
];
|
||||
// 调用getlist方法同步数据(内部调用,isInner=true)
|
||||
$chatroomController->getlist($syncData, true, 0);
|
||||
} catch (\Exception $e) {
|
||||
Log::warning("同步群组信息失败: " . $e->getMessage());
|
||||
// 即使同步失败,也继续查询本地数据
|
||||
}
|
||||
|
||||
// 2. 查询本地表 s2_wechat_chatroom
|
||||
// 计算5分钟前的时间戳
|
||||
$fiveMinutesAgo = time() - 300; // 5分钟 = 300秒
|
||||
$now = time();
|
||||
|
||||
// 查询群聊:通过群名称、账号ID或微信账号ID和创建时间查询
|
||||
// 如果accountId不为空,优先使用accountId查询;如果accountId为空,则使用wechatAccountWechatId查询
|
||||
$chatroom = Db::table('s2_wechat_chatroom')
|
||||
->where('nickname', $chatroomName)
|
||||
->where('isDeleted', 0)
|
||||
->where('createTime', '>=', $fiveMinutesAgo) // 创建时间在5分钟内
|
||||
->where('createTime', '<=', $now)
|
||||
->where('wechatAccountWechatId', $wechatAccountWechatId)
|
||||
->order('createTime', 'desc')
|
||||
->find();
|
||||
|
||||
|
||||
// 如果找到了群聊,返回群ID和chatroomId
|
||||
if ($chatroom && !empty($chatroom['id'])) {
|
||||
$chatroomId = !empty($chatroom['chatroomId']) ? (string)$chatroom['chatroomId'] : null;
|
||||
// 如果有chatroomId,尝试查询wechat_group表获取groupId
|
||||
$groupId = $chatroom['id'];
|
||||
Log::info("轮询检查群创建成功。群名称: {$chatroomName}, 群聊ID: {$chatroom['id']}, chatroomId: {$chatroomId}, 群组ID: {$groupId}, 尝试次数: {$attempt}");
|
||||
return [
|
||||
'groupId' => $groupId > 0 ? $groupId : intval($chatroom['id']), // 如果没有groupId,使用chatroom的id
|
||||
'chatroomId' => $chatroomId ?: (string)$chatroom['id']
|
||||
];
|
||||
}
|
||||
|
||||
Log::debug("轮询检查群创建中。群名称: {$chatroomName}, 尝试次数: {$attempt}/{$maxAttempts}");
|
||||
}
|
||||
|
||||
// 10次查询后仍未找到,返回0表示失败
|
||||
Log::warning("轮询检查群创建失败,已查询{$maxAttempts}次仍未找到群组。群名称: {$chatroomName}, 群主微信ID: {$ownerWechatId}, 账号ID: {$accountId}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否在时间范围内
|
||||
* @param array $config 配置
|
||||
* @return bool
|
||||
*/
|
||||
protected function isWithinTimeRange($config)
|
||||
{
|
||||
if (empty($config['startTime']) || empty($config['endTime'])) {
|
||||
return true; // 如果没有配置时间,则允许执行
|
||||
}
|
||||
|
||||
$today = date('Y-m-d');
|
||||
$startTimestamp = strtotime($today . ' ' . $config['startTime'] . ':00');
|
||||
$endTimestamp = strtotime($today . ' ' . $config['endTime'] . ':00');
|
||||
|
||||
$currentTime = time();
|
||||
|
||||
// 如果开始时间大于当前时间,还未到执行时间
|
||||
if ($startTimestamp > $currentTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果结束时间小于当前时间,已过执行时间
|
||||
if ($endTimestamp < $currentTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查每日建群数量限制
|
||||
* @param int $workbenchId 工作台ID
|
||||
* @param array $config 配置
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkDailyLimit($workbenchId, $config)
|
||||
{
|
||||
if (empty($config['maxGroupsPerDay']) || $config['maxGroupsPerDay'] <= 0) {
|
||||
return true; // 如果没有配置限制,则允许执行
|
||||
}
|
||||
|
||||
$today = date('Y-m-d');
|
||||
$startTimestamp = strtotime($today . ' 00:00:00');
|
||||
$endTimestamp = strtotime($today . ' 23:59:59');
|
||||
|
||||
// 查询今日已创建的群数量(状态为成功)
|
||||
$todayCount = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('status', self::STATUS_SUCCESS)
|
||||
->where('groupId', '<>', null) // 排除groupId为NULL的记录
|
||||
->where('createTime', 'between', [$startTimestamp, $endTimestamp])
|
||||
->group('groupId')
|
||||
->count();
|
||||
|
||||
return $todayCount < $config['maxGroupsPerDay'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定的微信群组中获取成员
|
||||
* @param array $wechatGroups 群组ID数组(可能是好友ID或群组ID)
|
||||
* @param array $groupMember 群主成员微信ID数组(引用传递)
|
||||
* @param array $groupMemberId 群主成员好友ID数组(引用传递)
|
||||
* @param array $groupMemberWechatId 群主成员微信ID映射(引用传递)
|
||||
*/
|
||||
protected function addGroupMembersFromWechatGroups($wechatGroups, &$groupMember, &$groupMemberId, &$groupMemberWechatId)
|
||||
{
|
||||
foreach ($wechatGroups as $groupId) {
|
||||
if (is_numeric($groupId)) {
|
||||
// 数字ID:可能是好友ID,查询好友信息
|
||||
$friend = Db::table('s2_wechat_friend')
|
||||
->where('id', $groupId)
|
||||
->where('isDeleted', 0)
|
||||
->field('id,wechatId,ownerWechatId')
|
||||
->find();
|
||||
|
||||
if ($friend) {
|
||||
// 添加到群主成员
|
||||
if (!in_array($friend['ownerWechatId'], $groupMember)) {
|
||||
$groupMember[] = $friend['ownerWechatId'];
|
||||
}
|
||||
|
||||
if (!isset($groupMemberWechatId[$friend['id']])) {
|
||||
$groupMemberWechatId[$friend['id']] = $friend['wechatId'];
|
||||
$groupMemberId[] = $friend['id'];
|
||||
}
|
||||
} else {
|
||||
// 如果不是好友ID,可能是群组ID,查询群组信息
|
||||
$group = Db::name('wechat_group')
|
||||
->where('id', $groupId)
|
||||
->where('deleteTime', 0)
|
||||
->field('ownerWechatId')
|
||||
->find();
|
||||
|
||||
if ($group && !in_array($group['ownerWechatId'], $groupMember)) {
|
||||
$groupMember[] = $group['ownerWechatId'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 字符串ID:手动创建的群组,可能是wechatId
|
||||
if (!in_array($groupId, $groupMember)) {
|
||||
$groupMember[] = $groupId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 记录任务开始
|
||||
* @param string $jobId
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function logJobStart($jobId, $queueLockKey)
|
||||
{
|
||||
// 去除开始日志,减少日志空间消耗
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务成功
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function handleJobSuccess($job, $queueLockKey)
|
||||
{
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
// 去除成功日志,减少日志空间消耗
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务错误
|
||||
* @param \Exception $e
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function handleJobError(\Exception $e, $job, $queueLockKey)
|
||||
{
|
||||
Log::error('工作台消息群发任务异常:' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
109
application/job/WorkbenchGroupCreateOwnerFriendJob.php
Normal file
109
application/job/WorkbenchGroupCreateOwnerFriendJob.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\api\controller\WebSocketController;
|
||||
use think\facade\Log;
|
||||
use think\facade\Env;
|
||||
use think\Db;
|
||||
use think\queue\Job;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* 工作台群创建-拉群主好友任务
|
||||
* Class WorkbenchGroupCreateOwnerFriendJob
|
||||
* @package app\job
|
||||
*/
|
||||
class WorkbenchGroupCreateOwnerFriendJob
|
||||
{
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* 成员类型常量
|
||||
*/
|
||||
const MEMBER_TYPE_OWNER_FRIEND = 3;
|
||||
|
||||
/**
|
||||
* 状态常量
|
||||
*/
|
||||
const STATUS_SUCCESS = 2;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$workbenchId = $data['workbenchId'] ?? 0;
|
||||
$wechatAccountId = $data['wechatAccountId'] ?? 0;
|
||||
$groupId = $data['groupId'] ?? 0;
|
||||
$chatroomId = $data['chatroomId'] ?? '';
|
||||
$ownerFriendIds = $data['ownerFriendIds'] ?? [];
|
||||
$createTime = $data['createTime'] ?? 0;
|
||||
|
||||
try {
|
||||
if (empty($ownerFriendIds) || empty($groupId)) {
|
||||
Log::info("群主好友或群ID为空,跳过。工作台ID: {$workbenchId}");
|
||||
$job->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 初始化WebSocket
|
||||
$toAccountId = '';
|
||||
$username = Env::get('api.username2', '');
|
||||
$password = Env::get('api.password2', '');
|
||||
if (!empty($username) || !empty($password)) {
|
||||
$toAccountId = Db::name('users')->where('account', $username)->value('s2_accountId');
|
||||
}
|
||||
$webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]);
|
||||
|
||||
// 拉群主好友进群
|
||||
$inviteResult = $webSocket->CmdChatroomInvite([
|
||||
'wechatChatroomId' => $groupId,
|
||||
'wechatFriendIds' => $ownerFriendIds
|
||||
]);
|
||||
|
||||
// 获取好友微信ID映射
|
||||
$friendWechatIds = Db::table('s2_wechat_friend')
|
||||
->where('id', 'in', $ownerFriendIds)
|
||||
->column('id,wechatId');
|
||||
|
||||
// 更新群主好友记录状态
|
||||
Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('status', 1) // 创建中
|
||||
->where('memberType', self::MEMBER_TYPE_OWNER_FRIEND)
|
||||
->where('createTime', '>=', $createTime - 10)
|
||||
->where('createTime', '<=', $createTime + 10)
|
||||
->update([
|
||||
'status' => self::STATUS_SUCCESS,
|
||||
'groupId' => $groupId,
|
||||
'chatroomId' => $chatroomId,
|
||||
'verifyTime' => time()
|
||||
]);
|
||||
|
||||
Log::info("群主好友已拉入群。工作台ID: {$workbenchId}, 群ID: {$groupId}, 好友数: " . count($ownerFriendIds));
|
||||
|
||||
$job->delete();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Log::error("拉群主好友任务异常:{$e->getMessage()}");
|
||||
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
179
application/job/WorkbenchGroupCreateRetryJob.php
Normal file
179
application/job/WorkbenchGroupCreateRetryJob.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\api\controller\WebSocketController;
|
||||
use app\cunkebao\model\Workbench;
|
||||
use app\cunkebao\model\WorkbenchGroupCreate;
|
||||
use think\facade\Log;
|
||||
use think\facade\Env;
|
||||
use think\Db;
|
||||
use think\queue\Job;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
use think\Queue;
|
||||
|
||||
/**
|
||||
* 工作台群创建重试任务
|
||||
* Class WorkbenchGroupCreateRetryJob
|
||||
* @package app\job
|
||||
*/
|
||||
class WorkbenchGroupCreateRetryJob
|
||||
{
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$workbenchId = $data['workbenchId'] ?? 0;
|
||||
$wechatAccountId = $data['wechatAccountId'] ?? 0;
|
||||
$createTime = $data['createTime'] ?? 0;
|
||||
|
||||
try {
|
||||
// 获取工作台和配置
|
||||
$workbench = Workbench::where('id', $workbenchId)->find();
|
||||
if (!$workbench) {
|
||||
Log::error("未找到工作台。工作台ID: {$workbenchId}");
|
||||
$job->delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = WorkbenchGroupCreate::where('workbenchId', $workbench->id)->find();
|
||||
if (!$config) {
|
||||
Log::error("未找到工作台配置。工作台ID: {$workbenchId}");
|
||||
$job->delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取失败记录
|
||||
$failedItems = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('createTime', '>=', $createTime - 10)
|
||||
->where('createTime', '<=', $createTime + 10)
|
||||
->where('status', 'in', [1, 3]) // 创建中或失败
|
||||
->select();
|
||||
|
||||
if (empty($failedItems)) {
|
||||
Log::info("未找到需要重试的记录。工作台ID: {$workbenchId}");
|
||||
$job->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 解析配置
|
||||
$config['poolGroups'] = json_decode($config['poolGroups'], true);
|
||||
$config['devices'] = json_decode($config['devices'], true);
|
||||
$config['admins'] = json_decode($config['admins'] ?? '[]', true) ?: [];
|
||||
|
||||
// 获取群主成员
|
||||
$groupMember = Db::name('device_wechat_login')->alias('dwl')
|
||||
->join(['s2_wechat_account' => 'a'], 'dwl.wechatId = a.wechatId')
|
||||
->whereIn('dwl.deviceId', $config['devices'])
|
||||
->group('a.id')
|
||||
->column('a.wechatId');
|
||||
|
||||
$groupMemberWechatId = Db::table('s2_wechat_friend')
|
||||
->where('ownerWechatId', $groupMember[0])
|
||||
->whereIn('wechatId', $groupMember)
|
||||
->column('id,wechatId');
|
||||
|
||||
$groupMemberId = array_keys($groupMemberWechatId);
|
||||
|
||||
// 获取管理员好友ID
|
||||
$adminFriendIds = [];
|
||||
if (!empty($config['admins'])) {
|
||||
$adminFriends = Db::table('s2_wechat_friend')
|
||||
->where('id', 'in', $config['admins'])
|
||||
->column('id,wechatId,ownerWechatId');
|
||||
|
||||
$accountWechatId = Db::table('s2_wechat_account')->where('id', $wechatAccountId)->value('wechatId');
|
||||
foreach ($adminFriends as $adminFriend) {
|
||||
if ($adminFriend['ownerWechatId'] == $accountWechatId) {
|
||||
$adminFriendIds[] = $adminFriend['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化WebSocket
|
||||
$toAccountId = '';
|
||||
$username = Env::get('api.username2', '');
|
||||
$password = Env::get('api.password2', '');
|
||||
if (!empty($username) || !empty($password)) {
|
||||
$toAccountId = Db::name('users')->where('account', $username)->value('s2_accountId');
|
||||
}
|
||||
$webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]);
|
||||
|
||||
// 重新创建群
|
||||
$createFriendIds = array_merge($adminFriendIds, $groupMemberId);
|
||||
|
||||
if (count($createFriendIds) < 2) {
|
||||
Log::error("重试建群好友数量不足。工作台ID: {$workbenchId}");
|
||||
$job->delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成群名称
|
||||
$existingGroupCount = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('status', 2) // 成功
|
||||
->group('groupId')
|
||||
->count();
|
||||
|
||||
$chatroomName = $existingGroupCount > 0
|
||||
? $config['groupNameTemplate'] . ($existingGroupCount + 1) . '群'
|
||||
: $config['groupNameTemplate'];
|
||||
|
||||
// 调用建群接口
|
||||
$createResult = $webSocket->CmdChatroomCreate([
|
||||
'chatroomName' => $chatroomName,
|
||||
'wechatFriendIds' => $createFriendIds,
|
||||
'wechatAccountId' => $wechatAccountId
|
||||
]);
|
||||
|
||||
// 更新记录状态为创建中
|
||||
Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('createTime', '>=', $createTime - 10)
|
||||
->where('createTime', '<=', $createTime + 10)
|
||||
->update([
|
||||
'status' => 1, // 创建中
|
||||
'createTime' => time() // 更新创建时间
|
||||
]);
|
||||
|
||||
// 创建新的轮询验证任务
|
||||
Queue::later(5, 'app\job\WorkbenchGroupCreateVerifyJob', [
|
||||
'workbenchId' => $workbenchId,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'createTime' => time(),
|
||||
'adminFriendIds' => $adminFriendIds,
|
||||
'poolUsers' => [], // 重试时暂时不传poolUsers,后续可以优化
|
||||
], 'default');
|
||||
|
||||
Log::info("重试建群任务已创建。工作台ID: {$workbenchId}, 微信账号ID: {$wechatAccountId}");
|
||||
|
||||
$job->delete();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Log::error("重试建群任务异常:{$e->getMessage()}");
|
||||
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
248
application/job/WorkbenchGroupCreateVerifyJob.php
Normal file
248
application/job/WorkbenchGroupCreateVerifyJob.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\api\controller\WechatChatroomController;
|
||||
use think\facade\Log;
|
||||
use think\Db;
|
||||
use think\queue\Job;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
use think\Queue;
|
||||
|
||||
/**
|
||||
* 工作台群创建验证任务(轮询群创建状态)
|
||||
* Class WorkbenchGroupCreateVerifyJob
|
||||
* @package app\job
|
||||
*/
|
||||
class WorkbenchGroupCreateVerifyJob
|
||||
{
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 15; // 最多轮询15次
|
||||
|
||||
/**
|
||||
* 轮询间隔(秒)
|
||||
*/
|
||||
const POLL_INTERVAL = 5;
|
||||
|
||||
/**
|
||||
* 状态常量
|
||||
*/
|
||||
const STATUS_CREATING = 1;
|
||||
const STATUS_SUCCESS = 2;
|
||||
const STATUS_FAILED = 3;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$workbenchId = $data['workbenchId'] ?? 0;
|
||||
$wechatAccountId = $data['wechatAccountId'] ?? 0;
|
||||
$createTime = $data['createTime'] ?? 0;
|
||||
$adminFriendIds = $data['adminFriendIds'] ?? [];
|
||||
$poolUsers = $data['poolUsers'] ?? [];
|
||||
|
||||
try {
|
||||
$attempts = $job->attempts();
|
||||
|
||||
// 查询待验证的群记录
|
||||
$groupItems = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('status', self::STATUS_CREATING)
|
||||
->where('createTime', '>=', $createTime - 10) // 允许10秒误差
|
||||
->where('createTime', '<=', $createTime + 10)
|
||||
->group('wechatAccountId')
|
||||
->select();
|
||||
|
||||
if (empty($groupItems)) {
|
||||
// 去除信息日志,减少日志空间消耗
|
||||
$job->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取微信账号信息
|
||||
$wechatAccount = Db::table('s2_wechat_account')->where('id', $wechatAccountId)->find();
|
||||
if (empty($wechatAccount)) {
|
||||
Log::error("未找到微信账号,任务失败。微信账号ID: {$wechatAccountId}");
|
||||
$job->delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 调用接口查询群聊列表
|
||||
$chatroomController = new WechatChatroomController();
|
||||
$chatroomList = $chatroomController->getlist([
|
||||
'wechatAccountKeyword' => $wechatAccount['wechatId'],
|
||||
'pageIndex' => 0,
|
||||
'pageSize' => 100
|
||||
], true);
|
||||
|
||||
$chatroomListData = json_decode($chatroomList, true);
|
||||
|
||||
if (empty($chatroomListData['data']['results'])) {
|
||||
// 如果超过最大重试次数,标记为失败并重试创建
|
||||
if ($attempts >= self::MAX_RETRY_ATTEMPTS) {
|
||||
$this->handleCreateFailed($workbenchId, $wechatAccountId, $createTime, $job);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 继续轮询
|
||||
$job->release(self::POLL_INTERVAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 查找符合条件的群(chatroomOwnerAvatar和chatroomOwnerNickname不为空)
|
||||
$successGroup = null;
|
||||
foreach ($chatroomListData['data']['results'] as $chatroom) {
|
||||
if (!empty($chatroom['chatroomOwnerAvatar']) && !empty($chatroom['chatroomOwnerNickname'])) {
|
||||
// 检查创建时间是否匹配(允许30秒误差)
|
||||
$chatroomCreateTime = isset($chatroom['createTime']) ? strtotime($chatroom['createTime']) : 0;
|
||||
if (abs($chatroomCreateTime - $createTime) <= 30) {
|
||||
$successGroup = $chatroom;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($successGroup) {
|
||||
// 群创建成功,更新记录状态
|
||||
$groupId = $successGroup['id'] ?? 0;
|
||||
$chatroomId = $successGroup['chatroomId'] ?? '';
|
||||
|
||||
// 更新管理员和群主成员的记录状态
|
||||
Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('status', self::STATUS_CREATING)
|
||||
->where('memberType', 'in', [1, 2]) // 群主成员和管理员
|
||||
->where('createTime', '>=', $createTime - 10)
|
||||
->where('createTime', '<=', $createTime + 10)
|
||||
->update([
|
||||
'status' => self::STATUS_SUCCESS,
|
||||
'groupId' => $groupId,
|
||||
'chatroomId' => $chatroomId,
|
||||
'verifyTime' => time()
|
||||
]);
|
||||
|
||||
// 去除成功日志,减少日志空间消耗
|
||||
|
||||
// 3. 拉群主好友进群(在验证成功后执行)
|
||||
$ownerFriendIds = $data['ownerFriendIds'] ?? [];
|
||||
if (!empty($ownerFriendIds)) {
|
||||
Queue::push('app\job\WorkbenchGroupCreateOwnerFriendJob', [
|
||||
'workbenchId' => $workbenchId,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'groupId' => $groupId,
|
||||
'chatroomId' => $chatroomId,
|
||||
'ownerFriendIds' => $ownerFriendIds,
|
||||
'createTime' => $createTime
|
||||
], 'default');
|
||||
}
|
||||
|
||||
// 5. 创建拉管理员好友的任务(在群主好友拉入后执行)
|
||||
if (!empty($adminFriendIds) && !empty($poolUsers)) {
|
||||
Queue::push('app\job\WorkbenchGroupCreateAdminFriendJob', [
|
||||
'workbenchId' => $workbenchId,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'groupId' => $groupId,
|
||||
'chatroomId' => $chatroomId,
|
||||
'adminFriendIds' => $adminFriendIds,
|
||||
'poolUsers' => $poolUsers
|
||||
], 'default');
|
||||
}
|
||||
|
||||
$job->delete();
|
||||
return true;
|
||||
} else {
|
||||
// 如果超过最大重试次数,标记为失败并重试创建
|
||||
if ($attempts >= self::MAX_RETRY_ATTEMPTS) {
|
||||
$this->handleCreateFailed($workbenchId, $wechatAccountId, $createTime, $job);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 继续轮询
|
||||
$job->release(self::POLL_INTERVAL);
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("群创建验证任务异常:{$e->getMessage()}");
|
||||
|
||||
if ($job->attempts() >= self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(self::POLL_INTERVAL);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理创建失败的情况(重试创建)
|
||||
* @param int $workbenchId 工作台ID
|
||||
* @param int $wechatAccountId 微信账号ID
|
||||
* @param int $createTime 创建时间
|
||||
* @param Job $job 队列任务
|
||||
*/
|
||||
protected function handleCreateFailed($workbenchId, $wechatAccountId, $createTime, $job)
|
||||
{
|
||||
// 更新状态为失败
|
||||
Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('status', self::STATUS_CREATING)
|
||||
->where('createTime', '>=', $createTime - 10)
|
||||
->where('createTime', '<=', $createTime + 10)
|
||||
->update([
|
||||
'status' => self::STATUS_FAILED,
|
||||
'verifyTime' => time()
|
||||
]);
|
||||
|
||||
Log::warning("群创建失败,准备重试。工作台ID: {$workbenchId}, 微信账号ID: {$wechatAccountId}");
|
||||
|
||||
// 检查重试次数
|
||||
$failedItems = Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('createTime', '>=', $createTime - 10)
|
||||
->where('createTime', '<=', $createTime + 10)
|
||||
->select();
|
||||
|
||||
$maxRetryCount = 0;
|
||||
foreach ($failedItems as $item) {
|
||||
if ($item['retryCount'] >= 3) {
|
||||
Log::error("群创建重试次数已达上限,放弃重试。工作台ID: {$workbenchId}, 微信账号ID: {$wechatAccountId}");
|
||||
$job->delete();
|
||||
return;
|
||||
}
|
||||
$maxRetryCount = max($maxRetryCount, $item['retryCount']);
|
||||
}
|
||||
|
||||
// 增加重试次数并重置状态
|
||||
Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->where('createTime', '>=', $createTime - 10)
|
||||
->where('createTime', '<=', $createTime + 10)
|
||||
->update([
|
||||
'status' => self::STATUS_CREATING,
|
||||
'retryCount' => Db::raw('retryCount + 1')
|
||||
]);
|
||||
|
||||
// 重新创建建群任务(延迟10秒)
|
||||
Queue::later(10, 'app\job\WorkbenchGroupCreateRetryJob', [
|
||||
'workbenchId' => $workbenchId,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'createTime' => $createTime
|
||||
], 'default');
|
||||
|
||||
$job->delete();
|
||||
}
|
||||
}
|
||||
|
||||
1045
application/job/WorkbenchGroupPushJob.php
Normal file
1045
application/job/WorkbenchGroupPushJob.php
Normal file
File diff suppressed because it is too large
Load Diff
399
application/job/WorkbenchImportContactJob.php
Normal file
399
application/job/WorkbenchImportContactJob.php
Normal file
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\api\controller\DeviceController;
|
||||
use app\cunkebao\model\Workbench;
|
||||
use app\cunkebao\model\WorkbenchImportContact;
|
||||
use think\facade\Log;
|
||||
use think\facade\Env;
|
||||
use think\Db;
|
||||
use think\queue\Job;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* 工作台通讯录导入任务
|
||||
* Class WorkbenchImportContactJob
|
||||
* @package app\job
|
||||
*/
|
||||
class WorkbenchImportContactJob
|
||||
{
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$jobId = $data['jobId'] ?? '';
|
||||
$queueLockKey = $data['queueLockKey'] ?? '';
|
||||
try {
|
||||
$this->logJobStart($jobId, $queueLockKey);
|
||||
$this->execute();
|
||||
$this->handleJobSuccess($job, $queueLockKey);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleJobError($e, $job, $queueLockKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
// 获取所有启用的通讯录导入工作台
|
||||
$workbenches = Workbench::where(['status' => 1, 'type' => 6, 'isDel' => 0])->order('id desc')->select();
|
||||
foreach ($workbenches as $workbench) {
|
||||
// 获取工作台配置
|
||||
$config = WorkbenchImportContact::where('workbenchId', $workbench->id)->find();
|
||||
if (!$config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 判断是否需要导入
|
||||
$shouldImport = $this->shouldImport($workbench, $config);
|
||||
if (!$shouldImport) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取需要导入的设备列表
|
||||
$devices = $this->getDeviceList($workbench, $config);
|
||||
if (empty($devices)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取通讯录数据
|
||||
$contactData = $this->getContactFromDatabase($workbench, $config);
|
||||
if (empty($contactData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 执行通讯录导入
|
||||
$this->importContactToDevices($workbench, $config, $devices, $contactData);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("通讯录导入任务异常: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入通讯录到设备
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchImportContact $config
|
||||
* @param array $devices
|
||||
* @param array $contactData
|
||||
*/
|
||||
public function importContactToDevices($workbench, $config, $devices, $contactData)
|
||||
{
|
||||
$deviceController = new DeviceController();
|
||||
|
||||
// 根据设备数量平分通讯录数据
|
||||
$deviceCount = count($devices);
|
||||
if ($deviceCount == 0) {
|
||||
Log::warning("没有可用设备进行通讯录导入");
|
||||
return;
|
||||
}
|
||||
|
||||
$contactCount = count($contactData);
|
||||
if ($contactCount == 0) {
|
||||
Log::warning("没有通讯录数据需要导入");
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算每个设备分配的联系人数量
|
||||
$contactsPerDevice = ceil($contactCount / $deviceCount);
|
||||
foreach ($devices as $index => $device) {
|
||||
try {
|
||||
// 计算当前设备的联系人数据范围
|
||||
$startIndex = $index * $contactsPerDevice;
|
||||
$endIndex = min($startIndex + $contactsPerDevice, $contactCount);
|
||||
|
||||
// 如果起始索引超出范围,跳过
|
||||
if ($startIndex >= $contactCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取当前设备的联系人数据片段
|
||||
$deviceContactData = array_slice($contactData, $startIndex, $endIndex - $startIndex);
|
||||
|
||||
if (empty($deviceContactData)) {
|
||||
continue;
|
||||
}
|
||||
// 准备联系人数据
|
||||
$contactJson = $this->formatContactData($deviceContactData, $config);
|
||||
|
||||
// 调用设备控制器的导入联系人方法
|
||||
$result = $deviceController->importContact([
|
||||
'deviceId' => $device['deviceId'],
|
||||
'contactJson' => $contactJson,
|
||||
'clearContact' => $config['clearContact'] ?? false
|
||||
], true);
|
||||
|
||||
$resultData = json_decode($result, true);
|
||||
|
||||
// 记录导入历史
|
||||
$this->recordImportHistory($workbench, $device, $deviceContactData);
|
||||
|
||||
if ($resultData['code'] == 200) {
|
||||
Log::info("设备 {$device['deviceId']} 通讯录导入成功,导入联系人数量: " . count($deviceContactData));
|
||||
} else {
|
||||
Log::error("设备 {$device['deviceId']} 通讯录导入失败: " . ($resultData['msg'] ?? '未知错误'));
|
||||
}
|
||||
|
||||
// 添加延迟,避免频繁请求
|
||||
if ($config['importInterval'] ?? 0 > 0) {
|
||||
sleep($config['importInterval']);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("设备 {$device['deviceId']} 通讯录导入异常: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化联系人数据
|
||||
* @param array $contactData
|
||||
* @param WorkbenchImportContact $config
|
||||
* @return array|string
|
||||
*/
|
||||
protected function formatContactData($contactData, $config)
|
||||
{
|
||||
$remarkType = $config['remarkType'] ?? 0;
|
||||
$remark = $config['remark'] ?? '';
|
||||
|
||||
// 根据remarkType添加备注
|
||||
$suffix = '';
|
||||
switch ($remarkType) {
|
||||
case 0:
|
||||
// 不添加备注
|
||||
$suffix = '';
|
||||
break;
|
||||
case 1:
|
||||
// 添加年月日
|
||||
$suffix = date('Ymd') . '_';
|
||||
break;
|
||||
case 2:
|
||||
// 添加月日
|
||||
$suffix = date('md') . '_';
|
||||
break;
|
||||
case 3:
|
||||
// 自定义备注
|
||||
$suffix = $remark . '_';
|
||||
break;
|
||||
default:
|
||||
$suffix = '';
|
||||
break;
|
||||
}
|
||||
// 返回数组格式
|
||||
$contacts = [];
|
||||
foreach ($contactData as $contact) {
|
||||
$name = !empty($contact['name']) ? trim($contact['name']) : trim($contact['phone']);
|
||||
if (!empty($suffix)) {
|
||||
$name = $suffix . $name;
|
||||
}
|
||||
$contacts[] = [
|
||||
'name' => $name,
|
||||
'phone' => trim($contact['phone'])
|
||||
];
|
||||
}
|
||||
return $contacts;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录导入历史
|
||||
* @param Workbench $workbench
|
||||
* @param array $device
|
||||
* @param array $contactData
|
||||
* @param array $result
|
||||
*/
|
||||
protected function recordImportHistory($workbench, $device, $contactData)
|
||||
{
|
||||
$data = [];
|
||||
foreach ($contactData as $v){
|
||||
$data[] = [
|
||||
'workbenchId' => $workbench->id,
|
||||
'deviceId' => $device['deviceId'],
|
||||
'packageId' => !empty($v['packageId']) ? $v['packageId'] : 0,
|
||||
'poolId' => !empty($v['id']) ? $v['id'] : 0,
|
||||
'createTime' => time(),
|
||||
];
|
||||
}
|
||||
Db::name('workbench_import_contact_item')->insertAll($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备列表
|
||||
* @param Workbench $workbench 工作台
|
||||
* @param WorkbenchImportContact $config 配置
|
||||
* @return array
|
||||
*/
|
||||
protected function getDeviceList($workbench, $config)
|
||||
{
|
||||
$deviceIds = json_decode($config['devices'], true);
|
||||
if (empty($deviceIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 从数据库获取设备信息
|
||||
$devices = Db::table('s2_device')
|
||||
->whereIn('id', $deviceIds)
|
||||
->where('isDeleted', 0)
|
||||
->where('alive', 1) // 只选择在线设备
|
||||
->field('id as deviceId, imei, nickname')
|
||||
->select();
|
||||
|
||||
return $devices;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要导入
|
||||
* @param Workbench $workbench 工作台
|
||||
* @param WorkbenchImportContact $config 配置
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldImport($workbench, $config)
|
||||
{
|
||||
// 检查导入间隔
|
||||
$today = date('Y-m-d');
|
||||
$startTimestamp = strtotime($today . ' ' . $config['startTime'] . ':00');
|
||||
$endTimestamp = strtotime($today . ' ' . $config['endTime'] . ':00');
|
||||
// 如果不在指定时间范围内,则跳过
|
||||
if ($startTimestamp > time() || $endTimestamp < time()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$maxPerDay = $config['num'];
|
||||
if ($maxPerDay <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 查询今日已导入次数
|
||||
$count = Db::name('workbench_import_contact_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->whereTime('createTime', 'between', [$startTimestamp, $endTimestamp])
|
||||
->count();
|
||||
|
||||
if ($count >= $maxPerDay) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算导入间隔
|
||||
$totalSeconds = $endTimestamp - $startTimestamp;
|
||||
$interval = floor($totalSeconds / $maxPerDay);
|
||||
$nextImportTime = $startTimestamp + $count * $interval;
|
||||
|
||||
if (time() < $nextImportTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 从数据库读取通讯录
|
||||
* @param WorkbenchImportContact $config
|
||||
* @return array
|
||||
*/
|
||||
protected function getContactFromDatabase($workbench,$config)
|
||||
{
|
||||
$pools = json_decode($config['pools'], true);
|
||||
$deviceIds = json_decode($config['devices'], true);
|
||||
if (empty($pools) || empty($deviceIds)) {
|
||||
return false;
|
||||
}
|
||||
$deviceNum = count($deviceIds);
|
||||
$contactNum = $deviceNum * $config['num'];
|
||||
if (empty($contactNum)) {
|
||||
return false;
|
||||
}
|
||||
//过滤已删除的数据
|
||||
$packageIds = Db::name('traffic_source_package')
|
||||
->where(['isDel' => 0])
|
||||
->whereIn('id', $pools)
|
||||
->column('id');
|
||||
|
||||
if (empty($packageIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = Db::name('traffic_source_package_item')->alias('tpi')
|
||||
->join('traffic_pool tp', 'tp.identifier = tpi.identifier')
|
||||
->join('traffic_source ts', 'ts.identifier = tpi.identifier','left')
|
||||
->join('workbench_import_contact_item wici', 'wici.poolId = tp.id AND wici.workbenchId = '.$workbench->id,'left')
|
||||
->where('tp.mobile', '>',0)
|
||||
->where('wici.id','null')
|
||||
->whereIn('tpi.packageId',$packageIds)
|
||||
->field('tp.id,tpi.packageId,tp.mobile as phone,ts.name')
|
||||
->order('tp.id DESC')
|
||||
->group('tpi.identifier')
|
||||
->limit($contactNum)
|
||||
->select();
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录任务开始
|
||||
* @param string $jobId
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function logJobStart($jobId, $queueLockKey)
|
||||
{
|
||||
Log::info('开始处理工作台通讯录导入任务: ' . json_encode([
|
||||
'jobId' => $jobId,
|
||||
'queueLockKey' => $queueLockKey
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务成功
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function handleJobSuccess($job, $queueLockKey)
|
||||
{
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info('工作台通讯录导入任务执行成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务错误
|
||||
* @param \Exception $e
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function handleJobError(\Exception $e, $job, $queueLockKey)
|
||||
{
|
||||
Log::error('工作台通讯录导入任务异常:' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
562
application/job/WorkbenchMomentsJob.php
Normal file
562
application/job/WorkbenchMomentsJob.php
Normal file
@@ -0,0 +1,562 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use app\api\controller\WebSocketController;
|
||||
use app\cunkebao\model\Workbench;
|
||||
use app\cunkebao\model\WorkbenchMomentsSync as WorkbenchMoments;
|
||||
use app\api\model\WechatFriendModel as WechatFriend;
|
||||
use app\api\model\WechatMomentsModel as WechatMoments;
|
||||
use think\facade\Log;
|
||||
use think\facade\Env;
|
||||
use think\Db;
|
||||
use think\queue\Job;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
use app\api\controller\MomentsController as Moments;
|
||||
use app\chukebao\model\KfMoments;
|
||||
|
||||
/**
|
||||
* 工作台朋友圈同步任务
|
||||
* Class WorkbenchMomentsJob
|
||||
* @package app\job
|
||||
*/
|
||||
class WorkbenchMomentsJob
|
||||
{
|
||||
/**
|
||||
* 内容类型映射
|
||||
* 0:未知 1:图片 2:链接 3:视频 4:文本 5:小程序 6:图文
|
||||
*/
|
||||
const CONTENT_TYPE_MAP = [
|
||||
0 => 1, // 未知 -> 文本
|
||||
1 => 2, // 图片 -> 图文
|
||||
2 => 4, // 链接 -> 链接
|
||||
3 => 3, // 视频 -> 视频
|
||||
4 => 1, // 文本 -> 文本
|
||||
5 => 1, // 小程序 -> 文本
|
||||
6 => 2, // 图文 -> 图文
|
||||
];
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$jobId = $data['jobId'] ?? '';
|
||||
$queueLockKey = $data['queueLockKey'] ?? '';
|
||||
try {
|
||||
$this->logJobStart($jobId, $queueLockKey);
|
||||
$this->execute2();
|
||||
$this->execute();
|
||||
$this->handleJobSuccess($job, $queueLockKey);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleJobError($e, $job, $queueLockKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
// 获取所有工作台
|
||||
$workbenches = Workbench::where(['status' => 1, 'type' => 2, 'isDel' => 0])->order('id desc')->select();
|
||||
foreach ($workbenches as $workbench) {
|
||||
// 获取工作台配置
|
||||
$config = WorkbenchMoments::where('workbenchId', $workbench->id)->find();
|
||||
if (!$config) {
|
||||
continue;
|
||||
}
|
||||
$startTime = strtotime(date('Y-m-d ' . $config['startTime']));
|
||||
$endTime = strtotime(date('Y-m-d ' . $config['endTime']));
|
||||
// 如果时间不符,则跳过
|
||||
if ($startTime > time() || $endTime < time()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取设备
|
||||
$devices = $this->getDevice($workbench, $config);
|
||||
if (empty($devices)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取内容库
|
||||
$contentLibrary = $this->getContentLibrary($workbench, $config);
|
||||
if (empty($contentLibrary)) {
|
||||
continue;
|
||||
}
|
||||
// 处理内容发送
|
||||
$this->handleContentSend($workbench, $config, $devices, $contentLibrary);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("朋友圈同步任务异常: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function execute2()
|
||||
{
|
||||
try {
|
||||
// 1) 每日重置
|
||||
$this->resetDailyCountersIfNeeded();
|
||||
|
||||
// 2) 获取发送窗口内的任务
|
||||
[$nowTs, $kfMoments] = $this->getWindowTasks();
|
||||
foreach ($kfMoments as $val) {
|
||||
$companyId = (int)($val['companyId'] ?? 0);
|
||||
$userId = (int)($val['userId'] ?? 0);
|
||||
|
||||
// 2.1) 数据规范化
|
||||
$sendData = json_decode($val->sendData, true);
|
||||
$sendData = $this->normalizeSendData($sendData);
|
||||
|
||||
// 2.2) 账号额度过滤
|
||||
$items = $sendData['jobPublishWechatMomentsItems'] ?? [];
|
||||
if (empty($items)) { continue; }
|
||||
$allowed = $this->filterAccountsByQuota($companyId, $userId, $items);
|
||||
if (empty($allowed)) { continue; }
|
||||
$sendData['jobPublishWechatMomentsItems'] = $allowed;
|
||||
|
||||
// 3) 下发
|
||||
$moments = new Moments();
|
||||
$res = $moments->addJob($sendData);
|
||||
$res = json_decode($res, true);
|
||||
if ($res['code'] == 200){
|
||||
KfMoments::where(['id' => $val['id']])->update(['isSend' => 1]);
|
||||
|
||||
// 4) 统计
|
||||
$this->incrementSendStats($companyId, $userId, $allowed);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("朋友圈同步任务异常: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
protected function resetDailyCountersIfNeeded()
|
||||
{
|
||||
$now = time();
|
||||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||||
if ($now - $todayStart >= 0 && $now - $todayStart <= 600) {
|
||||
$cacheKey = 'moments_settings_reset_' . date('Ymd');
|
||||
if (!Cache::has($cacheKey)) {
|
||||
Db::table('ck_kf_moments_settings')->where('sendNum', '<>', 0)
|
||||
->update(['sendNum' => 0, 'updateTime' => $now]);
|
||||
Cache::set($cacheKey, 1, 7200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getWindowTasks()
|
||||
{
|
||||
$nowTs = time();
|
||||
$windowStart = $nowTs - 300;
|
||||
$windowEnd = $nowTs + 300;
|
||||
$kfMoments = KfMoments::where(['isSend' => 0, 'isDel' => 0])
|
||||
->whereBetween('sendTime', [$windowStart, $windowEnd])
|
||||
->order('id desc')->select();
|
||||
return [$nowTs, $kfMoments];
|
||||
}
|
||||
|
||||
protected function normalizeSendData(array $sendData)
|
||||
{
|
||||
$endTime = strtotime($sendData['endTime'] ?? '');
|
||||
if ($endTime <= time() + 1800) {
|
||||
$endTime = time() + 3600;
|
||||
$sendData['endTime'] = date('Y-m-d H:i:s', $endTime);
|
||||
}
|
||||
switch ($sendData['momentContentType'] ?? 1) {
|
||||
case 1:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
case 2:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
case 3:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
break;
|
||||
case 4:
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
default:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
}
|
||||
return $sendData;
|
||||
}
|
||||
|
||||
protected function filterAccountsByQuota(int $companyId, int $userId, array $items)
|
||||
{
|
||||
$wechatIds = array_values(array_filter(array_map(function($it){ return (int)($it['wechatAccountId'] ?? 0); }, $items)));
|
||||
if (empty($wechatIds)) { return []; }
|
||||
$settings = Db::table('ck_kf_moments_settings')
|
||||
->where('companyId', $companyId)
|
||||
->where('userId', $userId)
|
||||
->whereIn('wechatId', $wechatIds)
|
||||
->column('id,max,sendNum', 'wechatId');
|
||||
$allowed = [];
|
||||
foreach ($items as $it) {
|
||||
$wid = (int)($it['wechatAccountId'] ?? 0);
|
||||
if ($wid <= 0) { continue; }
|
||||
if (isset($settings[$wid])) {
|
||||
$max = (int)$settings[$wid]['max'];
|
||||
$sent = (int)$settings[$wid]['sendNum'];
|
||||
if ($sent < ($max > 0 ? $max : 5)) { $allowed[] = $it; }
|
||||
} else {
|
||||
$allowed[] = $it;
|
||||
}
|
||||
}
|
||||
return $allowed;
|
||||
}
|
||||
|
||||
protected function incrementSendStats(int $companyId, int $userId, array $items)
|
||||
{
|
||||
try {
|
||||
$nowTs = time();
|
||||
foreach ($items as $it) {
|
||||
$wechatId = (int)($it['wechatAccountId'] ?? 0);
|
||||
if ($wechatId <= 0) { continue; }
|
||||
$cond = ['companyId' => $companyId, 'userId' => $userId, 'wechatId' => $wechatId];
|
||||
$setting = Db::table('ck_kf_moments_settings')->where($cond)->find();
|
||||
if ($setting) {
|
||||
Db::table('ck_kf_moments_settings')->where('id', $setting['id'])
|
||||
->update(['sendNum' => Db::raw('sendNum + 1'), 'updateTime' => $nowTs]);
|
||||
} else {
|
||||
Db::table('ck_kf_moments_settings')->insert([
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
'wechatId' => $wechatId,
|
||||
'max' => 5,
|
||||
'sendNum' => 1,
|
||||
'createTime' => $nowTs,
|
||||
'updateTime' => $nowTs,
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('朋友圈发送统计失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理内容发送
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchMoments $config
|
||||
* @param array $devices
|
||||
* @param array $contentLibrary
|
||||
*/
|
||||
protected function handleContentSend($workbench, $config, $devices, $contentLibrary)
|
||||
{
|
||||
// 准备评论数据
|
||||
$comment = [];
|
||||
if (!empty($contentLibrary['comment'])) {
|
||||
$comment[] = $contentLibrary['comment'];
|
||||
}
|
||||
|
||||
// 准备发送数据
|
||||
$jobPublishWechatMomentsItems = [];
|
||||
foreach ($devices as $device) {
|
||||
$jobPublishWechatMomentsItems[] = [
|
||||
'comments' => $comment,
|
||||
'labels' => [],
|
||||
'wechatAccountId' => $device['wechatAccountId']
|
||||
];
|
||||
}
|
||||
|
||||
// 转换内容类型
|
||||
$momentContentType = self::CONTENT_TYPE_MAP[$contentLibrary['contentType']] ?? 1;
|
||||
$sendTime = !empty($contentLibrary['sendTime']) ? $contentLibrary['sendTime'] : time();
|
||||
|
||||
// 图片url
|
||||
if ($momentContentType == 2) {
|
||||
$picUrlList = json_decode($contentLibrary['resUrls'], true);
|
||||
} else {
|
||||
$picUrlList = [];
|
||||
}
|
||||
|
||||
// 视频url
|
||||
if ($momentContentType == 3) {
|
||||
$videoUrl = json_decode($contentLibrary['urls'], true);
|
||||
$videoUrl = $videoUrl[0] ?? '';
|
||||
} else {
|
||||
$videoUrl = '';
|
||||
}
|
||||
|
||||
// 链接url
|
||||
if ($momentContentType == 4) {
|
||||
$urls = json_decode($contentLibrary['urls'], true);
|
||||
$url = $urls[0] ?? [];
|
||||
$link = [
|
||||
'desc' => $url['desc'] ?? '',
|
||||
'image' => $url['image'] ?? '',
|
||||
'url' => $url['url'] ?? ''
|
||||
];
|
||||
} else {
|
||||
$link = ['image' => ''];
|
||||
}
|
||||
|
||||
// 准备发送参数
|
||||
$data = [
|
||||
'altList' => '',
|
||||
'immediately' => false,
|
||||
'isUseLocation' => false,
|
||||
'jobPublishWechatMomentsItems' => $jobPublishWechatMomentsItems,
|
||||
'lat' => 0,
|
||||
'lng' => 0,
|
||||
'link' => $link,
|
||||
'momentContentType' => $momentContentType,
|
||||
'picUrlList' => $picUrlList,
|
||||
'poiAddress' => '',
|
||||
'poiName' => '',
|
||||
'publicMode' => '',
|
||||
'text' => !empty($contentLibrary['contentAi']) ? $contentLibrary['contentAi'] : $contentLibrary['content'],
|
||||
'timingTime' => date('Y-m-d H:i:s', $sendTime),
|
||||
'beginTime' => date('Y-m-d H:i:s', $sendTime),
|
||||
'endTime' => date('Y-m-d H:i:s', $sendTime + 3600),
|
||||
'videoUrl' => $videoUrl,
|
||||
];
|
||||
// 发送朋友圈
|
||||
$moments = new Moments();
|
||||
$res = $moments->addJob($data);
|
||||
$res = json_decode($res,true);
|
||||
if ($res['code'] == 200){
|
||||
// 记录发送记录
|
||||
$this->recordSendHistory($workbench, $devices, $contentLibrary);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 记录发送历史
|
||||
* @param Workbench $workbench
|
||||
* @param array $devices
|
||||
* @param array $contentLibrary
|
||||
*/
|
||||
protected function recordSendHistory($workbench, $devices, $contentLibrary)
|
||||
{
|
||||
$now = time();
|
||||
$data = [];
|
||||
foreach ($devices as $device) {
|
||||
$data = [
|
||||
'workbenchId' => $workbench->id,
|
||||
'deviceId' => $device['deviceId'],
|
||||
'contentId' => $contentLibrary['id'],
|
||||
'wechatAccountId' => $device['wechatAccountId'],
|
||||
'isLoop' => 0, // 初始状态为未完成循环
|
||||
'createTime' => $now,
|
||||
];
|
||||
Db::name('workbench_moments_sync_item')->insert($data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备列表
|
||||
* @param Workbench $workbench 工作台
|
||||
* @param WorkbenchMoments $config 配置
|
||||
* @return array|bool
|
||||
*/
|
||||
protected function getDevice($workbench, $config)
|
||||
{
|
||||
$devices = json_decode($config['devices'], true);
|
||||
if (empty($devices)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$list = Db::name('device')->alias('d')
|
||||
->join('device_wechat_login dw', 'dw.alive = 1 and dw.deviceId = d.id and dw.companyId = d.companyId')
|
||||
->join(['s2_wechat_account' => 'wa'], 'wa.wechatId = dw.wechatId')
|
||||
->where(['d.companyId' => $workbench->companyId, 'd.alive' => 1])
|
||||
->whereIn('d.id', $devices)
|
||||
->field('d.id as deviceId, d.memo as deviceName, d.companyId, dw.wechatId, wa.id as wechatAccountId')
|
||||
->select();
|
||||
|
||||
$newList = [];
|
||||
foreach ($list as $val) {
|
||||
// 检查发送间隔(新逻辑:根据startTime、endTime、syncCount动态计算)
|
||||
$today = date('Y-m-d');
|
||||
$startTimestamp = strtotime($today . ' ' . $config['startTime'] . ':00');
|
||||
$endTimestamp = strtotime($today . ' ' . $config['endTime'] . ':00');
|
||||
$totalSeconds = $endTimestamp - $startTimestamp;
|
||||
if ($totalSeconds <= 0 || empty($config['syncCount'])) {
|
||||
continue;
|
||||
}
|
||||
$interval = floor($totalSeconds / $config['syncCount']);
|
||||
|
||||
// 查询今日已同步次数
|
||||
$count = Db::name('workbench_moments_sync_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('deviceId', $val['deviceId'])
|
||||
->whereTime('createTime', 'between', [$startTimestamp, $endTimestamp])
|
||||
->count();
|
||||
|
||||
if ($count >= $config['syncCount']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算本次同步的最早允许时间
|
||||
$nextSyncTime = $startTimestamp + $count * $interval;
|
||||
if (time() < $nextSyncTime) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$newList[] = $val;
|
||||
}
|
||||
|
||||
return $newList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内容库
|
||||
* @param Workbench $workbench 工作台
|
||||
* @param WorkbenchMoments $config 配置
|
||||
* @return array|bool
|
||||
*/
|
||||
protected function getContentLibrary($workbench, $config)
|
||||
{
|
||||
$contentids = json_decode($config['contentLibraries'], true);
|
||||
// 清洗 contentids:去除 null/空字符串,并去重,保持原顺序
|
||||
if (is_array($contentids)) {
|
||||
$contentids = array_values(array_unique(array_filter($contentids, function ($v) {
|
||||
return $v !== null && $v !== '';
|
||||
})));
|
||||
} else {
|
||||
$contentids = [];
|
||||
}
|
||||
if (empty($contentids)) {
|
||||
return false;
|
||||
}
|
||||
// 基础查询
|
||||
$query = Db::name('content_library')->alias('cl')
|
||||
->join('content_item ci', 'ci.libraryId = cl.id')
|
||||
->where(['cl.isDel' => 0, 'ci.isDel' => 0])
|
||||
->whereIn('cl.id', $contentids)
|
||||
->field([
|
||||
'ci.id',
|
||||
'ci.libraryId',
|
||||
'ci.contentType',
|
||||
'ci.title',
|
||||
'ci.content',
|
||||
'ci.resUrls',
|
||||
'ci.urls',
|
||||
'ci.comment',
|
||||
'ci.sendTime'
|
||||
]);
|
||||
// 复制 query
|
||||
$query2 = clone $query;
|
||||
$query3 = clone $query;
|
||||
// 根据accountType处理不同的发送逻辑
|
||||
if ($config['accountType'] == 1) {
|
||||
// 可以循环发送
|
||||
// 1. 优先获取未发送的内容
|
||||
$unsentContent = $query2->join('workbench_moments_sync_item wmsi', 'wmsi.contentId = ci.id and wmsi.workbenchId = ' . $workbench->id, 'left')
|
||||
->where('wmsi.id', 'null')
|
||||
->where('ci.sendTime <= ' . (time() + 60))
|
||||
->order('ci.sendTime desc, ci.id desc')
|
||||
->find();
|
||||
|
||||
if (!empty($unsentContent)) {
|
||||
return $unsentContent;
|
||||
}
|
||||
|
||||
// 获取下一个要发送的内容(从内容库中查询,排除isLoop为0的数据)
|
||||
$isPushIds = Db::name('workbench_moments_sync_item')
|
||||
->where(['workbenchId' => $workbench->id, 'isLoop' => 0])
|
||||
->column('contentId');
|
||||
|
||||
if (empty($isPushIds)) {
|
||||
$isPushIds = [0];
|
||||
}
|
||||
$sentContent = $query3
|
||||
->whereNotIn('ci.id', $isPushIds)
|
||||
->group('ci.id')
|
||||
->order('ci.id asc')
|
||||
->find();
|
||||
// 4. 如果仍然没有内容,说明内容库为空,将所有记录的isLoop标记为1
|
||||
if (empty($sentContent)) {
|
||||
// 将所有该工作台的记录标记为循环完成
|
||||
Db::name('workbench_moments_sync_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('isLoop', 0)
|
||||
->update(['isLoop' => 1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $sentContent;
|
||||
} else {
|
||||
// 不能循环发送,只获取未发送的内容
|
||||
$list = $query2->join('workbench_moments_sync_item wmsi', 'wmsi.contentId = ci.id and wmsi.workbenchId = ' . $workbench->id, 'left')
|
||||
->where('wmsi.id', 'null')
|
||||
->order('ci.sendTime desc, ci.id desc')
|
||||
->find();
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录任务开始
|
||||
* @param string $jobId
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function logJobStart($jobId, $queueLockKey)
|
||||
{
|
||||
// 去除开始日志,减少日志空间消耗
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务成功
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function handleJobSuccess($job, $queueLockKey)
|
||||
{
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
// 去除成功日志,减少日志空间消耗
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务错误
|
||||
* @param \Exception $e
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function handleJobError(\Exception $e, $job, $queueLockKey)
|
||||
{
|
||||
Log::error('工作台朋友圈同步任务异常:' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
267
application/job/WorkbenchTrafficDistributeJob.php
Normal file
267
application/job/WorkbenchTrafficDistributeJob.php
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use think\facade\Cache;
|
||||
use app\cunkebao\model\Workbench;
|
||||
use app\cunkebao\model\WorkbenchTrafficConfig;
|
||||
use think\Db;
|
||||
use app\api\controller\AutomaticAssign;
|
||||
|
||||
class WorkbenchTrafficDistributeJob
|
||||
{
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$jobId = $data['jobId'] ?? '';
|
||||
$queueLockKey = $data['queueLockKey'] ?? '';
|
||||
try {
|
||||
$this->logJobStart($jobId, $queueLockKey);
|
||||
$workbenches = $this->getActiveWorkbenches();
|
||||
if (empty($workbenches)) {
|
||||
$this->handleEmptyWorkbenches($job, $queueLockKey);
|
||||
return true;
|
||||
}
|
||||
$this->processWorkbenches($workbenches);
|
||||
$this->handleJobSuccess($job, $queueLockKey);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleJobError($e, $job, $queueLockKey);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getActiveWorkbenches()
|
||||
{
|
||||
return Workbench::where([
|
||||
['status', '=', 1],
|
||||
['isDel', '=', 0],
|
||||
['type', '=', 5]
|
||||
])->order('id DESC')->select();
|
||||
}
|
||||
|
||||
protected function processWorkbenches($workbenches)
|
||||
{
|
||||
foreach ($workbenches as $workbench) {
|
||||
try {
|
||||
$this->processSingleWorkbench($workbench);
|
||||
} catch (\Exception $e) {
|
||||
Log::error("处理流量分发工作台 {$workbench->id} 失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function processSingleWorkbench($workbench)
|
||||
{
|
||||
$page = 1;
|
||||
$pageSize = 20;
|
||||
|
||||
$config = WorkbenchTrafficConfig::where('workbenchId', $workbench->id)->find();
|
||||
if (!$config) {
|
||||
Log::error("流量分发工作台 {$workbench->id} 配置获取失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证是否在流量分发时间范围内
|
||||
if (!$this->isTimeRange($config) && $config['timeType'] == 2) {
|
||||
return;
|
||||
}
|
||||
// 获取当天未超额的可用账号
|
||||
if(empty($config['account'])){
|
||||
Log::error("流量分发工作台 {$workbench->id} 未配置分发的客服");
|
||||
return;
|
||||
}
|
||||
$accountIds = json_decode($config['account'],true);
|
||||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||||
$todayEnd = strtotime(date('Y-m-d 23:59:59'));
|
||||
$accounts = Db::table('s2_company_account')
|
||||
->alias('a')
|
||||
->where(['a.departmentId' => $workbench->companyId, 'a.status' => 0])
|
||||
->whereIn('a.id',$accountIds)
|
||||
->whereNotLike('a.userName', '%_offline%')
|
||||
->whereNotLike('a.userName', '%_delete%')
|
||||
->leftJoin('workbench_traffic_config_item wti', "wti.wechatAccountId = a.id AND wti.workbenchId = {$workbench->id} AND wti.createTime BETWEEN {$todayStart} AND {$todayEnd}")
|
||||
->field('a.id,a.userName,a.realName,a.nickname,COUNT(wti.id) as todayCount')
|
||||
->group('a.id')
|
||||
->having('todayCount <= ' . $config['maxPerDay'])
|
||||
->select();
|
||||
$accountNum = count($accounts);
|
||||
if ($accountNum < 1) {
|
||||
Log::info("流量分发工作台 {$workbench->id} 可分配账号少于1个");
|
||||
return;
|
||||
}
|
||||
$automaticAssign = new AutomaticAssign();
|
||||
do {
|
||||
$friends = $this->getFriendsByLabels($workbench, $config, $page, $pageSize);
|
||||
|
||||
if (empty($friends) || count($friends) == 0) {
|
||||
Log::info("流量分发工作台 {$workbench->id} 没有可分配的好友");
|
||||
break;
|
||||
}
|
||||
$i = 0;
|
||||
$accountNum = count($accounts);
|
||||
foreach ($friends as $friend) {
|
||||
if ($accountNum == 0) {
|
||||
Log::info("流量分发工作台 {$workbench->id} 所有账号今日分配已满");
|
||||
break 2;
|
||||
}
|
||||
if ($i >= $accountNum) {
|
||||
$i = 0;
|
||||
}
|
||||
$account = $accounts[$i];
|
||||
|
||||
// 如果该账号今天分配的记录数加上本次分配的记录数超过最大限制
|
||||
if (($account['todayCount'] + $pageSize) >= $config['maxPerDay']) {
|
||||
// 查询该客服账号当天分配记录数
|
||||
$todayCount = Db::name('workbench_traffic_config_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->where('wechatAccountId', $account['id'])
|
||||
->whereBetween('createTime', [$todayStart, $todayEnd])
|
||||
->count();
|
||||
if ($todayCount >= $config['maxPerDay']) {
|
||||
unset($accounts[$i]);
|
||||
$accounts = array_values($accounts);
|
||||
$accountNum = count($accounts);
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行切换好友命令
|
||||
$res = $automaticAssign->allotWechatFriend([
|
||||
'wechatFriendId' => $friend['id'],
|
||||
'toAccountId' => $account['id']
|
||||
], true);
|
||||
|
||||
$res = json_decode($res,true);
|
||||
if ($res['code'] == 200){
|
||||
Db::table('s2_wechat_friend')
|
||||
->where('id',$friend['id'])
|
||||
->update([
|
||||
'accountId' => $account['id'],
|
||||
'accountUserName' => $account['userName'],
|
||||
'accountRealName' => $account['realName'],
|
||||
'accountNickname' => $account['nickname'],
|
||||
]);
|
||||
// 写入分配记录表
|
||||
Db::name('workbench_traffic_config_item')->insert([
|
||||
'workbenchId' => $workbench->id,
|
||||
'deviceId' => $friend['deviceId'],
|
||||
'wechatFriendId' => $friend['id'],
|
||||
'wechatAccountId' => $account['id'],
|
||||
'createTime' => time(),
|
||||
'exp' => $config['exp'],
|
||||
'expTime' => time() + 86400 * $config['exp'],
|
||||
]);
|
||||
- // 去除成功日志,减少日志空间消耗
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
break;
|
||||
$page++;
|
||||
} while (true);
|
||||
// 去除完成日志,减少日志空间消耗
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查是否在流量分发时间范围内
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @return bool
|
||||
*/
|
||||
protected function isTimeRange($config)
|
||||
{
|
||||
$currentTime = date('H:i');
|
||||
if ($currentTime < $config['startTime'] || $currentTime > $config['endTime']) {
|
||||
Log::info("当前时间 {$currentTime} 不在流量分发时间范围内 ({$config['startTime']} - {$config['endTime']})");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性查出所有包含指定标签数组的好友(支持分页)
|
||||
* @param object $workbench 工作台对象
|
||||
* @param object $config 配置对象
|
||||
* @param int $page 页码
|
||||
* @param int $pageSize 每页数量
|
||||
* @return array
|
||||
*/
|
||||
protected function getFriendsByLabels($workbench, $config, $page = 1, $pageSize = 20)
|
||||
{
|
||||
$labels = [];
|
||||
if (!empty($config['pools'])) {
|
||||
$labels = is_array($config['pools']) ? $config['pools'] : json_decode($config['pools'], true);
|
||||
}
|
||||
|
||||
$devices = [];
|
||||
if (!empty($config['devices'])) {
|
||||
$devices = is_array($config['devices']) ? $config['devices'] : json_decode($config['devices'], true);
|
||||
}
|
||||
if (empty($devices)) {
|
||||
return [];
|
||||
}
|
||||
$query = Db::table('s2_wechat_friend')->alias('wf')
|
||||
->join(['s2_company_account' => 'sa'], 'sa.id = wf.accountId', 'left')
|
||||
->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left')
|
||||
->join('workbench_traffic_config_item wtci', 'wtci.isRecycle = 0 and wtci.wechatFriendId = wf.id AND wtci.workbenchId = ' . $config['workbenchId'], 'left')
|
||||
->where([
|
||||
['wf.isDeleted', '=', 0],
|
||||
['wf.isPassed', '=', 1],
|
||||
//['sa.departmentId', '=', $workbench->companyId],
|
||||
['wtci.id', 'null', null]
|
||||
])
|
||||
->whereIn('wa.currentDeviceId', $devices)
|
||||
->field('wf.id,wf.wechatAccountId,wf.wechatId,wf.labels,sa.userName,wa.currentDeviceId as deviceId');
|
||||
|
||||
|
||||
if(!empty($labels)){
|
||||
$query->where(function ($q) use ($labels) {
|
||||
foreach ($labels as $label) {
|
||||
$q->whereOrRaw("JSON_CONTAINS(wf.labels, '\"{$label}\"')");
|
||||
}
|
||||
});
|
||||
}
|
||||
$list = $query->page($page, $pageSize)->order('wf.id DESC')->select();
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected function logJobStart($jobId, $queueLockKey)
|
||||
{
|
||||
// 去除开始日志,减少日志空间消耗
|
||||
}
|
||||
|
||||
protected function handleJobSuccess($job, $queueLockKey)
|
||||
{
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
// 去除成功日志,减少日志空间消耗
|
||||
}
|
||||
|
||||
protected function handleJobError(\Exception $e, $job, $queueLockKey)
|
||||
{
|
||||
Log::error('流量分发任务异常:' . $e->getMessage());
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function handleEmptyWorkbenches(Job $job, $queueLockKey)
|
||||
{
|
||||
Log::info('没有需要处理的流量分发任务');
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user