初始化项目:添加后端代码、ThinkPHP框架、前端资源
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library\live\message\provider;
|
||||
|
||||
use app\common\library\live\message\ProviderInterface;
|
||||
use app\common\library\live\message\Result;
|
||||
use app\common\model\config\System;
|
||||
use aliyun\LiveMessage;
|
||||
|
||||
/**
|
||||
* 阿里云互动消息服务商适配器
|
||||
* 基于 \aliyun\LiveMessage 实现统一接口
|
||||
*/
|
||||
class Aliyun implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var LiveMessage 阿里云 SDK 实例
|
||||
*/
|
||||
protected $sdk;
|
||||
|
||||
/**
|
||||
* 构造函数,从配置 aliyun_message 读取阿里云相关参数并实例化 SDK
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = System::getConfig('aliyun_message');
|
||||
|
||||
$appId = (is_array($config) && isset($config['app_id'])) ? $config['app_id'] : '';
|
||||
$appKey = (is_array($config) && isset($config['app_key'])) ? $config['app_key'] : '';
|
||||
$appSign = (is_array($config) && isset($config['app_sign'])) ? $config['app_sign'] : '';
|
||||
$accessKeyId = (is_array($config) && isset($config['access_key_id'])) ? $config['access_key_id'] : '';
|
||||
$accessKeySecret = (is_array($config) && isset($config['access_key_secret'])) ? $config['access_key_secret'] : '';
|
||||
$dataCenter = (is_array($config) && !empty($config['data_center'])) ? $config['data_center'] : 'cn-shanghai';
|
||||
|
||||
$this->sdk = new LiveMessage($appId, $appKey, $appSign, $accessKeyId, $accessKeySecret, $dataCenter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息到群组
|
||||
* @param string $groupId 群组ID
|
||||
* @param string $body 消息体
|
||||
* @param int $msgType 消息类型(业务 type 映射后的阿里云 MsgType)
|
||||
* @param string $senderId 发送者ID
|
||||
* @param string $senderMeta 发送者扩展信息
|
||||
* @param string $msgTid 消息唯一标识
|
||||
* @return Result data 含 msg_tid
|
||||
*/
|
||||
public function send($groupId, $body, $msgType = 10001, $senderId = '', $senderMeta = '', $msgTid = '')
|
||||
{
|
||||
// 若未传入 senderId,使用系统默认(管理员发送场景)
|
||||
if ($senderId === '') {
|
||||
$senderId = 'system';
|
||||
}
|
||||
|
||||
$result = $this->sdk->sendGroupMessage($groupId, $body, $msgType, $senderId, $senderMeta, $msgTid);
|
||||
|
||||
if ($result['success']) {
|
||||
return Result::success([
|
||||
'msg_tid' => isset($result['data']['msg_tid']) ? $result['data']['msg_tid'] : '',
|
||||
], $result['raw']);
|
||||
}
|
||||
|
||||
return Result::fail($result['error'], $result['raw']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回/删除消息
|
||||
* @param string $groupId 群组ID
|
||||
* @param string $msgTid 阿里云消息唯一标识
|
||||
* @return Result
|
||||
*/
|
||||
public function del($groupId, $msgTid)
|
||||
{
|
||||
$result = $this->sdk->deleteGroupMessage($groupId, $msgTid);
|
||||
|
||||
if ($result['success']) {
|
||||
return Result::success($result['data'], $result['raw']);
|
||||
}
|
||||
|
||||
return Result::fail($result['error'], $result['raw']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群组在线用户
|
||||
* 对于超过 2000 人的超级大群,返回 success=false 且 error='SuperLargeGroup',业务层可降级处理
|
||||
* @param string $groupId 群组ID
|
||||
* @param int $pageSize 每页数量(10-30,阿里云实际限制)
|
||||
* @param int $nextPageToken 下一页起始位置
|
||||
* @return Result data 含 total、list、next_page_token、has_more
|
||||
*/
|
||||
public function getOnlineUser($groupId, $pageSize = 20, $nextPageToken = 0)
|
||||
{
|
||||
$result = $this->sdk->listGroupUsers($groupId, $pageSize, $nextPageToken);
|
||||
|
||||
if ($result['success']) {
|
||||
$data = $result['data'];
|
||||
$userIds = [];
|
||||
if (isset($data['user_list']) && is_array($data['user_list'])) {
|
||||
foreach ($data['user_list'] as $user) {
|
||||
if (isset($user['user_id'])) {
|
||||
$userIds[] = $user['user_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result::success([
|
||||
'total' => count($userIds),
|
||||
'list' => $userIds,
|
||||
'next_page_token' => isset($data['next_page_token']) ? $data['next_page_token'] : 0,
|
||||
'has_more' => isset($data['has_more']) ? $data['has_more'] : false,
|
||||
], $result['raw']);
|
||||
}
|
||||
|
||||
// 超级大群特殊错误码,保持与接口约定一致
|
||||
if ($result['error_code'] === 'SuperLargeGroup') {
|
||||
return Result::fail('SuperLargeGroup', $result['raw']);
|
||||
}
|
||||
|
||||
return Result::fail($result['error'], $result['raw']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端登录鉴权信息
|
||||
* 实现 token = sha256(appId + appKey + userId + nonce + timestamp + role)
|
||||
* @param string $userId 用户ID(需过滤,仅保留 A-Z a-z 0-9 -,最长 64 字节)
|
||||
* @param string $role 角色(admin 或空字符串)
|
||||
* @return Result data 含 app_id、app_sign、auth、app_token
|
||||
*/
|
||||
public function getLoginAuth($userId, $role = '')
|
||||
{
|
||||
$appId = $this->sdk->getAppId();
|
||||
$appKey = $this->sdk->getAppKey();
|
||||
|
||||
// userId 过滤:仅保留 [A-Za-z0-9-],最长 64 字节
|
||||
$filteredUserId = $this->filterUserId($userId);
|
||||
if ($filteredUserId === '') {
|
||||
return Result::fail('userId 无效,过滤后为空', null);
|
||||
}
|
||||
|
||||
// nonce 生成:AK- + md5(uniqid(mt_rand, true))
|
||||
$nonce = 'AK-' . md5(uniqid(mt_rand(), true));
|
||||
|
||||
// timestamp:当前时间 + 86400 秒
|
||||
$timestamp = time() + 86400;
|
||||
|
||||
// role 仅允许 admin 或空字符串
|
||||
$role = ($role === 'admin') ? 'admin' : '';
|
||||
|
||||
// 计算 token
|
||||
$token = hash('sha256', $appId . $appKey . $filteredUserId . $nonce . $timestamp . $role);
|
||||
|
||||
$data = [
|
||||
'app_id' => $appId,
|
||||
'app_sign' => $this->sdk->getAppSign(),
|
||||
'auth' => [
|
||||
'nonce' => $nonce,
|
||||
'timestamp' => $timestamp,
|
||||
'role' => $role,
|
||||
'user_id' => $filteredUserId,
|
||||
],
|
||||
'app_token' => $token,
|
||||
];
|
||||
|
||||
return Result::success($data, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建群组
|
||||
* @param string $groupId 群组ID
|
||||
* @param string $groupName 群组名称
|
||||
* @param string $groupMeta 群组扩展信息
|
||||
* @return Result
|
||||
*/
|
||||
public function createGroup($groupId, $groupName = '', $groupMeta = '')
|
||||
{
|
||||
$result = $this->sdk->createGroup($groupId, $groupName, $groupMeta);
|
||||
|
||||
if ($result['success']) {
|
||||
return Result::success($result['data'], $result['raw']);
|
||||
}
|
||||
|
||||
return Result::fail($result['error'], $result['raw']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除群组
|
||||
* @param string $groupId 群组ID
|
||||
* @param string $operatorId 操作者ID(可选)
|
||||
* @return Result
|
||||
*/
|
||||
public function deleteGroup($groupId, $operatorId = '')
|
||||
{
|
||||
$result = $this->sdk->deleteGroup($groupId, $operatorId);
|
||||
|
||||
if ($result['success']) {
|
||||
return Result::success($result['data'], $result['raw']);
|
||||
}
|
||||
|
||||
return Result::fail($result['error'], $result['raw']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层 SDK 实例(供扩展调用,如 listGroups)
|
||||
* @return LiveMessage
|
||||
*/
|
||||
public function getSdk()
|
||||
{
|
||||
return $this->sdk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤 userId,仅保留 [A-Za-z0-9-],最长 64 字节
|
||||
* @param string $userId 原始用户ID
|
||||
* @return string
|
||||
*/
|
||||
protected function filterUserId($userId)
|
||||
{
|
||||
$filtered = preg_replace('/[^A-Za-z0-9\-]/', '', (string)$userId);
|
||||
// 截断到 64 字节
|
||||
if (strlen($filtered) > 64) {
|
||||
$filtered = substr($filtered, 0, 64);
|
||||
}
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library\live\message\provider;
|
||||
|
||||
use app\common\library\live\message\ProviderInterface;
|
||||
use app\common\library\live\message\Result;
|
||||
use app\common\model\config\System;
|
||||
use aodianyun\Dms;
|
||||
|
||||
/**
|
||||
* 奥点云消息服务商适配器
|
||||
* 封装 \aodianyun\Dms,保持与原 library/live/Message.php 一致的行为
|
||||
*/
|
||||
class Aodianyun implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var Dms
|
||||
*/
|
||||
protected $dms;
|
||||
|
||||
/**
|
||||
* 构造函数,从配置读取 s_key 并实例化 Dms
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = System::getConfig('aodianyun');
|
||||
$sKey = is_array($config) && isset($config['s_key']) ? $config['s_key'] : '';
|
||||
$this->dms = new Dms($sKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息到群组
|
||||
* 奥点云 DMS 成功响应:HTTP 201 + {"uuid":"xxx"}(无 Flag 字段)
|
||||
* 失败响应:{"error":"xxx"} 或 Dms::curl 兜底构造的 ['Flag'=>httpCode, 'FlagString'=>body]
|
||||
* @param string $groupId 群组ID(奥点云 topic)
|
||||
* @param string $body 消息内容
|
||||
* @param int $msgType 消息类型(奥点云不使用)
|
||||
* @param string $senderId 发送者ID(奥点云不使用)
|
||||
* @param string $senderMeta 发送者扩展信息(奥点云不使用)
|
||||
* @param string $msgTid 消息唯一标识(奥点云由服务端生成 uuid)
|
||||
* @return Result data 含 uuid
|
||||
*/
|
||||
public function send($groupId, $body, $msgType = 10001, $senderId = '', $senderMeta = '', $msgTid = '')
|
||||
{
|
||||
$response = $this->dms->sendMsg($groupId, $body);
|
||||
|
||||
// 成功:响应中含 uuid 字段
|
||||
if (is_array($response) && isset($response['uuid'])) {
|
||||
$uuid = $response['uuid'];
|
||||
return Result::success(['uuid' => $uuid, 'msg_tid' => $uuid], $response);
|
||||
}
|
||||
|
||||
$error = $this->extractError($response, '发送失败');
|
||||
return Result::fail($error, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回/删除消息
|
||||
* 奥点云 DMS 成功响应:HTTP 204 No Content,body 为空,Dms::delMsg 返回 null
|
||||
* 失败响应:{"error":"xxx"} 或 ['Flag'=>httpCode, 'FlagString'=>body]
|
||||
* @param string $groupId 群组ID
|
||||
* @param string $msgTid 消息唯一标识(奥点云为 uuid)
|
||||
* @return Result
|
||||
*/
|
||||
public function del($groupId, $msgTid)
|
||||
{
|
||||
$response = $this->dms->delMsg($groupId, $msgTid);
|
||||
|
||||
// 成功:HTTP 204 空内容,json_decode 返回 null;curl 失败时 Dms 会构造 ['Flag'=>0, ...] 数组
|
||||
if ($response === null || (is_array($response) && empty($response))) {
|
||||
return Result::success([], $response);
|
||||
}
|
||||
|
||||
$error = $this->extractError($response, '删除失败');
|
||||
return Result::fail($error, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群组在线用户
|
||||
* 奥点云 DMS 成功响应:{"list":[...], "total":N}
|
||||
* 失败响应:{"error":"xxx"} 或 ['Flag'=>httpCode, 'FlagString'=>body]
|
||||
* @param string $groupId 群组ID
|
||||
* @param int $pageSize 每页数量(奥点云作为 num)
|
||||
* @param int $nextPageToken 起始偏移量(奥点云作为 skip)
|
||||
* @return Result data 含 total、list、next_page_token、has_more
|
||||
*/
|
||||
public function getOnlineUser($groupId, $pageSize = 50, $nextPageToken = 0)
|
||||
{
|
||||
$skip = (int)$nextPageToken;
|
||||
$num = (int)$pageSize;
|
||||
|
||||
$response = $this->dms->getOnlineUser($groupId, $skip, $num);
|
||||
|
||||
// 成功:响应中含 total 字段
|
||||
if (is_array($response) && isset($response['total'])) {
|
||||
$total = (int)$response['total'];
|
||||
$list = isset($response['list']) ? $response['list'] : [];
|
||||
return Result::success([
|
||||
'total' => $total,
|
||||
'list' => $list,
|
||||
'next_page_token' => 0,
|
||||
'has_more' => false,
|
||||
], $response);
|
||||
}
|
||||
|
||||
$error = $this->extractError($response, '获取在线用户失败');
|
||||
return Result::fail($error, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从奥点云 Dms 响应中提取错误信息
|
||||
* 优先取官方文档的 error 字段,其次取 Dms::curl 兜底的 FlagString 字段
|
||||
* @param mixed $response
|
||||
* @param string $default 默认错误文案
|
||||
* @return string
|
||||
*/
|
||||
protected function extractError($response, $default = '操作失败')
|
||||
{
|
||||
if (!is_array($response)) {
|
||||
return $default;
|
||||
}
|
||||
if (isset($response['error']) && $response['error'] !== '') {
|
||||
return $response['error'];
|
||||
}
|
||||
if (isset($response['FlagString']) && $response['FlagString'] !== '') {
|
||||
return $response['FlagString'];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端登录鉴权信息(奥点云不需要 token 鉴权,返回空数据)
|
||||
* @param string $userId 用户ID
|
||||
* @param string $role 角色
|
||||
* @return Result
|
||||
*/
|
||||
public function getLoginAuth($userId, $role = '')
|
||||
{
|
||||
return Result::success([], null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建群组(奥点云无需创建群组,直接返回成功)
|
||||
* @param string $groupId 群组ID
|
||||
* @param string $groupName 群组名称
|
||||
* @param string $groupMeta 群组扩展信息
|
||||
* @return Result
|
||||
*/
|
||||
public function createGroup($groupId, $groupName = '', $groupMeta = '')
|
||||
{
|
||||
return Result::success([], null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除群组(奥点云无需删除群组,直接返回成功)
|
||||
* @param string $groupId 群组ID
|
||||
* @param string $operatorId 操作者ID
|
||||
* @return Result
|
||||
*/
|
||||
public function deleteGroup($groupId, $operatorId = '')
|
||||
{
|
||||
return Result::success([], null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user