diff --git a/Server/application/api/controller/WebSocketController.php b/Server/application/api/controller/WebSocketController.php index 37978712..0119808c 100644 --- a/Server/application/api/controller/WebSocketController.php +++ b/Server/application/api/controller/WebSocketController.php @@ -630,59 +630,44 @@ class WebSocketController extends BaseController * 个人消息发送 * @return \think\response\Json */ - public function sendPersonal() + public function sendPersonal(array $dataArray) { - if ($this->request->isPost()) { - $data = $this->request->param(); - - 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['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:动图表情包(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; } /** diff --git a/Server/application/common/TaskServer.php b/Server/application/common/TaskServer.php index d833c652..2fb2aaad 100644 --- a/Server/application/common/TaskServer.php +++ b/Server/application/common/TaskServer.php @@ -6,6 +6,7 @@ use think\Db; use think\facade\Log; use Workerman\Lib\Timer; use think\worker\Server; +use WeChatDeviceApi\Adapters\ChuKeBao\Adapter as ChuKeBaoAdapter; class TaskServer extends Server { @@ -30,8 +31,6 @@ class TaskServer extends Server Log::record("error $code $msg"); } - - public function onMessage($connection, $data) {} public function onClose($connection) {} @@ -43,80 +42,175 @@ class TaskServer extends Server $current_worker_id = $worker->id; - // echo "current_worker_id: $current_worker_id\n"; - $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) { - // TODO 专门检查添加后的情况,是否通过 ; 使用独立的进程和定时器处理,周期改为1min + + // 只在一个进程里开这个定时器,处理指定任务 + if ($current_worker_id == self::PROCESS_COUNT - 1) { + + // todo 封装为 handleFriendAddTaskWithStatusIsCreated() ; 重复代码进一步抽象 + Timer::add(60, function () use($adapter) { $tasks = Db::name('task_customer') ->where('status', 1) ->limit(50) ->select(); + + 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') ->where('status', 0) ->whereRaw("id % $process_count_for_status_0 = {$current_worker_id}") ->limit(50) ->select(); if ($tasks) { - // ... 更新状态为1,然后处理,~~再更新为2或3~~ ... + foreach ($tasks as $task) { - // 查找 ck_customer_acquisition_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) { - cache('task_info_' . $task['task_id'], $task_info); - } else { - continue; - } - } + $task_info = $adapter->getCustomerAcquisitionTask($task_id); - if (empty($task_info['status']) || empty($task_info['reqConf'])) { + if (empty($task_info['status']) || empty($task_info['reqConf']) || empty($task_info['reqConf']['devices'])) { 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') - ->where('id', $task['id']) - ->update(['status' => 1]); - - - // todo 基于 $task['phone'] 和 $task_info['reqConf'] 进行处理 - //通过conpany_id拿到设备/微信,判断这些微信的状态(是否在线,是否能加人) - - - // ~~// 更新状态为1 || 4~~ - // // 更新状态为2 - // Db::name('task_customer') - // ->where('id', $task['id']) - // ->update(['status' => 1]); - - // 之后可能会更新为~~失败~~或者不处理 -- 失败一定是另一个进程/定时器在检查的 - - + ->where('id', $task['id']) + ->update(['status' => $friendAddTaskCreated ? 1 : 3, 'fail_reason' => $friendAddTaskCreated ? '' : '所有账号不可添加', 'updated_at' => time()]); // ~~不用管,回头再添加再判断即可~~ + // 失败一定是另一个进程/定时器在检查的 } } - } - }); + }); + } + } } diff --git a/Server/application/common/service/AuthService.php b/Server/application/common/service/AuthService.php index 5789b381..aac5fbd1 100644 --- a/Server/application/common/service/AuthService.php +++ b/Server/application/common/service/AuthService.php @@ -187,9 +187,10 @@ class AuthService /** * 获取系统授权信息,使用缓存存储10分钟 * + * @param bool $useCache 是否使用缓存 * @return string */ - public static function getSystemAuthorization() + public static function getSystemAuthorization($useCache = true) { // 定义缓存键名 $cacheKey = 'system_authorization_token'; @@ -198,7 +199,7 @@ class AuthService $authorization = Cache::get($cacheKey); //$authorization = ''; // 如果缓存中没有或已过期,则重新获取 - if (empty($authorization)) { + if (empty($authorization) || !$useCache) { try { // 从环境变量中获取API用户名和密码 $username = Env::get('api.username', ''); diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index 5b555127..0762aed1 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -6,10 +6,16 @@ use WeChatDeviceApi\Contracts\WeChatServiceInterface; use WeChatDeviceApi\Exceptions\ApiException; // 如果有 Client.php // 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\facade\Config; use think\facade\Log; +use app\api\controller\FriendTaskController; +use app\common\service\AuthService; +use app\api\controller\WebSocketController; class Adapter implements WeChatServiceInterface { @@ -141,7 +147,52 @@ class Adapter implements WeChatServiceInterface 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 { if (empty($wxId) || empty($phone)) { @@ -162,7 +213,8 @@ class Adapter implements WeChatServiceInterface // ->where('phone', 'like', $phone . '%') // Match phone numbers starting with $phone // ->order('createTime', 'desc') // Order by creation time as hinted // ->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('phone', 'like', $phone . '%') // Match phone numbers starting with $phone ->order('createTime', 'desc') // Order by creation time as hinted @@ -170,7 +222,8 @@ class Adapter implements WeChatServiceInterface ->value('id'); // If a record is found, $friendRecord will not be empty. - return !empty($friendRecord); + // return !empty($friendRecord); + return (bool)$id; } catch (\Exception $e) { // Log the exception for diagnostics. 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 { 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 同步一次