sync wechat data

This commit is contained in:
xavier
2025-05-12 21:18:19 +08:00
parent a34c9bc0c7
commit eb2b7a6f39
4 changed files with 184 additions and 14 deletions

View File

@@ -28,4 +28,5 @@ return [
'content:collect' => 'app\command\ContentCollectCommand', // 内容采集任务 √ 'content:collect' => 'app\command\ContentCollectCommand', // 内容采集任务 √
'moments:collect' => 'app\command\WechatMomentsCommand', // 朋友圈采集任务 'moments:collect' => 'app\command\WechatMomentsCommand', // 朋友圈采集任务
'workbench:run' => 'app\command\WorkbenchCommand', // 工作台任务 'workbench:run' => 'app\command\WorkbenchCommand', // 工作台任务
'sync:wechatData' => 'app\command\SyncWechatDataToCkbTask', // 同步微信数据到存客宝
]; ];

View File

@@ -0,0 +1,54 @@
<?php
namespace app\command;
use think\facade\Log;
use think\console\Input;
use think\console\Output;
use think\console\Command;
use WeChatDeviceApi\Adapters\ChuKeBao\Adapter as ChuKeBaoAdapter;
// */7 * * * * cd /www/wwwroot/mckb_quwanzhi_com/Server && php think sync:wechatData >> /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();
}
}

View File

@@ -1,24 +1,26 @@
<?php <?php
namespace WeChatDeviceApi\Adapters\ChuKeBao; namespace WeChatDeviceApi\Adapters\ChuKeBao;
use WeChatDeviceApi\Contracts\WeChatServiceInterface; use WeChatDeviceApi\Contracts\WeChatServiceInterface;
use WeChatDeviceApi\Exceptions\ApiException; use WeChatDeviceApi\Exceptions\ApiException;
//use WeChatDeviceApi\Exceptions\DeviceOfflineException;
// 如果有 Client.php // 如果有 Client.php
// use WeChatDeviceApi\Adapters\VendorA\Client as VendorAApiClient; // use WeChatDeviceApi\Adapters\ChuKeBao\Client as ChuKeBaoApiClient;
use think\Db;
class Adapter implements WeChatServiceInterface class Adapter implements WeChatServiceInterface
{ {
protected $config; protected $config;
// protected $apiClient; // 如果使用 VendorAApiClient // protected $apiClient; // 如果使用 VendorAApiClient
public function __construct(array $config) public function __construct(array $config = [])
{ {
$this->config = $config; $this->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'])) { if (empty($config['api_key']) || empty($config['username']) || empty($config['password'])) {
throw new \InvalidArgumentException("VendorA API key and base_url are required."); throw new \InvalidArgumentException("ChuKeBao username and password are required.");
} }
} }
@@ -28,7 +30,8 @@ class Adapter implements WeChatServiceInterface
$params = [ $params = [
'device_identifier' => $deviceId, 'device_identifier' => $deviceId,
'wechat_user_to_add' => $targetWxId, 'wechat_user_to_add' => $targetWxId,
'apiKey' => $this->config['api_key'], 'username' => $this->config['username'],
'password' => $this->config['password'],
// ... 其他 VendorA 特定参数 // ... 其他 VendorA 特定参数
]; ];
@@ -48,9 +51,7 @@ class Adapter implements WeChatServiceInterface
if (!isset($responseData['code'])) { if (!isset($responseData['code'])) {
throw new ApiException("VendorA: Invalid API response for addFriend."); 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) { if ($responseData['code'] !== 0) {
throw new ApiException("VendorA: Failed to add friend - " . ($responseData['message'] ?? 'Unknown error')); throw new ApiException("VendorA: Failed to add friend - " . ($responseData['message'] ?? 'Unknown error'));
} }
@@ -94,5 +95,106 @@ class Adapter implements WeChatServiceInterface
return true; return true;
} }
// ... 实现接口中的其他方法 /**
} * 获取群成员列表
* @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;
}
}

View File

@@ -9,7 +9,6 @@ interface WeChatServiceInterface
* @param string $targetWxId 目标微信ID * @param string $targetWxId 目标微信ID
* @return bool 是否成功 * @return bool 是否成功
* @throws \WeChatDeviceApi\Exceptions\ApiException * @throws \WeChatDeviceApi\Exceptions\ApiException
// * @throws \WeChatDeviceApi\Exceptions\DeviceOfflineException
*/ */
public function addFriend(string $deviceId, string $targetWxId): bool; public function addFriend(string $deviceId, string $targetWxId): bool;
@@ -26,7 +25,7 @@ interface WeChatServiceInterface
* @param string $deviceId 设备ID * @param string $deviceId 设备ID
* @return array 群信息列表 * @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; 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;
// ... 其他方法定义 // ... 其他方法定义
} }