update task server
This commit is contained in:
@@ -630,59 +630,44 @@ class WebSocketController extends BaseController
|
|||||||
* 个人消息发送
|
* 个人消息发送
|
||||||
* @return \think\response\Json
|
* @return \think\response\Json
|
||||||
*/
|
*/
|
||||||
public function sendPersonal()
|
public function sendPersonal(array $dataArray)
|
||||||
{
|
{
|
||||||
if ($this->request->isPost()) {
|
//过滤消息
|
||||||
$data = $this->request->param();
|
if (empty($dataArray['content'])) {
|
||||||
|
return json_encode(['code' => 400, 'msg' => '内容缺失']);
|
||||||
if (empty($data)) {
|
|
||||||
return json_encode(['code'=>400,'msg'=>'参数缺失']);
|
|
||||||
}
|
|
||||||
$dataArray = $data;
|
|
||||||
if (!is_array($dataArray)) {
|
|
||||||
return json_encode(['code'=>400,'msg'=>'数据格式错误']);
|
|
||||||
}
|
|
||||||
|
|
||||||
//过滤消息
|
|
||||||
if (empty($dataArray['content'])) {
|
|
||||||
return json_encode(['code'=>400,'msg'=>'内容缺失']);
|
|
||||||
}
|
|
||||||
if (empty($dataArray['wechatAccountId'])) {
|
|
||||||
return json_encode(['code'=>400,'msg'=>'微信id不能为空']);
|
|
||||||
}
|
|
||||||
if (empty($dataArray['wechatFriendId'])) {
|
|
||||||
return json_encode(['code'=>400,'msg'=>'接收人不能为空']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($dataArray['msgType'])) {
|
|
||||||
return json_encode(['code'=>400,'msg'=>'类型缺失']);
|
|
||||||
}
|
|
||||||
|
|
||||||
//消息拼接 msgType(1:文本 3:图片 43:视频 47:动图表情包 49:小程序)
|
|
||||||
$result = [
|
|
||||||
"cmdType" => "CmdSendMessage",
|
|
||||||
"content" => $dataArray['content'],
|
|
||||||
"msgSubType" => 0,
|
|
||||||
"msgType" => $dataArray['msgType'],
|
|
||||||
"seq" => time(),
|
|
||||||
"wechatAccountId" => $dataArray['wechatAccountId'],
|
|
||||||
"wechatChatroomId" => 0,
|
|
||||||
"wechatFriendId" => $dataArray['wechatFriendId'],
|
|
||||||
];
|
|
||||||
|
|
||||||
$result = json_encode($result);
|
|
||||||
$this->client->send($result);
|
|
||||||
$message = $this->client->receive();
|
|
||||||
$message = json_decode($message, 1);
|
|
||||||
//关闭WS链接
|
|
||||||
$this->client->close();
|
|
||||||
//Log::write('WS个人消息发送');
|
|
||||||
return json_encode(['code'=>200,'msg'=>'消息成功发送','data'=>$message]);
|
|
||||||
//return successJson($message, '消息成功发送');
|
|
||||||
} else {
|
|
||||||
return json_encode(['code'=>400,'msg'=>'非法请求']);
|
|
||||||
//return errorJson('非法请求');
|
|
||||||
}
|
}
|
||||||
|
if (empty($dataArray['wechatAccountId'])) {
|
||||||
|
return json_encode(['code' => 400, 'msg' => '微信id不能为空']);
|
||||||
|
}
|
||||||
|
if (empty($dataArray['wechatFriendId'])) {
|
||||||
|
return json_encode(['code' => 400, 'msg' => '接收人不能为空']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($dataArray['msgType'])) {
|
||||||
|
return json_encode(['code' => 400, 'msg' => '类型缺失']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 消息拼接 msgType(1:文本 3:图片 43:视频 47:动图表情包(gif、其他表情包) 49:小程序/其他:图文、文件)
|
||||||
|
// 当前,type 为文本、图片、动图表情包的时候,content为string, 其他情况为对象 {type: 'file/link/...', url: '', title: '', thunmbPath: '', desc: ''}
|
||||||
|
$result = [
|
||||||
|
"cmdType" => "CmdSendMessage",
|
||||||
|
"content" => $dataArray['content'],
|
||||||
|
"msgSubType" => 0,
|
||||||
|
"msgType" => $dataArray['msgType'],
|
||||||
|
"seq" => time(),
|
||||||
|
"wechatAccountId" => $dataArray['wechatAccountId'],
|
||||||
|
"wechatChatroomId" => 0,
|
||||||
|
"wechatFriendId" => $dataArray['wechatFriendId'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = json_encode($result);
|
||||||
|
$this->client->send($result);
|
||||||
|
$message = $this->client->receive();
|
||||||
|
$message = json_decode($message, 1);
|
||||||
|
//关闭WS链接
|
||||||
|
$this->client->close();
|
||||||
|
//Log::write('WS个人消息发送');
|
||||||
|
return $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use think\Db;
|
|||||||
use think\facade\Log;
|
use think\facade\Log;
|
||||||
use Workerman\Lib\Timer;
|
use Workerman\Lib\Timer;
|
||||||
use think\worker\Server;
|
use think\worker\Server;
|
||||||
|
use WeChatDeviceApi\Adapters\ChuKeBao\Adapter as ChuKeBaoAdapter;
|
||||||
|
|
||||||
class TaskServer extends Server
|
class TaskServer extends Server
|
||||||
{
|
{
|
||||||
@@ -30,8 +31,6 @@ class TaskServer extends Server
|
|||||||
Log::record("error $code $msg");
|
Log::record("error $code $msg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function onMessage($connection, $data) {}
|
public function onMessage($connection, $data) {}
|
||||||
|
|
||||||
public function onClose($connection) {}
|
public function onClose($connection) {}
|
||||||
@@ -43,80 +42,175 @@ class TaskServer extends Server
|
|||||||
|
|
||||||
$current_worker_id = $worker->id;
|
$current_worker_id = $worker->id;
|
||||||
|
|
||||||
// echo "current_worker_id: $current_worker_id\n";
|
|
||||||
|
|
||||||
$process_count_for_status_0 = self::PROCESS_COUNT - 1;
|
$process_count_for_status_0 = self::PROCESS_COUNT - 1;
|
||||||
|
|
||||||
|
$adapter = new ChuKeBaoAdapter();
|
||||||
|
|
||||||
// todo 临时测试,回头封装到类里调用,每个任务一个类
|
|
||||||
Timer::add(5, function () use ($current_worker_id, $process_count_for_status_0) {
|
// 只在一个进程里开这个定时器,处理指定任务
|
||||||
if ($current_worker_id == self::PROCESS_COUNT - 1) {
|
if ($current_worker_id == self::PROCESS_COUNT - 1) {
|
||||||
// TODO 专门检查添加后的情况,是否通过 ; 使用独立的进程和定时器处理,周期改为1min
|
|
||||||
|
// todo 封装为 handleFriendAddTaskWithStatusIsCreated() ; 重复代码进一步抽象
|
||||||
|
Timer::add(60, function () use($adapter) {
|
||||||
|
|
||||||
$tasks = Db::name('task_customer')
|
$tasks = Db::name('task_customer')
|
||||||
->where('status', 1)
|
->where('status', 1)
|
||||||
->limit(50)
|
->limit(50)
|
||||||
->select();
|
->select();
|
||||||
|
|
||||||
|
|
||||||
if ($tasks) {
|
if ($tasks) {
|
||||||
// TODO 检查是否添加成功,是否需要再次发送,然后,更新状态为2或3 ...
|
foreach ($tasks as $task) {
|
||||||
|
|
||||||
|
$task_id = $task['task_id'];
|
||||||
|
|
||||||
|
$task_info = $adapter->getCustomerAcquisitionTask($task_id);
|
||||||
|
|
||||||
|
if (empty($task_info['status']) || empty($task_info['reqConf']) || empty($task_info['reqConf']['devices'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($task['processed_wechat_ids'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$weChatIds = explode(',', $task['processed_wechat_ids']);
|
||||||
|
|
||||||
|
$passedWeChatId = '';
|
||||||
|
|
||||||
|
foreach ($weChatIds as $wechatId) {
|
||||||
|
|
||||||
|
// 先是否是好友,如果不是好友,先查询执行状态,看是否还能以及需要换账号继续添加,还是直接更新状态为3
|
||||||
|
// 如果添加成功,先更新为2,然后去发消息(先判断有无消息设置,发消息的log记录?)
|
||||||
|
if ($adapter->checkIfIsWeChatFriendByPhone($wechatId, $task['phone'])) {
|
||||||
|
$passedWeChatId = $wechatId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($passedWeChatId && !empty($task_info['msgConf'])) {
|
||||||
|
|
||||||
|
// 直接发消息,同时更新状态为 4(已通过-已发消息)
|
||||||
|
$wechatFriendRecord = $adapter->getWeChatAccoutIdAndFriendIdByWeChatIdAndFriendPhone($passedWeChatId, $task['phone']);
|
||||||
|
|
||||||
|
$msgConf = is_string($task_info['msgConf']) ? json_decode($task_info['msgConf'], 1) : $task_info['msgConf'];
|
||||||
|
|
||||||
|
$wechatFriendRecord && $adapter->sendMsgToFriend($wechatFriendRecord['id'], $wechatFriendRecord['wechatAccountId'], $msgConf);
|
||||||
|
|
||||||
|
|
||||||
|
Db::name('task_customer')
|
||||||
|
->where('id', $task['id'])
|
||||||
|
->update(['status' => 4, 'updated_at' => time()]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
foreach ($weChatIds as $wechatId) {
|
||||||
|
|
||||||
|
// 查询执行状态
|
||||||
|
$latestFriendTask = $adapter->getLatestFriendTaskByPhoneAndWeChatId($task['phone'], $wechatId);
|
||||||
|
if (empty($latestFriendTask)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 已经执行成功的话,直接break,同时更新对应task_customer的状态为2(添加成功)
|
||||||
|
if (isset($latestFriendTask['status']) && $latestFriendTask['status'] == 1) {
|
||||||
|
Db::name('task_customer')
|
||||||
|
->where('id', $task['id'])
|
||||||
|
->update(['status' => 2, 'updated_at' => time()]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo 判断处理执行失败的情况 status=2,根据 extra 的描述去处理;-- 可以先直接更新为失败,然后 extra =》fail_reason -- 因为有专门的任务会处理失败的
|
||||||
|
if (isset($latestFriendTask['status']) && $latestFriendTask['status'] == 2) {
|
||||||
|
Db::name('task_customer')
|
||||||
|
->where('id', $task['id'])
|
||||||
|
->update(['status' => 3, 'fail_reason' => $latestFriendTask['extra'] ?? '未知原因', 'updated_at' => time()]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
// 其他任务 -- 现在只用于处理场景获客的后置操作(处理 status = 0 的数据);可添加其他任务进来~
|
}
|
||||||
|
|
||||||
|
if ($current_worker_id < self::PROCESS_COUNT - 1) {
|
||||||
|
|
||||||
|
Timer::add(1, function () use ($current_worker_id, $process_count_for_status_0, $adapter) {
|
||||||
|
|
||||||
$tasks = Db::name('task_customer')
|
$tasks = Db::name('task_customer')
|
||||||
->where('status', 0)
|
->where('status', 0)
|
||||||
->whereRaw("id % $process_count_for_status_0 = {$current_worker_id}")
|
->whereRaw("id % $process_count_for_status_0 = {$current_worker_id}")
|
||||||
->limit(50)
|
->limit(50)
|
||||||
->select();
|
->select();
|
||||||
if ($tasks) {
|
if ($tasks) {
|
||||||
// ... 更新状态为1,然后处理,~~再更新为2或3~~ ...
|
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
|
|
||||||
// 查找 ck_customer_acquisition_task 表,是否存在该任务;然后拿到任务配置详情;童谣的任务id需要缓存信息不要重复查询
|
|
||||||
$task_id = $task['task_id'];
|
$task_id = $task['task_id'];
|
||||||
// 先读取缓存
|
|
||||||
$task_info = cache('task_info_' . $task_id);
|
|
||||||
if (!$task_info) {
|
|
||||||
$task_info = Db::name('customer_acquisition_task')
|
|
||||||
->where('id', $task_id)
|
|
||||||
->find();
|
|
||||||
|
|
||||||
if ($task_info) {
|
$task_info = $adapter->getCustomerAcquisitionTask($task_id);
|
||||||
cache('task_info_' . $task['task_id'], $task_info);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($task_info['status']) || empty($task_info['reqConf'])) {
|
if (empty($task_info['status']) || empty($task_info['reqConf']) || empty($task_info['reqConf']['devices'])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先更新状态为1
|
|
||||||
|
$wechatIdAccountIdMap = $adapter->getWeChatIdsAccountIdsMapByDeviceIds($task_info['reqConf']['devices']);
|
||||||
|
if (empty($wechatIdAccountIdMap)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$friendAddTaskCreated = false;
|
||||||
|
|
||||||
|
foreach ($wechatIdAccountIdMap as $wechatId => $accountId) {
|
||||||
|
|
||||||
|
|
||||||
|
// 是否已经是好友的判断,如果已经是好友,直接break; 但状态还是维持1,让另外一个进程处理发消息的逻辑
|
||||||
|
if ($adapter->checkIfIsWeChatFriendByPhone($wechatId, $task['phone'])) {
|
||||||
|
$task['processed_wechat_ids'] = $task['processed_wechat_ids'] . ',' . $wechatId; // 处理失败任务用,用于过滤已处理的微信号
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断时间间隔\时间段和最后一次的状态
|
||||||
|
$canCreateFriendAddTask = $adapter->checkIfCanCreateFriendAddTask($wechatId, $task_info['reqConf']);
|
||||||
|
if (empty($canCreateFriendAddTask)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断24h内加的好友数量,friend_task 先固定10个人 getLast24hAddedFriendsCount
|
||||||
|
$last24hAddedFriendsCount = $adapter->getLast24hAddedFriendsCount($wechatId);
|
||||||
|
if ($last24hAddedFriendsCount >= 10) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 采取乐观尝试的策略,假设第一个可以添加的人可以添加成功的; 回头再另外一个任务进程去判断
|
||||||
|
|
||||||
|
// 创建好友添加任务, 对接触客宝
|
||||||
|
$conf = array_merge($task_info['reqConf'], ['task_name' => $task_info['name']]);
|
||||||
|
$adapter->createFriendAddTask($accountId, $task['phone'], $conf);
|
||||||
|
$friendAddTaskCreated = true;
|
||||||
|
$task['processed_wechat_ids'] = $task['processed_wechat_ids'] . ',' . $wechatId; // 处理失败任务用,用于过滤已处理的微信号
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Db::name('task_customer')
|
Db::name('task_customer')
|
||||||
->where('id', $task['id'])
|
->where('id', $task['id'])
|
||||||
->update(['status' => 1]);
|
->update(['status' => $friendAddTaskCreated ? 1 : 3, 'fail_reason' => $friendAddTaskCreated ? '' : '所有账号不可添加', 'updated_at' => time()]); // ~~不用管,回头再添加再判断即可~~
|
||||||
|
// 失败一定是另一个进程/定时器在检查的
|
||||||
|
|
||||||
// todo 基于 $task['phone'] 和 $task_info['reqConf'] 进行处理
|
|
||||||
//通过conpany_id拿到设备/微信,判断这些微信的状态(是否在线,是否能加人)
|
|
||||||
|
|
||||||
|
|
||||||
// ~~// 更新状态为1 || 4~~
|
|
||||||
// // 更新状态为2
|
|
||||||
// Db::name('task_customer')
|
|
||||||
// ->where('id', $task['id'])
|
|
||||||
// ->update(['status' => 1]);
|
|
||||||
|
|
||||||
// 之后可能会更新为~~失败~~或者不处理 -- 失败一定是另一个进程/定时器在检查的
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,9 +187,10 @@ class AuthService
|
|||||||
/**
|
/**
|
||||||
* 获取系统授权信息,使用缓存存储10分钟
|
* 获取系统授权信息,使用缓存存储10分钟
|
||||||
*
|
*
|
||||||
|
* @param bool $useCache 是否使用缓存
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function getSystemAuthorization()
|
public static function getSystemAuthorization($useCache = true)
|
||||||
{
|
{
|
||||||
// 定义缓存键名
|
// 定义缓存键名
|
||||||
$cacheKey = 'system_authorization_token';
|
$cacheKey = 'system_authorization_token';
|
||||||
@@ -198,7 +199,7 @@ class AuthService
|
|||||||
$authorization = Cache::get($cacheKey);
|
$authorization = Cache::get($cacheKey);
|
||||||
//$authorization = '';
|
//$authorization = '';
|
||||||
// 如果缓存中没有或已过期,则重新获取
|
// 如果缓存中没有或已过期,则重新获取
|
||||||
if (empty($authorization)) {
|
if (empty($authorization) || !$useCache) {
|
||||||
try {
|
try {
|
||||||
// 从环境变量中获取API用户名和密码
|
// 从环境变量中获取API用户名和密码
|
||||||
$username = Env::get('api.username', '');
|
$username = Env::get('api.username', '');
|
||||||
|
|||||||
@@ -6,10 +6,16 @@ use WeChatDeviceApi\Contracts\WeChatServiceInterface;
|
|||||||
use WeChatDeviceApi\Exceptions\ApiException;
|
use WeChatDeviceApi\Exceptions\ApiException;
|
||||||
// 如果有 Client.php
|
// 如果有 Client.php
|
||||||
// use WeChatDeviceApi\Adapters\ChuKeBao\Client as ChuKeBaoApiClient;
|
// use WeChatDeviceApi\Adapters\ChuKeBao\Client as ChuKeBaoApiClient;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
use think\Db;
|
use think\Db;
|
||||||
use think\facade\Config;
|
use think\facade\Config;
|
||||||
use think\facade\Log;
|
use think\facade\Log;
|
||||||
|
use app\api\controller\FriendTaskController;
|
||||||
|
use app\common\service\AuthService;
|
||||||
|
use app\api\controller\WebSocketController;
|
||||||
|
|
||||||
class Adapter implements WeChatServiceInterface
|
class Adapter implements WeChatServiceInterface
|
||||||
{
|
{
|
||||||
@@ -141,7 +147,52 @@ class Adapter implements WeChatServiceInterface
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkIfIsWeChatFriend
|
// sendMsgToFriend 要处理计划任务
|
||||||
|
public function sendMsgToFriend(int $friendId, int $wechatAccountId, array $msgConf)
|
||||||
|
{
|
||||||
|
// todo 直接发消息,同时更新状态为 4(已通过-已发消息) application/api/controller/WebSocketController.php sendPersonal
|
||||||
|
// 消息拼接 msgType(1:文本 3:图片 43:视频 47:动图表情包(gif、其他表情包) 49:小程序/其他:图文、文件)
|
||||||
|
// 当前,type 为文本、图片、动图表情包的时候,content为string, 其他情况为对象 {type: 'file/link/...', url: '', title: '', thunmbPath: '', desc: ''}
|
||||||
|
// $result = [
|
||||||
|
// "content" => $dataArray['content'],
|
||||||
|
// "msgSubType" => 0,
|
||||||
|
// "msgType" => $dataArray['msgType'],
|
||||||
|
// "seq" => time(),
|
||||||
|
// "wechatAccountId" => $dataArray['wechatAccountId'],
|
||||||
|
// "wechatChatroomId" => 0,
|
||||||
|
// "wechatFriendId" => $dataArray['wechatFriendId'],
|
||||||
|
// ];
|
||||||
|
$wsController = new WebSocketController();
|
||||||
|
$wsController->sendPersonal([
|
||||||
|
'wechatFriendId' => $friendId,
|
||||||
|
'wechatAccountId' => $wechatAccountId,
|
||||||
|
'msgConf' => $msgConf,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCustomerAcquisitionTask
|
||||||
|
public function getCustomerAcquisitionTask($id) {
|
||||||
|
|
||||||
|
// 先读取缓存
|
||||||
|
$task_info = cache('task_info_' . $id);
|
||||||
|
if (!$task_info) {
|
||||||
|
$task_info = Db::name('customer_acquisition_task')
|
||||||
|
->where('id', $id)
|
||||||
|
->find();
|
||||||
|
|
||||||
|
if ($task_info) {
|
||||||
|
cache('task_info_' . $id, $task_info);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $task_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否是好友关系
|
||||||
public function checkIfIsWeChatFriendByPhone(string $wxId, string $phone): bool
|
public function checkIfIsWeChatFriendByPhone(string $wxId, string $phone): bool
|
||||||
{
|
{
|
||||||
if (empty($wxId) || empty($phone)) {
|
if (empty($wxId) || empty($phone)) {
|
||||||
@@ -162,7 +213,8 @@ class Adapter implements WeChatServiceInterface
|
|||||||
// ->where('phone', 'like', $phone . '%') // Match phone numbers starting with $phone
|
// ->where('phone', 'like', $phone . '%') // Match phone numbers starting with $phone
|
||||||
// ->order('createTime', 'desc') // Order by creation time as hinted
|
// ->order('createTime', 'desc') // Order by creation time as hinted
|
||||||
// ->find(); // Fetches the first matching record or null
|
// ->find(); // Fetches the first matching record or null
|
||||||
$friendRecord = Db::table('s2_wechat_friend')
|
// $friendRecord = Db::table('s2_wechat_friend')
|
||||||
|
$id = Db::table('s2_wechat_friend')
|
||||||
->where('ownerWechatId', $wxId)
|
->where('ownerWechatId', $wxId)
|
||||||
->where('phone', 'like', $phone . '%') // Match phone numbers starting with $phone
|
->where('phone', 'like', $phone . '%') // Match phone numbers starting with $phone
|
||||||
->order('createTime', 'desc') // Order by creation time as hinted
|
->order('createTime', 'desc') // Order by creation time as hinted
|
||||||
@@ -170,7 +222,8 @@ class Adapter implements WeChatServiceInterface
|
|||||||
->value('id');
|
->value('id');
|
||||||
|
|
||||||
// If a record is found, $friendRecord will not be empty.
|
// If a record is found, $friendRecord will not be empty.
|
||||||
return !empty($friendRecord);
|
// return !empty($friendRecord);
|
||||||
|
return (bool)$id;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Log the exception for diagnostics.
|
// Log the exception for diagnostics.
|
||||||
Log::error("Error in checkIfIsWeChatFriendByPhone (wxId: {$wxId}, phone: {$phone}): " . $e->getMessage());
|
Log::error("Error in checkIfIsWeChatFriendByPhone (wxId: {$wxId}, phone: {$phone}): " . $e->getMessage());
|
||||||
@@ -179,7 +232,22 @@ class Adapter implements WeChatServiceInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getWeChatFriendPassTimeByPhone
|
// getWeChatAccoutIdAndFriendIdByWeChatId
|
||||||
|
public function getWeChatAccoutIdAndFriendIdByWeChatIdAndFriendPhone(string $wechatId, string $phone): array
|
||||||
|
{
|
||||||
|
if (empty($wechatId) || empty($phone)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Db::table('s2_wechat_friend')
|
||||||
|
->where('ownerWechatId', $wechatId)
|
||||||
|
->where('phone', 'like', $phone . '%')
|
||||||
|
->field('id,wechatAccountId,passTime,createTime')
|
||||||
|
->find();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否已添加某手机号为好友并返回添加时间
|
||||||
public function getWeChatFriendPassTimeByPhone(string $wxId, string $phone): int
|
public function getWeChatFriendPassTimeByPhone(string $wxId, string $phone): int
|
||||||
{
|
{
|
||||||
if (empty($wxId) || empty($phone)) {
|
if (empty($wxId) || empty($phone)) {
|
||||||
@@ -205,8 +273,353 @@ class Adapter implements WeChatServiceInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询某个微信今天添加了多少个好友
|
||||||
|
* @param string $wechatId 微信ID
|
||||||
|
* @return int 好友数量
|
||||||
|
*/
|
||||||
|
public function getTodayAddedFriendsCount(string $wechatId): int
|
||||||
|
{
|
||||||
|
if (empty($wechatId)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$count = Db::table('s2_friend_task')
|
||||||
|
->where('wechatId', $wechatId)
|
||||||
|
->whereRaw("FROM_UNIXTIME(createTime, '%Y-%m-%d') = CURDATE()")
|
||||||
|
->count();
|
||||||
|
return (int)$count;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error("Error in getTodayAddedFriendsCount (wechatId: {$wechatId}): " . $e->getMessage());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* todo 以上方法待实现,基于/参考 application/api/controller/WebSocketController.php 去实现 */
|
/**
|
||||||
|
* 查询某个微信24小时内添加了多少个好友
|
||||||
|
* @param string $wechatId 微信ID
|
||||||
|
* @return int 好友数量
|
||||||
|
*/
|
||||||
|
public function getLast24hAddedFriendsCount(string $wechatId): int
|
||||||
|
{
|
||||||
|
if (empty($wechatId)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$twentyFourHoursAgo = time() - (24 * 60 * 60);
|
||||||
|
$count = Db::table('s2_friend_task')
|
||||||
|
->where('wechatId', $wechatId)
|
||||||
|
->where('createTime', '>=', $twentyFourHoursAgo)
|
||||||
|
->count();
|
||||||
|
return (int)$count;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error("Error in getLast24hAddedFriendsCount (wechatId: {$wechatId}): " . $e->getMessage());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询某个微信最新的一条添加好友任务记录
|
||||||
|
* @param string $wechatId 微信ID
|
||||||
|
* @return array|null 任务记录或null
|
||||||
|
*/
|
||||||
|
public function getLatestFriendTask(string $wechatId): ?array
|
||||||
|
{
|
||||||
|
if (empty($wechatId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$task = Db::table('s2_friend_task')
|
||||||
|
->where('wechatId', $wechatId)
|
||||||
|
->order('createTime', 'desc')
|
||||||
|
->find();
|
||||||
|
return $task;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error("Error in getLatestFriendTask (wechatId: {$wechatId}): " . $e->getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLatestFriendTaskByPhoneAndWeChatId
|
||||||
|
public function getLatestFriendTaskByPhoneAndWeChatId(string $phone, string $wechatId): array
|
||||||
|
{
|
||||||
|
if (empty($phone) || empty($wechatId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = Db::table('s2_friend_task')
|
||||||
|
->where('phone', $phone)
|
||||||
|
->where('wechatId', $wechatId)
|
||||||
|
->order('createTime', 'desc')
|
||||||
|
->find();
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最新的一条添加好友任务记录的创建时间
|
||||||
|
public function getLastCreateFriendTaskTime(string $wechatId): int
|
||||||
|
{
|
||||||
|
if (empty($wechatId)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$record = Db::table('s2_friend_task')
|
||||||
|
->where('wechatId', $wechatId)
|
||||||
|
->order('createTime', 'desc')
|
||||||
|
->find();
|
||||||
|
return $record['createTime'] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否能够加好友
|
||||||
|
public function checkIfCanCreateFriendAddTask(string $wechatId, $conf = []): bool
|
||||||
|
{
|
||||||
|
if (empty($wechatId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = $this->getLatestFriendTask($wechatId);
|
||||||
|
if (empty($record)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($conf['add_gap']) && isset($record['createTime']) && $record['createTime'] > time() - $conf['add_gap'] * 60) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// conf['allow_add_time_between']
|
||||||
|
if (!empty($conf['allow_add_time_between']) && count($conf['allow_add_time_between']) == 2) {
|
||||||
|
$currentTime = date('H:i');
|
||||||
|
$startTime = $conf['allow_add_time_between'][0];
|
||||||
|
$endTime = $conf['allow_add_time_between'][1];
|
||||||
|
|
||||||
|
// If current time is NOT between start and end time, return false
|
||||||
|
if ($currentTime >= $startTime && $currentTime <= $endTime) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($record['status'])) {
|
||||||
|
// if ($record['status'] == 1) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if ($record['status'] == 2) {
|
||||||
|
|
||||||
|
// todo 判断$record['extra'] 是否包含文字: 操作过于频繁;如果包含判断 updateTime 是否已经超过72min,updateTime是10位时间戳;如果包含指定文字且时间未超过72min,return false
|
||||||
|
if (isset($record['extra']) && strpos($record['extra'], '操作过于频繁') !== false) {
|
||||||
|
$updateTime = isset($record['updateTime']) ? (int)$record['updateTime'] : 0;
|
||||||
|
$now = time();
|
||||||
|
$diff = $now - $updateTime;
|
||||||
|
|
||||||
|
// Check if less than 72 minutes (72 * 60 = 4320 seconds) have passed
|
||||||
|
// if ($diff < 72 * 60) {
|
||||||
|
if ($diff < 24 * 60 * 60) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// $createTime = $record['createTime'];
|
||||||
|
// $now = time();
|
||||||
|
// $diff = $now - $createTime;
|
||||||
|
// if ($diff > 10 * 60) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取触客宝系统的客服微信账号id,用于后续微信相关操作
|
||||||
|
public function getWeChatAccountIdByWechatId(string $wechatId): string
|
||||||
|
{
|
||||||
|
if (empty($wechatId)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$record = Db::table('s2_wechat_account')
|
||||||
|
->where('wechatId', $wechatId)
|
||||||
|
->field('id')
|
||||||
|
->find();
|
||||||
|
return $record['id'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取在线的客服微信账号id列表
|
||||||
|
public function getOnlineWeChatAccountIdsByWechatIds(array $wechatIds): array
|
||||||
|
{
|
||||||
|
if (empty($wechatIds)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$records = Db::table('s2_wechat_account')
|
||||||
|
->where('deviceAlive', 1)
|
||||||
|
->where('wechatAlive', 1)
|
||||||
|
->where('wechatId', 'in', $wechatIds)
|
||||||
|
// ->field('id')
|
||||||
|
->field('id,wechatId')
|
||||||
|
// ->select();
|
||||||
|
->column('id', 'wechatId');
|
||||||
|
|
||||||
|
return $records;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getWeChatIdsByDeviceIds
|
||||||
|
// public function getWeChatIdsByDeviceIds(array $deviceIds): array
|
||||||
|
public function getWeChatIdsAccountIdsMapByDeviceIds(array $deviceIds): array
|
||||||
|
{
|
||||||
|
if (empty($deviceIds)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$records = Db::table('s2_wechat_account')
|
||||||
|
->where('deviceAlive', 1)
|
||||||
|
->where('currentDeviceId', 'in', $deviceIds)
|
||||||
|
// ->field('id,wechatId,currentDeviceId')
|
||||||
|
->field('id,wechatId')
|
||||||
|
// ->select();
|
||||||
|
->column('id,wechatId');
|
||||||
|
return $records;
|
||||||
|
}
|
||||||
|
|
||||||
|
// addFriendTaskApi
|
||||||
|
public function addFriendTaskApi(int $wechatAccountId, string $phone, string $message, string $remark, array $labels, $authorization = '') {
|
||||||
|
|
||||||
|
$authorization = $authorization ?: AuthService::getSystemAuthorization();
|
||||||
|
|
||||||
|
if (empty($authorization)) {
|
||||||
|
return [
|
||||||
|
'status_code' => 0,
|
||||||
|
'body' => null,
|
||||||
|
'error' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// $friendTaskController = new FriendTaskController();
|
||||||
|
// $friendTaskController->addFriendTask($wechatAccountId, $phone, $reqConf);
|
||||||
|
|
||||||
|
// todo 调用 application/api/controller/FriendTaskController.php: addFriendTask()
|
||||||
|
$params = [
|
||||||
|
'phone' => $phone,
|
||||||
|
'message' => $message,
|
||||||
|
'remark' => $remark,
|
||||||
|
// 'labels' => is_array($labels) ? $labels : [$labels],
|
||||||
|
'labels' => $labels,
|
||||||
|
'wechatAccountId' => $wechatAccountId
|
||||||
|
];
|
||||||
|
$client = new Client([
|
||||||
|
'base_uri' => $this->config['base_url'],
|
||||||
|
'timeout' => 30, // 设置超时时间,可以根据需要调整
|
||||||
|
]);
|
||||||
|
// 准备请求头
|
||||||
|
$headers = [
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'client' => 'system'
|
||||||
|
];
|
||||||
|
// 如果有授权信息,添加到请求头
|
||||||
|
// if (!empty($authorization)) {
|
||||||
|
// $headers['Authorization'] = 'bearer ' . $authorization;
|
||||||
|
// }
|
||||||
|
$headers['Authorization'] = 'bearer ' . $authorization;
|
||||||
|
try {
|
||||||
|
// 发送请求
|
||||||
|
$response = $client->request('POST', 'api/AddFriendByPhoneTask/add', [
|
||||||
|
'headers' => $headers,
|
||||||
|
'json' => $params, // Guzzle 会自动将数组转换为 JSON
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 获取状态码
|
||||||
|
$statusCode = $response->getStatusCode();
|
||||||
|
|
||||||
|
// 获取响应体并解析 JSON
|
||||||
|
$body = $response->getBody()->getContents();
|
||||||
|
$result = json_decode($body, true);
|
||||||
|
|
||||||
|
// 返回结果,包含状态码和响应体
|
||||||
|
return [
|
||||||
|
'status_code' => $statusCode,
|
||||||
|
'body' => $result
|
||||||
|
];
|
||||||
|
|
||||||
|
} catch (RequestException $e) {
|
||||||
|
// 处理请求异常,可以获取错误响应
|
||||||
|
if ($e->hasResponse()) {
|
||||||
|
$statusCode = $e->getResponse()->getStatusCode();
|
||||||
|
|
||||||
|
if ($statusCode == 401) {
|
||||||
|
$authorization = AuthService::getSystemAuthorization(false);
|
||||||
|
return $this->addFriendTaskApi($wechatAccountId, $phone, $message, $remark, $labels, $authorization);
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $e->getResponse()->getBody()->getContents();
|
||||||
|
$result = json_decode($body, true);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status_code' => $statusCode,
|
||||||
|
'body' => $result,
|
||||||
|
'error' => true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::error("Error in addFriendTaskApi (wechatAccountId: {$wechatAccountId}, phone: {$phone}, message: {$message}, remark: {$remark}, labels: " . json_encode($labels) . "): " . $e->getMessage());
|
||||||
|
|
||||||
|
// 没有响应的异常
|
||||||
|
return [
|
||||||
|
'status_code' => 0,
|
||||||
|
'body' => null,
|
||||||
|
'error' => true,
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
];
|
||||||
|
} catch (GuzzleException $e) {
|
||||||
|
// 处理其他 Guzzle 异常
|
||||||
|
return [
|
||||||
|
'status_code' => 0,
|
||||||
|
'body' => null,
|
||||||
|
'error' => true,
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createFriendAddTask $accountId, $task['phone'], $task_info['reqConf'] -hello_msg,remark_type
|
||||||
|
// public function createFriendAddTask(int $wechatAccountId, string $phone, array $conf): bool
|
||||||
|
public function createFriendAddTask(int $wechatAccountId, string $phone, array $conf)
|
||||||
|
{
|
||||||
|
if (empty($wechatAccountId) || empty($phone) || empty($conf)) {
|
||||||
|
// return false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $remark = '';
|
||||||
|
// if (isset($conf['remark_type']) && $conf['remark_type'] == 'phone') {
|
||||||
|
// $remark = $phone . '-' . $conf['task_name'] ?? '获客';
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
// }
|
||||||
|
$remark = $phone . '-' . $conf['task_name'] ?? '获客';
|
||||||
|
|
||||||
|
$tags = [];
|
||||||
|
if (!empty($conf['tags'])) {
|
||||||
|
if (is_array($conf['tags'])) {
|
||||||
|
$tags = $conf['tags'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($conf['tags'], ',') !== false) {
|
||||||
|
$tags = explode(',', $conf['tags']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// $res = $this->addFriendTaskApi($wechatAccountId, $phone, $conf['hello_msg'] ?? '你好', $remark, $conf['tags'] ?? []);
|
||||||
|
$this->addFriendTaskApi($wechatAccountId, $phone, $conf['hello_msg'] ?? '你好', $remark, $tags);
|
||||||
|
|
||||||
|
// if ($res['status_code']) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: 以上方法待实现,基于/参考 application/api/controller/WebSocketController.php 去实现;以下同步脚本用的方法转移到其他类 */
|
||||||
|
|
||||||
|
|
||||||
// NOTE: run in background; 5min 同步一次
|
// NOTE: run in background; 5min 同步一次
|
||||||
|
|||||||
Reference in New Issue
Block a user