xiaoai/php_server/app/common/traits/WechatTrait.php

414 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace app\common\traits;
use Channel\Client;
use app\common\workerman\wechat\handlers\client\TalkToFriendTaskHandler;
use app\common\workerman\wechat\handlers\client\AcceptFriendAddRequestTaskHandler;
use app\common\workerman\wechat\handlers\client\PostSNSNewsTaskHandler;
use app\common\workerman\wechat\constants\ResponseCode;
use app\common\workerman\wechat\traits\{ DeviceTrait, CacheTrait};
use Jubo\JuLiao\IM\Wx\Proto\TransportMessage;
use Google\Protobuf\Any;
use think\facade\Log;
use Predis\Client as redisClient;
use GuzzleHttp\Client as httpClient;
/**
* 微信操作能力
* @author Qasim
* @package app\traits
*/
trait WechatTrait
{
use DeviceTrait, CacheTrait;
/**
* 推送消息
* @param array $params
* @return array
* @throws \Exception
*/
protected static function wxPush(array $params): array
{
try {
// self::setLog([
// 'title' => 'Send message Device push',
// 'params' => $params,
// ]);
// 1. 参数验证
if (!isset($params['wechat_id']) || !isset($params['friend_id']) || !isset($params['message']) || !isset($params['device_code'])) {
throw new \Exception(ResponseCode::getMessage(ResponseCode::INVALID_PARAMS), ResponseCode::INVALID_PARAMS);
}
// 2. 验证设备是否存在
if (!self::checkDevice($params['device_code'])) {
throw new \Exception(ResponseCode::getMessage(ResponseCode::DEVICE_NOT_FOUND), ResponseCode::DEVICE_NOT_FOUND);
}
$deviceId = $params['device_code'];
$data = [
'WeChatId' => $params['wechat_id'],
'FriendId' => $params['friend_id'],
'Content' => $params['message'],
'ContentType' => $params['message_type'] ?? 1,
'Remark' => $params['remark'] ?? '',
'MsgId' => time(),
'Immediate' => true,
'OptType' => $params['opt_type'] ?? 'send'
];
$key = "push:device:{$deviceId}";
self::redis()->lpush($key, json_encode($data, JSON_UNESCAPED_UNICODE));
return [
'code' => 10000,
'message' => 'success'
];
} catch (\Throwable $th) {
self::setLog([
'title' => 'Send message Device push',
'params' => $params,
'error' => $th->getMessage(),
'code' => $th->getCode(),
'trace' => $th->getTraceAsString()
], 'error');
//throw new \Exception($th->getMessage(), $th->getCode());
return [
'code' => $th->getCode(),
'message' => $th->getMessage()
];
}
}
/**
* 微信设备在线状态
* @param array $params
* @return array
*/
public static function wxOnline(array $params): array
{
try {
// self::setLog([
// 'title' => 'Send message Device online',
// 'params' => $params,
// ]);
// 1. 参数验证
if (!isset($params['wechat_id']) || !isset($params['device_code']) || !isset($params['type'])) {
throw new \Exception(ResponseCode::getMessage(ResponseCode::INVALID_PARAMS), ResponseCode::INVALID_PARAMS);
}
$wechatId = $params['wechat_id'];
$deviceId = $params['device_code'];
$type = $params['type'];
if($type == 1){
$online = self::isDeviceOnline($deviceId);
$content = [
'online_status' => $online ? 1 : 0
];
}else{
// 获取微信信息
$key = self::getWechatKey($deviceId, $wechatId);
$content = self::redis()->hGetAll($key) ?: [];
$content['online_status'] = (isset($content['Status']) && $content['Status'] == 'online') ? 1 : 0;
}
return [
'code' => 10000,
'message' => 'success',
'data' => $content
];
}catch (\Throwable $th) {
self::setLog([
'title' => 'Send message Device online',
'params' => $params,
'error' => $th->getMessage(),
'code' => $th->getCode(),
'trace' => $th->getTraceAsString()
], 'error');
return [
'code' => $th->getCode(),
'message' => $th->getMessage()
];
}
}
/**
* 接受好友请求
* @param array $params
* @return array
*/
protected function wxAccept(array $params): array
{
try {
self::setLog([
'title' => 'Send message Device accept',
'params' => $params,
]);
// 1. 参数验证
if (!isset($params['wechat_id']) || !isset($params['friend_id']) || !isset($params['device_code'])) {
throw new \Exception(ResponseCode::getMessage(ResponseCode::INVALID_PARAMS), ResponseCode::INVALID_PARAMS);
}
// 2. 验证设备是否存在
if (!self::checkDevice($params['device_code'])) {
throw new \Exception(ResponseCode::getMessage(ResponseCode::DEVICE_NOT_FOUND), ResponseCode::DEVICE_NOT_FOUND);
}
$deviceId = $params['device_code'];
// 3. 构建消息发送请求
$content = AcceptFriendAddRequestTaskHandler::handle([
'WeChatId' => $params['wechat_id'],
'FriendId' => $params['friend_id'],
'Operation' => 1,
'TaskId' => time(),
]);
// 4. 构建protobuf消息
$message = new TransportMessage();
$message->setMsgType($content['MsgType']);
$any = new Any();
$any->pack($content['Content']);
$message->setContent($any);
$data = $message->serializeToString();
// 5. 发送到设备端
$channel = "socket.{$deviceId}.message";
Client::publish($channel, [
'data' => $data
]);
return [
'code' => 10000,
'message' => 'success',
];
}catch (\Throwable $th) {
self::setLog([
'title' => 'Send message Device accept',
'params' => $params,
'error' => $th->getMessage(),
'code' => $th->getCode(),
'trace' => $th->getTraceAsString()
], 'error');
return [
'code' => $th->getCode(),
'message' => $th->getMessage()
];
}
}
protected function wxCircle(array $params): array
{
try {
self::setLog([
'title' => 'Send message Device pcircleush',
'params' => $params,
]);
// 1. 参数验证
if (!isset($params['wechat_id']) ||!isset($params['content']) ||!isset($params['device_code'])) {
throw new \Exception(ResponseCode::getMessage(ResponseCode::INVALID_PARAMS), ResponseCode::INVALID_PARAMS);
}
// 2. 验证设备是否存在
if (!self::checkDevice($params['device_code'])) {
throw new \Exception(ResponseCode::getMessage(ResponseCode::DEVICE_NOT_FOUND), ResponseCode::DEVICE_NOT_FOUND);
}
$deviceId = $params['device_code'];
$attachmentType = $params['attachment_type'] ?? 0;
$attachment = [];
//0: 纯文本 1图片 2短视频 3长视频 4链接 5小程序
// 附件类型 0链接 2 图片 3短视频 4长视频
switch ($attachmentType) {
case 1:
$attachment = [
'Type' => 2,
'Content' => $params['attachment_content'] ?? []
];
break;
case 2:
$attachment = [
'Type' => 3,
'Content' => $params['attachment_content'] ?? []
];
break;
case 3:
$attachment = [
'Type' => 4,
'Content' => $params['attachment_content'] ?? []
];
break;
case 4:
$attachment = [
'Type' => 0,
'Content' => $params['attachment_content'] ?? []
];
break;
default:
break;
}
// 3. 构建消息发送请求
$content = PostSNSNewsTaskHandler::handle([
'WeChatId' => $params['wechat_id'],
'Content' => $params['content'],
'Attachment' => $attachment,
'ExtComment' => $params['comment'] ?? [],
'TaskId' => time(),
]);
// 4. 构建protobuf消息
$message = new TransportMessage();
$message->setMsgType($content['MsgType']);
$any = new Any();
$any->pack($content['Content']);
$message->setContent($any);
$data = $message->serializeToString();
// 5. 发送到设备端
$channel = "socket.{$deviceId}.message";
Client::publish($channel, [
'data' => $data
]);
return [
'code' => 10000,
'message' => 'success',
];
}catch (\Throwable $th) {
self::setLog([
'title' => 'Send message Device circle',
'params' => $params,
'error' => $th->getMessage(),
'code' => $th->getCode(),
'trace' => $th->getTraceAsString()
], 'error');
return [
'code' => $th->getCode(),
'message' => $th->getMessage()
];
}
}
public static function getWxDeviceInfo(string $deviceId): array
{
return self::getDeviceInfo($deviceId);
}
public static function isWxDeviceOnline(string $deviceId): bool
{
return self::isDeviceOnline($deviceId);
}
public static function updateWxDevices(array $deviceInfo): void
{
try {
$body = \app\common\service\ToolsService::Auth()->deviceUpdate($deviceInfo);
} catch (\Throwable $e) {
//throw $th;
self::setLog([
'title' => 'Send message Device online',
'params' => $deviceInfo,
'error' => $e->getMessage(),
'code' => $e->getCode(),
'trace' => $e->getTraceAsString()
], 'error');
}
}
private static function checkDevice(string $deviceId): bool
{
// 获取设备信息
$deviceInfo = self::getDeviceInfo($deviceId);
return $deviceInfo ? true : false;
}
/**
* 获取设备信息
*
* @param string $deviceId 设备ID
* @return array
*/
private static function getDeviceInfo(string $deviceId): array
{
try {
$result = \app\common\service\ToolsService::Auth()->checkDevice($deviceId);
if((int)$result['code'] === 10000){
return $result['data'];
}else{
return [];
}
} catch (\Throwable $e) {
self::setLog([
'title' => 'getDeviceInfo',
'deviceId' => $deviceId,
'trace' => $e->getTraceAsString(),
], 'error');
return [];
}
}
/**
* 检查设备是否在线
*
* @param string $deviceId 设备ID
* @return bool
*/
private static function isDeviceOnline(string $deviceId): bool
{
$statusKey = self::getDeviceKey($deviceId, 'status');
$status = self::redis()->get($statusKey);
return $status === 'online';
}
private static function getDeviceKey(string $deviceId, string $type): string
{
return sprintf('%s:%s:%s', 'device', $deviceId, $type);
}
private static function getWechatKey(string $deviceId, string $type): string
{
return sprintf('%s:%s:%s', 'wechat', $deviceId, $type);
}
private static function redis(): redisClient
{
return new redisClient([
'host' => env('redis.HOST', '127.0.0.1'),
'port' => env('redis.PORT', 6379),
'password' => env('redis.PASSWORD', '123456'),
'database' => env('redis.WX_SELECT', 9),
'timeout' => 0,
'pool' => [
'max_connections' => 5,
'min_connections' => 1,
'wait_timeout' => 3,
'idle_timeout' => 60,
'heartbeat_interval' => 50,
],
]);
}
private static function setLog($content, $level = 'info'){
if(is_array($content)){
$content = json_encode($content, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
Log::channel('wechat_socket')->write($content, $level);
}
}