bool, 'data' => mixed, 'error' => string, 'error_code' => string, 'raw' => mixed] */ class LiveMessage { /** * @var string 互动消息应用 AppId */ protected $appId; /** * @var string 互动消息应用 AppKey(仅后端使用) */ protected $appKey; /** * @var string 互动消息应用 AppSign */ protected $appSign; /** * @var string 阿里云 RAM 用户 AccessKeyId */ protected $accessKeyId; /** * @var string 阿里云 RAM 用户 AccessKeySecret */ protected $accessKeySecret; /** * @var string 数据中心(cn-shanghai / ap-southeast-1) */ protected $dataCenter; /** * @var bool 是否已初始化默认客户端 */ protected static $clientInited = false; /** * 构造函数 * @param string $appId 互动消息应用 AppId * @param string $appKey 互动消息应用 AppKey * @param string $appSign 互动消息应用 AppSign * @param string $accessKeyId 阿里云 RAM 用户 AccessKeyId * @param string $accessKeySecret 阿里云 RAM 用户 AccessKeySecret * @param string $dataCenter 数据中心 */ public function __construct($appId, $appKey, $appSign, $accessKeyId, $accessKeySecret, $dataCenter) { $this->appId = $appId; $this->appKey = $appKey; $this->appSign = $appSign; $this->accessKeyId = $accessKeyId; $this->accessKeySecret = $accessKeySecret; // 兼容历史脏数据:中文 label 归一化为 region id $regionMap = ['上海' => 'cn-shanghai', '新加坡' => 'ap-southeast-1']; $this->dataCenter = $regionMap[$dataCenter] ?? ($dataCenter ?: 'cn-shanghai'); $this->initClient(); } /** * 初始化阿里云默认客户端(全局只初始化一次) * @return void */ protected function initClient() { if (self::$clientInited) { return; } try { AlibabaCloud::accessKeyClient($this->accessKeyId, $this->accessKeySecret) ->connectTimeout(5000) ->timeout(10000) ->asDefaultClient(); self::$clientInited = true; } catch (\Exception $e) { // 初始化失败时记录日志,后续调用会抛出异常 \think\Log::error('[aliyun LiveMessage] initClient failed: ' . $e->getMessage()); } } /** * 发起 RPC 请求 * @param string $action 接口动作名 * @param array $params 请求参数 * @return array 统一返回结构 */ protected function rpc($action, array $params = []) { try { // RPC 调用需要指定 RegionId,阿里云互动消息服务与直播服务共用 live 产品,默认使用 cn-shanghai $regionId = $this->dataCenter ? $this->dataCenter : 'cn-shanghai'; $request = AlibabaCloud::rpc() ->product('live') ->version('2016-11-01') ->action($action) ->method('POST') ->scheme('https') ->regionId($regionId); if ($params) { $request->options(['query' => $params]); } $result = $request->request(); $dataArray = $result->toArray(); return [ 'success' => true, 'data' => $dataArray, 'error' => '', 'error_code' => '', 'raw' => $dataArray, ]; } catch (ServerException $e) { return [ 'success' => false, 'data' => null, 'error' => $e->getErrorMessage(), 'error_code' => $e->getErrorCode(), 'raw' => $e->getResult() ? $e->getResult()->toArray() : null, ]; } catch (ClientException $e) { return [ 'success' => false, 'data' => null, 'error' => $e->getErrorMessage(), 'error_code' => $e->getErrorCode(), 'raw' => null, ]; } catch (\Exception $e) { return [ 'success' => false, 'data' => null, 'error' => $e->getMessage(), 'error_code' => 'Unknown', 'raw' => null, ]; } } /** * 发送消息到群组(SendLiveMessageGroup) * @param string $groupId 群组ID * @param string $body 消息体 * @param int $msgType 消息类型 * @param string $senderId 发送者ID * @param string $senderMeta 发送者扩展信息 * @param string $msgTid 消息唯一标识 * @return array data 含 msg_tid */ public function sendGroupMessage($groupId, $body, $msgType, $senderId, $senderMeta, $msgTid) { $params = [ 'AppId' => $this->appId, 'GroupId' => $groupId, 'SenderId' => $senderId, 'Body' => $body, 'DataCenter' => $this->dataCenter, ]; if ($msgType) { $params['MsgType'] = (int)$msgType; } if ($senderMeta !== '') { $params['SenderMetaInfo'] = $senderMeta; } if ($msgTid !== '') { $params['MsgTid'] = $msgTid; } $result = $this->rpc('SendLiveMessageGroup', $params); if ($result['success']) { $data = $result['data']; $result['data'] = [ 'msg_tid' => isset($data['MsgTid']) ? $data['MsgTid'] : '', 'request_id' => isset($data['RequestId']) ? $data['RequestId'] : '', ]; } return $result; } /** * 删除(撤回)某条群组消息(DeleteLiveMessageGroupMessage) * @param string $groupId 群组ID * @param string $messageId 消息ID(与发送消息返回的 MsgTid 对应) * @return array data 含 group_id、message_id */ public function deleteGroupMessage($groupId, $messageId) { $params = [ 'AppId' => $this->appId, 'GroupId' => $groupId, 'MessageId' => $messageId, 'DataCenter' => $this->dataCenter, ]; $result = $this->rpc('DeleteLiveMessageGroupMessage', $params); if ($result['success']) { $data = $result['data']; $result['data'] = [ 'group_id' => isset($data['GroupId']) ? $data['GroupId'] : '', 'message_id' => isset($data['MessageId']) ? $data['MessageId'] : '', ]; } return $result; } /** * 查询群组用户列表(ListLiveMessageGroupUsers) * 对于超过 2000 人的超级大群,返回 error_code=SuperLargeGroup,业务层可降级处理 * @param string $groupId 群组ID * @param int $pageSize 每页数量(10-30,阿里云实际限制) * @param int $nextPageToken 下一页起始位置,0 表示首页 * @param int $sortType 排序方式:1 正序,2 逆序 * @return array data 含 group_id、next_page_token、has_more、user_list */ public function listGroupUsers($groupId, $pageSize = 20, $nextPageToken = 0, $sortType = 2) { $pageSize = max(10, min(30, (int)$pageSize)); $sortType = in_array((int)$sortType, [1, 2]) ? (int)$sortType : 2; $params = [ 'AppId' => $this->appId, 'GroupId' => $groupId, 'PageSize' => $pageSize, 'SortType' => $sortType, 'DataCenter' => $this->dataCenter, ]; if ($nextPageToken) { $params['NextPageToken'] = (int)$nextPageToken; } $result = $this->rpc('ListLiveMessageGroupUsers', $params); if ($result['success']) { $data = $result['data']; $userList = []; if (isset($data['UserList']) && is_array($data['UserList'])) { // SDK 可能返回 UserList.Users 或 UserList(直接数组) $users = isset($data['UserList']['Users']) ? $data['UserList']['Users'] : $data['UserList']; foreach ((array)$users as $user) { if (!is_array($user)) { continue; } $userList[] = [ 'user_id' => isset($user['UserId']) ? $user['UserId'] : '', 'user_info' => isset($user['UserInfo']) ? $user['UserInfo'] : '', ]; } } $result['data'] = [ 'group_id' => isset($data['GroupId']) ? $data['GroupId'] : $groupId, 'next_page_token' => isset($data['NextPageToken']) ? (int)$data['NextPageToken'] : 0, 'has_more' => isset($data['Hasmore']) ? (bool)$data['Hasmore'] : false, 'user_list' => $userList, ]; } return $result; } /** * 查询指定的用户是否在线(CheckLiveMessageUsersOnline) * @param array $userIds 用户ID列表(单次最多 10 个) * @return array data 含 user_list */ public function checkUsersOnline($userIds) { $userIds = is_array($userIds) ? $userIds : [$userIds]; $userIds = array_slice($userIds, 0, 10); $params = [ 'AppId' => $this->appId, 'UserIds' => implode(',', $userIds), 'DataCenter' => $this->dataCenter, ]; $result = $this->rpc('CheckLiveMessageUsersOnline', $params); if ($result['success']) { $data = $result['data']; $userList = []; if (isset($data['UserList']) && is_array($data['UserList'])) { $users = isset($data['UserList']['Users']) ? $data['UserList']['Users'] : $data['UserList']; foreach ((array)$users as $user) { if (!is_array($user)) { continue; } $userList[] = [ 'user_id' => isset($user['UserId']) ? $user['UserId'] : '', 'online' => isset($user['Online']) ? (bool)$user['Online'] : false, ]; } } $result['data'] = ['user_list' => $userList]; } return $result; } /** * 创建互动消息群组(CreateLiveMessageGroup) * @param string $groupId 群组ID * @param string $groupName 群组名称 * @param string $groupMeta 群组扩展信息 * @return array data 含 group_id */ public function createGroup($groupId, $groupName = '', $groupMeta = '') { $params = [ 'AppId' => $this->appId, 'GroupId' => $groupId, 'DataCenter' => $this->dataCenter, ]; if ($groupName !== '') { $params['GroupName'] = $groupName; } if ($groupMeta !== '') { $params['GroupMeta'] = $groupMeta; } $result = $this->rpc('CreateLiveMessageGroup', $params); if ($result['success']) { $data = $result['data']; $result['data'] = [ 'group_id' => isset($data['GroupId']) ? $data['GroupId'] : $groupId, 'request_id' => isset($data['RequestId']) ? $data['RequestId'] : '', ]; } return $result; } /** * 获取 AppId * @return string */ public function getAppId() { return $this->appId; } /** * 获取 AppKey * @return string */ public function getAppKey() { return $this->appKey; } /** * 获取 AppSign * @return string */ public function getAppSign() { return $this->appSign; } /** * 获取数据中心 * @return string */ public function getDataCenter() { return $this->dataCenter; } /** * 删除互动消息群组(DeleteLiveMessageGroup) * 群组删除后不再可用,在线用户会被通知群已结束 * @param string $groupId 群组ID * @param string $operatorId 操作者ID(可选,用于后台记录) * @return array data 含 group_id、request_id */ public function deleteGroup($groupId, $operatorId = '') { $params = [ 'AppId' => $this->appId, 'GroupId' => $groupId, 'DataCenter' => $this->dataCenter, ]; if ($operatorId !== '') { $params['OperatorId'] = $operatorId; } $result = $this->rpc('DeleteLiveMessageGroup', $params); if ($result['success']) { $data = $result['data']; $result['data'] = [ 'group_id' => isset($data['GroupId']) ? $data['GroupId'] : $groupId, 'request_id' => isset($data['RequestId']) ? $data['RequestId'] : '', ]; } return $result; } /** * 查询互动消息群组列表(ListLiveMessageGroups) * @param int $sortType 排序方式:1 正序(按创建时间),2 逆序 * @param int $nextPageToken 分页起始位置,-1 或 0 表示首页 * @param int $groupStatus 群组状态:0 全部,1 未删除,2 已删除 * @return array data 含 group_list、has_more、next_page_token */ public function listGroups($sortType = 1, $nextPageToken = -1, $groupStatus = 1) { $params = [ 'AppId' => $this->appId, 'SortType' => in_array((int)$sortType, [1, 2]) ? (int)$sortType : 1, 'GroupStatus' => in_array((int)$groupStatus, [0, 1, 2]) ? (int)$groupStatus : 1, 'DataCenter' => $this->dataCenter, ]; if ((int)$nextPageToken > 0) { $params['NextPageToken'] = (int)$nextPageToken; } $result = $this->rpc('ListLiveMessageGroups', $params); if ($result['success']) { $data = $result['data']; $groupList = []; if (isset($data['GroupList']) && is_array($data['GroupList'])) { foreach ($data['GroupList'] as $group) { if (!is_array($group)) { continue; } $groupList[] = [ 'group_id' => isset($group['GroupId']) ? $group['GroupId'] : '', 'group_name' => isset($group['GroupName']) ? $group['GroupName'] : '', 'creator_id' => isset($group['CreatorId']) ? $group['CreatorId'] : '', 'createtime' => isset($group['Createtime']) ? (int)$group['Createtime'] : 0, 'deleted' => isset($group['Delete']) ? (bool)$group['Delete'] : false, ]; } } $result['data'] = [ 'group_list' => $groupList, 'has_more' => isset($data['Hasmore']) ? (bool)$data['Hasmore'] : false, 'next_page_token' => isset($data['NextpageToken']) ? (int)$data['NextpageToken'] : 0, ]; } return $result; } }