diff --git a/Server/application/command.php b/Server/application/command.php index ae098aaa..dae77762 100644 --- a/Server/application/command.php +++ b/Server/application/command.php @@ -28,4 +28,5 @@ return [ 'content:collect' => 'app\command\ContentCollectCommand', // 内容采集任务 √ 'moments:collect' => 'app\command\WechatMomentsCommand', // 朋友圈采集任务 'workbench:run' => 'app\command\WorkbenchCommand', // 工作台任务 + 'sync:wechatData' => 'app\command\SyncWechatDataToCkbTask', // 同步微信数据到存客宝 ]; diff --git a/Server/application/command/SyncWechatDataToCkbTask.php b/Server/application/command/SyncWechatDataToCkbTask.php new file mode 100644 index 00000000..e2032119 --- /dev/null +++ b/Server/application/command/SyncWechatDataToCkbTask.php @@ -0,0 +1,54 @@ +> /www/wwwroot/mckb_quwanzhi_com/Server/runtime/log/sync_wechat_data.log 2>&1 +class SyncWechatDataToCkbTask extends Command +{ + protected $lockFile = RUNTIME_PATH . 'sync_wechat_to_ckb.lock'; + + protected function execute(Input $input, Output $output) + { + // 检查锁文件 + if (file_exists($this->lockFile)) { + $lockTime = filectime($this->lockFile); + if (time() - $lockTime < 3600) { + Log::info('微信好友同步任务已在运行中,跳过本次执行'); + return false; + } + unlink($this->lockFile); + } + + file_put_contents($this->lockFile, time()); + + try { + $ChuKeBaoAdapter = new ChuKeBaoAdapter(); + $this->syncWechatAccount($ChuKeBaoAdapter); + $this->syncWechatFriend($ChuKeBaoAdapter); + return true; + } catch (\Exception $e) { + Log::error('微信好友同步任务异常:' . $e->getMessage()); + return false; + } finally { + if (file_exists($this->lockFile)) { + unlink($this->lockFile); + } + } + } + + protected function syncWechatFriend(ChuKeBaoAdapter $ChuKeBaoAdapter) + { + return $ChuKeBaoAdapter->syncFriendship(); + } + + protected function syncWechatAccount(ChuKeBaoAdapter $ChuKeBaoAdapter) + { + return $ChuKeBaoAdapter->syncWechatAccount(); + } +} \ No newline at end of file diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index 5720ce68..21a262a7 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -1,24 +1,26 @@ config = $config; - // $this->apiClient = new VendorAApiClient($config['api_key'], $config['api_secret'], $config['base_url']); + // $this->apiClient = new ChuKeBaoApiClient($config['api_key'], $config['api_secret'], $config['base_url']); // 校验配置等... - if (empty($config['api_key']) || empty($config['base_url'])) { - throw new \InvalidArgumentException("VendorA API key and base_url are required."); + if (empty($config['api_key']) || empty($config['username']) || empty($config['password'])) { + throw new \InvalidArgumentException("ChuKeBao username and password are required."); } } @@ -28,7 +30,8 @@ class Adapter implements WeChatServiceInterface $params = [ 'device_identifier' => $deviceId, 'wechat_user_to_add' => $targetWxId, - 'apiKey' => $this->config['api_key'], + 'username' => $this->config['username'], + 'password' => $this->config['password'], // ... 其他 VendorA 特定参数 ]; @@ -48,9 +51,7 @@ class Adapter implements WeChatServiceInterface if (!isset($responseData['code'])) { throw new ApiException("VendorA: Invalid API response for addFriend."); } -// if ($responseData['code'] === 1001) { // 假设1001是设备离线 -// throw new DeviceOfflineException("VendorA: Device {$deviceId} is offline."); -// } + if ($responseData['code'] !== 0) { throw new ApiException("VendorA: Failed to add friend - " . ($responseData['message'] ?? 'Unknown error')); } @@ -94,5 +95,106 @@ class Adapter implements WeChatServiceInterface return true; } - // ... 实现接口中的其他方法 -} \ No newline at end of file + /** + * 获取群成员列表 + * @param string $deviceId 设备ID + * @param string $chatroomId 群ID + * @return array 群成员列表 + */ + public function getChatroomMemberList(string $deviceId, string $chatroomId): array + { + echo "VendorA: Getting chatroom member list for device {$deviceId}, chatroom {$chatroomId}\n"; + return [ + ['id' => 'member1_va', 'nickname' => 'VendorA Member 1', 'avatar' => ''], + ]; + } + + /** + * 获取指定微信的朋友圈内容/列表 + * @param string $deviceId 设备ID + * @param string $wxId 微信ID + * @return array 朋友圈列表 + */ + public function getMomentList(string $deviceId, string $wxId): array + { + echo "VendorA: Getting moment list for device {$deviceId}, wxId {$wxId}\n"; + return [ + ['id' => 'moment1_va', 'content' => 'VendorA Moment 1', 'created_at' => time()], + ]; + } + + /** + * 发送微信朋友圈 + * @param string $deviceId 设备ID + * @param string $wxId 微信ID + * @param string $moment 朋友圈内容 + * @return bool 是否成功 + */ + public function sendMoment(string $deviceId, string $wxId, string $moment): bool + { + echo "VendorA: Sending moment for device {$deviceId}, wxId {$wxId}, content: {$moment}\n"; + return true; + } + + /* todo 以上方法待实现,基于/参考 application/api/controller/WebSocketController.php 去实现 */ + + // NOTE: run in background; 5min 同步一次 + // todo: 后续经过`s2_`表,直接对接三方的api去sync + public function syncFriendship() + { + $sql = "INSERT INTO ck_wechat_friendship(id,wechatId,tags,memo,ownerWechatId,createTime,updateTime,deleteTime,companyId) + SELECT + f.id,f.wechatId,f.labels as tags,f.conRemark as memo,f.ownerWechatId,f.createTime,f.updateTime,f.deleteTime, + c.departmentId + FROM s2_wechat_friend f + LEFT JOIN s2_wechat_account a on a.id = f.wechatAccountId + LEFT JOIN s2_company_account c on c.id = a.deviceAccountId + LIMIT ?, ? + ON DUPLICATE KEY UPDATE + id=VALUES(id), + tags=VALUES(tags), + memo=VALUES(memo), + updateTime=VALUES(updateTime), + deleteTime=VALUES(deleteTime), + companyId=VALUES(companyId)"; + + $offset = 0; + $limit = 2000; + $usleepTime = 100000; + + do { + $affected = Db::execute($sql, [$offset, $limit]); + $offset += $limit; + if ($affected > 0) { + usleep($usleepTime); + } + } while ($affected > 0); + } + + + public function syncWechatAccount() + { + $sql = "INSERT INTO ck_wechat_account(wechatId,alias,nickname,pyInitial,quanPin,avatar,gender,region,signature,phone,country,privince,city,createTime,updateTime) + SELECT + wechatId,alias,nickname,pyInitial,quanPin,avatar,gender,region,signature,phone,country,privince,city,createTime,updateTime + FROM + s2_wechat_friend GROUP BY wechatId; + ON DUPLICATE KEY UPDATE + alias=VALUES(alias), + nickname=VALUES(nickname), + pyInitial=VALUES(pyInitial), + quanPin=VALUES(quanPin), + avatar=VALUES(avatar), + gender=VALUES(gender), + region=VALUES(region), + signature=VALUES(signature), + phone=VALUES(phone), + country=VALUES(country), + privince=VALUES(privince), + city=VALUES(city), + updateTime=VALUES(updateTime);"; + + $affected = Db::execute($sql); + return $affected; + } +} diff --git a/Server/extend/WeChatDeviceApi/Contracts/WeChatServiceInterface.php b/Server/extend/WeChatDeviceApi/Contracts/WeChatServiceInterface.php index e35cc496..ecebdf30 100644 --- a/Server/extend/WeChatDeviceApi/Contracts/WeChatServiceInterface.php +++ b/Server/extend/WeChatDeviceApi/Contracts/WeChatServiceInterface.php @@ -9,7 +9,6 @@ interface WeChatServiceInterface * @param string $targetWxId 目标微信ID * @return bool 是否成功 * @throws \WeChatDeviceApi\Exceptions\ApiException -// * @throws \WeChatDeviceApi\Exceptions\DeviceOfflineException */ public function addFriend(string $deviceId, string $targetWxId): bool; @@ -26,7 +25,7 @@ interface WeChatServiceInterface * @param string $deviceId 设备ID * @return array 群信息列表 */ - public function getGroupList(string $deviceId): array; + public function getGroupList(string $wxId): array; /** * 获取好友列表 @@ -50,5 +49,19 @@ interface WeChatServiceInterface */ public function bindDeviceToCompany(string $deviceId, string $companyId): bool; + /** + * 获取群成员列表 + * @param string $deviceId 设备ID + * @param string $chatroomId 群ID + * @return array 群成员列表 + */ + public function getChatroomMemberList(string $deviceId, string $chatroomId): array; + + // 获取指定微信的朋友圈内容/列表 + public function getMomentList(string $deviceId, string $wxId): array; + + // 发送微信朋友圈 + public function sendMoment(string $deviceId, string $wxId, string $moment): bool; + // ... 其他方法定义 } \ No newline at end of file