diff --git a/.gitignore b/.gitignore index 0f5c816..29fca42 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ config/site.php app/command/Test.php .env runtime +vendor \ No newline at end of file diff --git a/app/api/middleware/Auth.php b/app/api/middleware/Auth.php index 3804d1f..c4cc702 100644 --- a/app/api/middleware/Auth.php +++ b/app/api/middleware/Auth.php @@ -120,6 +120,11 @@ class Auth implements MiddlewareInterface 'user' => session('admin'), 'config' => $config ]); + $IM = new \support\OpenImSdk\Client([ + 'host' => 'http://127.0.0.1:10002', // OpenIM API地址 + 'secret' => 'openIM123', // OpenIM密钥 + ]); + $request->IM = $IM; $response = $next($request); $headers = [ 'Access-Control-Allow-Credentials' => 'true', diff --git a/app/command/OpenIm.php b/app/command/OpenIm.php new file mode 100644 index 0000000..89cc1c5 --- /dev/null +++ b/app/command/OpenIm.php @@ -0,0 +1,73 @@ +addOption('action','a', InputArgument::OPTIONAL, '操作类型'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $action = $input->getOption('action'); + if(!$action){ + $output->writeln('空操作'); + return self::FAILURE; + } + if(method_exists($this, $action)){ + return $this->$action($input, $output); + } + return self::FAILURE; + } + private function sync_users(InputInterface $input, OutputInterface $output):int{ + $im = new \support\OpenImSdk\Client([ + 'host' => 'http://127.0.0.1:10002', // OpenIM API地址 + 'secret' => 'openIM123', // OpenIM密钥 + ]); + $data = $im->user->getAllUsersUid(1,1000); + $exsit_user_ids = Db::name('user')->whereIn('id',$data['userIDs'])->column('id'); + $not_exsit_user_ids =array_diff($data['userIDs'],$exsit_user_ids); + if(count($not_exsit_user_ids)> 0){ + //同步用户 + $res = $im->user->getUsersInfo($not_exsit_user_ids); + $save_data = []; + foreach($res['usersInfo'] as $k=>$_user){ + array_push($save_data,[ + 'id' => $_user['userID'], + 'nickname' => $_user['nickname'], + 'password' => '123456', + 'avatar' => $_user['faceURL'] + ]); + + // "ex": "", + // "createTime": 1688454168302, + // "appMangerLevel": 18, + // "globalRecvMsgOpt": 0 + } + if(!empty($save_data)){ + Db::name('user')->insertAll($save_data); + } + } + return self::SUCCESS; + } +} diff --git a/app/command/Otop.php b/app/command/Otop.php index 6900369..e7f0ff4 100644 --- a/app/command/Otop.php +++ b/app/command/Otop.php @@ -20,7 +20,7 @@ class Otop extends Command */ protected function configure() { - $this->addOption('user_id','uid', InputArgument::OPTIONAL, 'user_id'); + $this->addOption('user_id','u', InputArgument::OPTIONAL, 'user_id'); } /** diff --git a/app/middleware/ActionHook.php b/app/middleware/ActionHook.php index 246f7ad..1d6b3ab 100644 --- a/app/middleware/ActionHook.php +++ b/app/middleware/ActionHook.php @@ -36,6 +36,7 @@ class ActionHook implements MiddlewareInterface return $after_response; } } + return $response; } return $next($request); diff --git a/app/model/User.php b/app/model/User.php index e080fc9..3cddbf7 100644 --- a/app/model/User.php +++ b/app/model/User.php @@ -75,6 +75,38 @@ use support\think\Db; */ class User extends Base { + use \think\model\concern\SoftDelete; + public static function onAfterInsert($row){ + $res = request()->IM->user->userRegister($row->id,$row->nickname,cdnurl($row->avatar)); + } + public static function onAfterUpdate($row){ + $changeData = $row->getChangedData(); + $orgData = $row->getOrigin(); + //cp($changeData); + if(isset($changeData['avatar']) || isset($changeData['nickname'])){ + request()->IM->user->updateUserInfo($row->id,[ + 'nickname' => $row->nickname, + 'faceURL' => cdnurl($row->avatar) + ]); + } + if(isset($changeData['status']) || $changeData['status'] == '0'){ + request()->IM->user->forceLogout($row->id); + } + } + public static function onAfterDelete($row) + { + Db::name('address')->where('user_id',$row->id)->delete(); + Db::name('recharge')->where('user_id',$row->id)->delete(); + Db::name('record')->where('user_id',$row->id)->delete(); + Db::name('withdrawl')->where('user_id',$row->id)->delete(); + Db::name('user_extend')->where('user_id',$row->id)->delete(); + Db::name('user_team')->where('descendant_id|ancestor_id','=',$row->id)->delete(); + Db::name('withdrawl')->where('user_id',$row->id)->delete(); + foreach(Config('site.allow_currencys') as $currency){ + (new \app\model\BalanceLog)->setSuffix('_'.$currency)->where('user_id',(int)$row->id)->delete(); + } + request()->IM->user->forceLogout($row->id); + } public function role() { return $this->belongsTo('\\app\\model\\UserRole', 'role_id', 'id');//->bind(['name']); diff --git a/composer.json b/composer.json index 8bc75cf..f04e3b0 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,8 @@ "spomky-labs/otphp": "^11.3.0", "firebase/php-jwt": "6.8", "php-mcp/server": "^3.3", - "intervention/image": "^2.7.2" + "intervention/image": "^2.7.2", + "guzzlehttp/guzzle": "^7.5.0" }, "suggest": { "ext-event": "For better performance. " diff --git a/plugin/admin/app/controller/AccountController.php b/plugin/admin/app/controller/AccountController.php index f815675..83a7927 100644 --- a/plugin/admin/app/controller/AccountController.php +++ b/plugin/admin/app/controller/AccountController.php @@ -98,7 +98,7 @@ class AccountController extends Crud // return $this->fail('账户不存在或密码错误'); // } //$secret = $admin['totp_secret'] ?:'EJGYB7OZR2W46XRX7VB3PXHSOY4LUAWCA5GTDAVTWKHXNDAAAIIP7AQ3JSO3XZJNX5J5OTIDEQVKLYFYIYNAXSCYF4GNZ2EMA4ORA3Y'; - \support\Log::alert($admin['totp_secret']); + //\support\Log::alert($admin['totp_secret']); $totp = \OTPHP\TOTP::create($admin->totp_secret); //$secret = $totp->getSecret(); //$totp->setLabel('cansnow'); diff --git a/plugin/admin/app/controller/ConfigController.php b/plugin/admin/app/controller/ConfigController.php index 865f8f0..956464a 100644 --- a/plugin/admin/app/controller/ConfigController.php +++ b/plugin/admin/app/controller/ConfigController.php @@ -143,9 +143,9 @@ class ConfigController extends Base $post = $request->post('row'); Db::startTrans(); try { - if($post['type'] == 'selects'){ - $post['value'] = implode(',',$post['value']); - } + // if($post['type'] == 'selects'){ + // $post['value'] = implode(',',$post['value']); + // } $user = ConfigModel::create($post); Db::commit(); $this->buildcache(); @@ -165,7 +165,7 @@ class ConfigController extends Base $v['value'] = json_decode($v['value'], true); } if(in_array($v['type'] ,['selects']) && !is_array($v['value'])){ - $v['value'] = explode(',',$v['value']); + $v['value'] = explode(',',$v['value']??''); } $list[$v['name']] = $v['value']; } diff --git a/plugin/admin/app/controller/IndexController.php b/plugin/admin/app/controller/IndexController.php index cf03994..254cd9e 100644 --- a/plugin/admin/app/controller/IndexController.php +++ b/plugin/admin/app/controller/IndexController.php @@ -70,7 +70,7 @@ class IndexController extends Base //$day7_user_recharge_sum = Recharge::where('status',2)->whereTime('created_at', '-7 days')->sum('amount'); // 总用户数 $user_count = \app\model\User::where('status',1)->count('id'); - $recharge_total = \app\model\Recharge::where('status',\app\enum\RechargeStatus::COMPLETE->value)->sum('amount'); + $recharge_total = 0;//\app\model\Recharge::where('status',\app\enum\RechargeStatus::COMPLETE->value)->sum('amount'); // mysql版本 $withdrawl_total = \app\model\Withdrawl::where('status',\app\enum\WithdrawlStatus::COMPLETE->value)->sum('recive_amount'); // mysql版本 diff --git a/plugin/admin/app/controller/OpenimController.php b/plugin/admin/app/controller/OpenimController.php new file mode 100644 index 0000000..dc42028 --- /dev/null +++ b/plugin/admin/app/controller/OpenimController.php @@ -0,0 +1,110 @@ +model = new UserModel(); + $groupList = [ + ['value'=>0,'label'=>"普通用户"], + ['value'=>1,'label'=>"内部用户"], + ['value'=>2,'label'=>"联盟商"], + ]; + $roleList = \app\model\UserRole::order('id','desc')->column('name as label,id as value'); + $this->assign('groupList',$groupList); + $this->assignconfig('groupList',$groupList); + $this->assign('roleList',$roleList); + $this->assignconfig('roleList',$roleList); + } + public function team(Request $request): Response + { + return view(); + } + // public function select(Request $request): Response + // { + // $this->model = $this->model->with(['referrer','role']); + // return parent::select($request); + // } + /** + * 浏览 + * @return Response + * @throws Throwable + */ + public function index(Request $request): Response + { + return view('user/index'); + } + public function select(Request $request): Response{ + $res = $request->IM->user->getUsers(1,20); + //cp($request->IM->user->getAllUsersUid()); + if($res['errCode']!==0){ + return $this->fail($res['errDlt']); + } + return json([ + 'code' => 0, + "msg" => "ok", + 'count' => $res['data']['total'], + 'data' =>$res['data']['users'], + ]); + } + + /** + * 插入 + * @param Request $request + * @return Response + * @throws BusinessException|Throwable + */ + public function insert(Request $request): Response + { + if ($request->method() === 'POST') { + return parent::insert($request); + } + return view('user/update',[ + 'row' => UserModel::findOrEmpty(0) + ]); + } + + /** + * 更新 + * @param Request $request + * @return Response + * @throws BusinessException|Throwable + */ + public function update(Request $request): Response + { + if ($request->method() === 'POST') { + [$id, $data] = $this->updateInput($request); + $this->doUpdate($id, $data); + $ret = $this->success('操作成功'); + return $ret; + } + $ids = Request()->get('ids'); + $user = $this->model->where('id',$ids)->find(); + return view('user/update',[ + 'row' => $user + ]); + } +} diff --git a/plugin/admin/app/controller/UserController.php b/plugin/admin/app/controller/UserController.php index 343bd39..1e7d4cd 100644 --- a/plugin/admin/app/controller/UserController.php +++ b/plugin/admin/app/controller/UserController.php @@ -68,6 +68,7 @@ class UserController extends Crud { if ($request->method() === 'POST') { return parent::insert($request); + } return view('user/update',[ 'row' => UserModel::findOrEmpty(0) diff --git a/plugin/admin/app/middleware/AccessControl.php b/plugin/admin/app/middleware/AccessControl.php index 6cee5a5..b694ad7 100644 --- a/plugin/admin/app/middleware/AccessControl.php +++ b/plugin/admin/app/middleware/AccessControl.php @@ -39,25 +39,8 @@ class AccessControl implements MiddlewareInterface $response = view('common/403')->withStatus(403); } } - } else { - $config = Config('site'); - $config['debug'] = config('app.debug'); - $config['controller'] = $request->controller_name; - $config['action'] = $request->action_name; - $config['moduleurl'] = '/app/admin'; - $request->_view_vars = array_merge((array) $request->_view_vars,[ - 'user' => session('admin'), - 'config' => $config - ]); - $response = $request->method() == 'OPTIONS' ? response('') : $handler($request); - $response->withBody(str_replace([ - '__SELF__' - ],[ - request()->path() - //url(request()->action) - ],$response->rawBody()))->getStatusCode(); + return $response; } - - return $response; + return $handler($request); } } diff --git a/plugin/admin/app/middleware/Config.php b/plugin/admin/app/middleware/Config.php new file mode 100644 index 0000000..631e36f --- /dev/null +++ b/plugin/admin/app/middleware/Config.php @@ -0,0 +1,41 @@ +controller_name = get_controller_name(); + $request->action_name = get_action_name(); + + $config = Config('site'); + $config['debug'] = config('app.debug'); + $config['controller'] = $request->controller_name; + $config['action'] = $request->action_name; + $config['moduleurl'] = '/app/admin'; + $request->_view_vars = array_merge((array) $request->_view_vars,[ + 'user' => session('admin'), + 'config' => $config + ]); + $IM = new \support\OpenImSdk\Client([ + 'host' => 'http://127.0.0.1:10002', // OpenIM API地址 + 'secret' => 'openIM123', // OpenIM密钥 + ]); + $request->IM = $IM; + return $handler($request); + } +} diff --git a/plugin/admin/app/middleware/Tpl.php b/plugin/admin/app/middleware/Tpl.php new file mode 100644 index 0000000..8e89fa8 --- /dev/null +++ b/plugin/admin/app/middleware/Tpl.php @@ -0,0 +1,34 @@ +method() == 'OPTIONS' ? response('') : $handler($request); + $response->withBody(str_replace([ + '__SELF__', + '__2__' + ],[ + request()->path(), + '-' + //url(request()->action) + ],$response->rawBody()))->getStatusCode(); + + return $response; + } +} diff --git a/plugin/admin/app/model/User.php b/plugin/admin/app/model/User.php index 4eb4766..eacc21f 100644 --- a/plugin/admin/app/model/User.php +++ b/plugin/admin/app/model/User.php @@ -30,6 +30,7 @@ class User extends \app\model\User { public static function onAfterUpdate($row){ + parent::onAfterUpdate($row); $changeData = $row->getChangedData(); $orgData = $row->getOrigin(); foreach(Config('site.allow_currencys') as $currency){ @@ -41,17 +42,4 @@ class User extends \app\model\User } } } - public static function onAfterDelete($row) - { - Db::name('address')->where('user_id',$row->id)->delete(); - Db::name('recharge')->where('user_id',$row->id)->delete(); - Db::name('record')->where('user_id',$row->id)->delete(); - Db::name('withdrawl')->where('user_id',$row->id)->delete(); - Db::name('user_extend')->where('user_id',$row->id)->delete(); - Db::name('user_team')->where('descendant_id|ancestor_id','=',$row->id)->delete(); - Db::name('withdrawl')->where('user_id',$row->id)->delete(); - (new \app\model\BalanceLog)->setSuffix('_money')->where('user_id',(int)$row->id)->delete(); - (new \app\model\BalanceLog)->setSuffix('_score')->where('user_id',(int)$row->id)->delete(); - (new \app\model\BalanceLog)->setSuffix('_currency1')->where('user_id',(int)$row->id)->delete(); - } } diff --git a/plugin/admin/app/view/index/dashboard.html b/plugin/admin/app/view/index/dashboard.html index 4e6f939..81b0bc2 100644 --- a/plugin/admin/app/view/index/dashboard.html +++ b/plugin/admin/app/view/index/dashboard.html @@ -97,11 +97,6 @@ ?> - - - - - 用户余额总和 {$user_money_total} diff --git a/plugin/admin/config/middleware.php b/plugin/admin/config/middleware.php index 5577139..094c575 100644 --- a/plugin/admin/config/middleware.php +++ b/plugin/admin/config/middleware.php @@ -11,12 +11,14 @@ * @link http://www.workerman.net/ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ - - use plugin\admin\app\middleware\AccessControl; - return [ '' => [ - AccessControl::class, + \plugin\admin\app\middleware\AccessControl::class, + \plugin\admin\app\middleware\Config::class, + //\plugin\admin\app\middleware\OpenIM::class, \app\middleware\ActionHook::class, + //\app\middleware\BeforeActionHook::class, + //\app\middleware\AfterActionHook::class, + \plugin\admin\app\middleware\Tpl::class, ] ]; \ No newline at end of file diff --git a/plugin/admin/public/js/user.js b/plugin/admin/public/js/user.js index f0721a0..c092c93 100644 --- a/plugin/admin/public/js/user.js +++ b/plugin/admin/public/js/user.js @@ -18,202 +18,101 @@ define(['table', 'upload','form'], function (Table,Upload,Form) { table: 'user', } }); - + console.log(Config) var table = $("#table"); + var columns = [ + {checkbox: true}, + { + field: 'id', + title: 'ID', + filter: "number", + sortable: true // 是否排序 + }, + // { + // title: "角色", + // field: "role_id", + // formatter:function(v,row){ + // return row.role ? row.role.name : '用户'; + // }, + // filter: "select", + // filterOption:"roleOption", + // visible: false, + // }, + { + title: "昵称", + field: "nickname" + }, + { + title: "头像", + field: "avatar", + formatter: function (v,d) { + return '' + }, + visible: false + }, + // { + // title: "等级", + // field: "level", + // visible: false, + // }, + // { + // title: "生日", + // field: "birthday", + // visible: false, + // }, + // { + // title: "后缀", + // field: "decimal_part", + // //visible: false, + // }, + ]; + var currencys = ['money','score','currency1','currency2','currency3','currency4','currency5','currency6','currency7','currency8','currency9']; + for (let i = 0; i < currencys.length; i++) { + if(Config.allow_currencys.indexOf(currencys[i])!==-1){ + columns.push({ + title: __(currencys[i]), + field: currencys[i], + formatter:Table.api.formatter.number, + //sortable: true, + //visible: false, + }); + } + }; + columns.push({ + title: "注册时间", + field: "createTime", + formatter:function(v,row){ + return Table.api.formatter.datetime(parseInt(v/1000)); + }, + filter:'datetime' + }); + columns.push({ + title: "状态", + field: "status", + formatter: Table.api.formatter.switch + }); + columns.push({ + field: 'operate', title: '操作', + table: table, + events: Table.api.events.operate, + formatter: Table.api.formatter.operate, + buttons:[ + { + text:"团队", + name:"team", + icon:"mdi mdi-account-group-outline", + classname:"btn btn-xs btn-info btn-dialog", + url:'/app/admin/user/team' + } + ] + }); var tableOptions = { url: $.fn.bootstrapTable.defaults.extend.index_url, pk: 'id', sortName: 'id', commonSearch: false, search: false, - columns: [ - [ - {checkbox: true}, - { - field: 'id', - title: 'ID', - filter: "number", - sortable: true // 是否排序 - }, - { - title: "推荐人", - field: "parent_id", - formatter:function(v,row){ - return row.referrer ? row.referrer.username : ''; - }, - visible: false, - }, - { - title: "分组", - field: "group", - formatter:function(v,row){ - for (let i = 0; i < Config.groupList.length; i++) { - if(Config.groupList[i].value == v){ - return Config.groupList[i].label; - } - } - return ''; - }, - filter: "select", - filterOption:"groupOption", - visible: false, - }, - { - title: "角色", - field: "role_id", - formatter:function(v,row){ - return row.role ? row.role.name : '用户'; - }, - filter: "select", - filterOption:"roleOption", - visible: false, - }, - { - title: "用户名", - field: "username", - filter: "string", - }, - { - title: "昵称", - field: "nickname", - visible: false - }, - { - field: 'domain', - title: '域名', - filter: "string", - visible: false - }, - { - title: "头像", - field: "avatar", - formatter: function (v,d) { - return '' - }, - visible: false - }, - { - title: "邮箱", - field: "email", - visible: false - }, - { - title: "手机", - field: "mobile", - visible: false - }, - // { - // title: "等级", - // field: "level", - // visible: false, - // }, - // { - // title: "生日", - // field: "birthday", - // visible: false, - // }, - // { - // title: "后缀", - // field: "decimal_part", - // //visible: false, - // }, - { - title: "调研币", - field: "money", - formatter:Table.api.formatter.number, - sortable: true, - //visible: false, - }, - { - title: "积分", - field: "score", - sortable: true, - visible: false, - formatter:Table.api.formatter.number - }, - { - title: "调研豆", - field: "currency1", - formatter:Table.api.formatter.number - }, - { - title: "可领取", - field: "currency6", - sortable: true, - formatter:Table.api.formatter.number - }, - { - title: "待分配", - field: "currency7", - sortable: true, - formatter:Table.api.formatter.number - }, - { - title: "已分配", - field: "currency8", - sortable: true, - formatter:Table.api.formatter.number - }, - { - title: "未通过", - field: "currency9", - sortable: true, - formatter:Table.api.formatter.number - }, - { - title: "登录时间", - field: "last_time", - formatter:Table.api.formatter.datetime, - visible: false, - }, - { - title: "登录ip", - field: "last_ip", - visible: false, - }, - { - title: "注册时间", - field: "join_time", - formatter:Table.api.formatter.datetime, - filter:'datetime' - }, - { - title: "注册ip", - field: "join_ip", - visible: false, - }, - { - title: "创建时间", - field: "created_at", - visible: false, - }, - { - title: "更新时间", - field: "updated_at", - formatter:Table.api.formatter.datetime, - visible: false, - }, - { - title: "状态", - field: "status", - formatter: Table.api.formatter.switch - }, - { - field: 'operate', title: '操作', - table: table, events: Table.api.events.operate, - formatter: Table.api.formatter.operate, - buttons:[ - { - text:"团队", - name:"team", - icon:"mdi mdi-account-group-outline", - classname:"btn btn-xs btn-info btn-dialog", - url:'/app/admin/user/team' - } - ] - } - ] - ] + columns: [columns] }; // 初始化表格 table.bootstrapTable(tableOptions); diff --git a/support/OpenIM.php b/support/OpenIM.php new file mode 100644 index 0000000..7109a6f --- /dev/null +++ b/support/OpenIM.php @@ -0,0 +1,38 @@ + 'openIM123', + 'baseUrl' => 'http://127.0.0.1:10002' + ]; + function get_admin_token($username='imAdmin'){ + + } + function get_user_token($username='imAdmin'){ + + } + /** + * 统一API请求方法 + */ + function api(){ + + } + /** + * 发送GET请求 + */ + function get(){ + + } + /** + * 发送POST请求 + */ + function post(){ + + } +} \ No newline at end of file diff --git a/support/OpenImSdk/Api/Auth.php b/support/OpenImSdk/Api/Auth.php new file mode 100644 index 0000000..61a3a8f --- /dev/null +++ b/support/OpenImSdk/Api/Auth.php @@ -0,0 +1,79 @@ + $userID, + 'secret' => Config::getSecret() + ]; + return Utils::send(Url::$getAdminToken, $data, '获取管理员token错误'); + } + + /** + * 获取用户token + * 直接从服务器获取,不使用缓存 + * @param string $userID 用户ID + * @param int $platformID 平台ID,默认为1 + * @return array + */ + public function getUserToken(string $userID, int $platformID = 1): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getUserToken, ['userID' => $userID, 'platformID' => $platformID], '获取用户token错误', $adminToken); + } + + /** + * 强制登出 + * @param string $userID 要登出的用户ID + * @param int $platformID 平台ID,默认为1 + * @return array + */ + public function forceLogout(string $userID, int $platformID = 1): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + + // 清除本地缓存的用户token + Utils::clearToken($userID); + + return Utils::send(Url::$forceLogout, ['userID' => $userID, 'platformID' => $platformID], '强制登出错误', $adminToken); + } + + /** + * 解析当前用户token + * @param string $token 用户token + * @return array + */ + public function parseToken(string $token): array + { + return Utils::send(Url::$parseToken, [], '解析当前用户token错误', $token); + } + + + /** + * 用户登录 (旧版,建议使用getUserToken) + * @param string $userID 用户ID + * @return array + */ + public function userToken(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$userToken, ['userID' => $userID], '用户登录错误', $adminToken); + } +} diff --git a/support/OpenImSdk/Api/Conversation.php b/support/OpenImSdk/Api/Conversation.php new file mode 100644 index 0000000..14be9a7 --- /dev/null +++ b/support/OpenImSdk/Api/Conversation.php @@ -0,0 +1,77 @@ + $userID, + 'pagination' => [ + 'pageNumber' => $pageNumber, + 'showNumber' => $showNumber + ] + ]; + return Utils::send(Url::$getOwnerConversation, $data, '获取当前用户分页会话列表失败', $adminToken); + } + + /** + * 获取排序的会话列表 + * @param string $userID 用户ID + * @return array + */ + public function getSortedConversationList(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getSortedConversationList, ['userID' => $userID], '获取排序的会话列表失败', $adminToken); + } + + /** + * 为多个用户设置相同会话ID的字段 + * @param string $conversationID 会话ID + * @param array $userIDs 用户ID列表 + * @param int $recvMsgOpt 接收消息选项 + * @param bool $isPinned 是否置顶 + * @param bool $isPrivateChat 是否私聊 + * @param int $groupAtType 群@类型 + * @param string $ex 扩展字段 + * @param bool $isMsgDestruct 是否开启消息销毁 + * @param int $msgDestructTime 消息销毁时间 + * @param int $burnDuration 阅后即焚时长 + * @return array + */ + public function setConversations(string $conversationID, array $userIDs, int $recvMsgOpt = 0, bool $isPinned = false, bool $isPrivateChat = false, int $groupAtType = 0, string $ex = '', bool $isMsgDestruct = false, int $msgDestructTime = 0, int $burnDuration = 0): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'conversationID' => $conversationID, + 'userIDs' => $userIDs, + 'conversation' => [ + 'recvMsgOpt' => $recvMsgOpt, + 'isPinned' => $isPinned, + 'isPrivateChat' => $isPrivateChat, + 'groupAtType' => $groupAtType, + 'ex' => $ex, + 'isMsgDestruct' => $isMsgDestruct, + 'msgDestructTime' => $msgDestructTime, + 'burnDuration' => $burnDuration + ] + ]; + return Utils::send(Url::$setConversations, $data, '为多个用户设置相同会话ID的字段失败', $adminToken); + } +} diff --git a/support/OpenImSdk/Api/Friend.php b/support/OpenImSdk/Api/Friend.php new file mode 100644 index 0000000..140b97c --- /dev/null +++ b/support/OpenImSdk/Api/Friend.php @@ -0,0 +1,212 @@ + $ownerUserID, 'blackUserID' => $blackUserID]; + return Utils::send(Url::$addBlack, $data, '添加黑名单错误', $adminToken); + } + + /** + * 添加好友 + * @param string $fromUserID 发送者ID + * @param string $toUserID 接收者ID + * @param string $reqMsg 请求消息 + * @return array + */ + public function addFriend(string $fromUserID, string $toUserID, string $reqMsg): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = ['fromUserID' => $fromUserID, 'toUserID' => $toUserID, 'reqMsg' => $reqMsg]; + return Utils::send(Url::$addFriend, $data, '添加好友错误', $adminToken); + } + + /** + * 同意/拒绝好友请求 + * @param string $ownerUserID 处理者ID + * @param string $friendUserID 好友ID + * @param string $handleMsg 处理消息 + * @param int $handleResult 处理结果,1同意,2拒绝 + * @return array + */ + public function addFriendResponse(string $ownerUserID, string $friendUserID, string $handleMsg, int $handleResult): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'ownerUserID' => $ownerUserID, + 'friendUserID' => $friendUserID, + 'handleMsg' => $handleMsg, + 'handleResult' => $handleResult + ]; + return Utils::send(Url::$addFriendResponse, $data, '同意/拒绝好友请求错误', $adminToken); + } + + /** + * 删除好友 + * @param string $ownerUserID 用户ID + * @param string $friendUserID 好友ID + * @return array + */ + public function deleteFriend(string $ownerUserID, string $friendUserID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = ['ownerUserID' => $ownerUserID, 'friendUserID' => $friendUserID]; + return Utils::send(Url::$deleteFriend, $data, '删除好友错误', $adminToken); + } + + /** + * 获取黑名单列表 + * @param string $userID 用户ID + * @return array + */ + public function getBlackList(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getBlackList, ['userID' => $userID], '获取黑名单列表错误', $adminToken); + } + + /** + * 获取好友申请列表(收到的申请) + * @param string $userID 用户ID + * @return array + */ + public function getFriendApplyList(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getFriendApplyList, ['userID' => $userID], '获取好友申请列表错误', $adminToken); + } + + /** + * 获取用户的好友列表 + * @param string $userID 用户ID + * @return array + */ + public function getFriendList(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getFriendList, ['userID' => $userID], '获取用户的好友列表错误', $adminToken); + } + + /** + * 获取自己的好友申请列表(发出的申请) + * @param string $userID 用户ID + * @return array + */ + public function getSelfFriendApplyList(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getSelfFriendApplyList, ['userID' => $userID], '获取自己的好友申请列表错误', $adminToken); + } + + /** + * 批量导入好友 + * @param string $ownerUserID 用户ID + * @param array $friendUserIDs 好友ID列表 + * @return array + */ + public function importFriend(string $ownerUserID, array $friendUserIDs = []): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = ['ownerUserID' => $ownerUserID, 'friendUserIDs' => $friendUserIDs]; + return Utils::send(Url::$importFriend, $data, '批量导入好友错误', $adminToken); + } + + /** + * 检查用户之间是否为好友 + * @param string $userID1 用户ID1 + * @param string $userID2 用户ID2 + * @return array + */ + public function isFriend(string $userID1, string $userID2): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = ['userID1' => $userID1, 'userID2' => $userID2]; + return Utils::send(Url::$isFriend, $data, '检查用户之间是否为好友错误', $adminToken); + } + + /** + * 把用户移除黑名单 + * @param string $ownerUserID 用户ID + * @param string $blackUserID 被移除黑名单的用户ID + * @return array + */ + public function removeBlack(string $ownerUserID, string $blackUserID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = ['ownerUserID' => $ownerUserID, 'blackUserID' => $blackUserID]; + return Utils::send(Url::$removeBlack, $data, '把用户移除黑名单错误', $adminToken); + } + + /** + * 设置好友备注 + * @param string $fromUserID 用户ID + * @param string $toUserID 好友ID + * @param string $remark 备注 + * @return array + */ + public function setFriendRemark(string $fromUserID, string $toUserID, string $remark): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = ['fromUserID' => $fromUserID, 'toUserID' => $toUserID, 'remark' => $remark]; + return Utils::send(Url::$setFriendRemark, $data, '设置好友备注错误', $adminToken); + } + + /** + * 更新好友信息 + * @param string $ownerUserID 用户ID + * @param string $friendUserID 好友ID + * @param string $remark 备注 + * @param bool $isPinned 是否置顶 + * @param string $ex 扩展字段 + * @return array + */ + public function updateFriends(string $ownerUserID, string $friendUserID, string $remark = '', bool $isPinned = false, string $ex = ''): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'ownerUserID' => $ownerUserID, + 'friendUserID' => $friendUserID + ]; + + // 只添加非空参数 + if ($remark !== '') { + $data['remark'] = $remark; + } + + if ($isPinned) { + $data['isPinned'] = $isPinned; + } + + if ($ex !== '') { + $data['ex'] = $ex; + } + + return Utils::send(Url::$updateFriends, $data, '更新好友信息失败', $adminToken); + } +} diff --git a/support/OpenImSdk/Api/Group.php b/support/OpenImSdk/Api/Group.php new file mode 100644 index 0000000..aa065e5 --- /dev/null +++ b/support/OpenImSdk/Api/Group.php @@ -0,0 +1,391 @@ + $ownerUserID, + 'memberUserIDs' => $memberUserIDs, + 'adminUserIDs' => $adminUserIDs, + 'groupInfo' => [ + 'groupID' => $groupID, + 'groupName' => $groupName, + 'notification' => $notification, + 'introduction' => $introduction, + 'faceURL' => $faceURL, + 'ex' => $ex, + 'groupType' => $groupType, + 'needVerification' => $needVerification, + 'lookMemberInfo' => $lookMemberInfo, + 'applyMemberFriend' => $applyMemberFriend + ] + ]; + return Utils::send(Url::$createGroup, $data, '创建群组失败', $adminToken); + } + + /** + * 申请加入群组 + * @param string $token 用户token + * @param string $groupID 群组ID + * @param string $reqMsg 申请消息 + * @param int $joinSource 加入来源 + * @return array + */ + public function joinGroup(string $token, string $groupID, string $reqMsg = '', int $joinSource = 0): array + { + $data = [ + 'groupID' => $groupID, + 'reqMsg' => $reqMsg, + 'joinSource' => $joinSource + ]; + return Utils::send(Url::$joinGroup, $data, '申请加入群组失败', $token); + } + + /** + * 退出群组 + * @param string $token 用户token + * @param string $groupID 群组ID + * @return array + */ + public function quitGroup(string $token, string $groupID): array + { + return Utils::send(Url::$quitGroup, ['groupID' => $groupID], '退出群组失败', $token); + } + + /** + * 获取群组信息 + * @param array $groupIDs 群组ID列表 + * @return array + */ + public function getGroupsInfo(array $groupIDs): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getGroupsInfo, ['groupIDs' => $groupIDs], '获取群组信息失败', $adminToken); + } + + /** + * 获取群成员列表 + * @param string $groupID 群组ID + * @param int $filter 过滤类型,0所有,1群主,2管理员,3普通成员,4禁言,5进入黑名单 + * @param int $offset 偏移量 + * @param int $count 数量 + * @return array + */ + public function getGroupMemberList(string $groupID, int $filter = 0, int $offset = 0, int $count = 100): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'filter' => $filter, + 'offset' => $offset, + 'count' => $count + ]; + return Utils::send(Url::$getGroupMemberList, $data, '获取群成员列表失败', $adminToken); + } + + /** + * 获取指定群成员信息 + * @param string $groupID 群组ID + * @param array $userIDs 用户ID列表 + * @return array + */ + public function getGroupMembersInfo(string $groupID, array $userIDs): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'userIDs' => $userIDs + ]; + return Utils::send(Url::$getGroupMembersInfo, $data, '获取指定群成员信息失败', $adminToken); + } + + /** + * 将用户拉入群组 + * @param string $groupID 群组ID + * @param string $inviterUserID 邀请者ID + * @param array $invitedUserIDList 被邀请的用户ID列表 + * @param string $reason 邀请原因 + * @return array + */ + public function inviteUserToGroup(string $groupID, string $inviterUserID, array $invitedUserIDList, string $reason = ''): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'inviterUserID' => $inviterUserID, + 'invitedUserIDList' => $invitedUserIDList, + 'reason' => $reason, + ]; + return Utils::send(Url::$inviteUserToGroup, $data, '将用户拉入群组失败', $adminToken); + } + + /** + * 踢出群成员 + * @param string $groupID 群组ID + * @param string $kickUserID 踢出者ID + * @param array $kickedUserIDs 被踢出的用户ID列表 + * @param string $reason 踢出原因 + * @return array + */ + public function kickGroupMember(string $groupID, string $kickUserID, array $kickedUserIDs, string $reason = ''): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'kickUserID' => $kickUserID, + 'kickedUserIDs' => $kickedUserIDs, + 'reason' => $reason + ]; + return Utils::send(Url::$kickGroupMember, $data, '踢出群成员失败', $adminToken); + } + + /** + * 转让群主 + * @param string $groupID 群组ID + * @param string $oldOwnerUserID 原群主ID + * @param string $newOwnerUserID 新群主ID + * @return array + */ + public function transferGroupOwner(string $groupID, string $oldOwnerUserID, string $newOwnerUserID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'oldOwnerUserID' => $oldOwnerUserID, + 'newOwnerUserID' => $newOwnerUserID + ]; + return Utils::send(Url::$transferGroupOwner, $data, '转让群主失败', $adminToken); + } + + /** + * 获取用户加入的群组列表 + * @param string $userID 用户ID + * @return array + */ + public function getJoinedGroupList(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getJoinedGroupList, ['userID' => $userID], '获取用户加入的群组列表失败', $adminToken); + } + + /** + * 解散群组 + * @param string $groupID 群组ID + * @return array + */ + public function dismissGroup(string $groupID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$dismissGroup, ['groupID' => $groupID], '解散群组失败', $adminToken); + } + + /** + * 设置群成员昵称 + * @param string $groupID 群组ID + * @param string $userID 用户ID + * @param string $nickname 群内昵称 + * @return array + */ + public function setGroupMemberNickname(string $groupID, string $userID, string $nickname): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'userID' => $userID, + 'nickname' => $nickname + ]; + return Utils::send(Url::$setGroupMemberNickname, $data, '设置群成员昵称失败', $adminToken); + } + + /** + * 设置群成员信息 + * @param string $groupID 群组ID + * @param string $userID 用户ID + * @param array $data 群成员信息 + * @return array + */ + public function setGroupMemberInfo(string $groupID, string $userID, array $data): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = array_merge([ + 'groupID' => $groupID, + 'userID' => $userID + ], $data); + return Utils::send(Url::$setGroupMemberInfo, $data, '设置群成员信息失败', $adminToken); + } + + /** + * 获取群成员用户ID列表 + * @param string $groupID 群组ID + * @return array + */ + public function getGroupMemberUserIDs(string $groupID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getGroupMemberUserIDs, ['groupID' => $groupID], '获取群成员用户ID列表失败', $adminToken); + } + + /** + * 获取群成员列表 + * @param string $groupID 群组ID + * @param int $offset 偏移量 + * @param int $count 数量 + * @return array + */ + public function getGroupAllMemberList(string $groupID, int $offset = 0, int $count = 100): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'pagination' => [ + 'pageNumber' => intval($offset / $count) + 1, + 'showNumber' => $count + ] + ]; + return Utils::send(Url::$getGroupAllMemberList, $data, '获取群成员列表失败', $adminToken); + } + + /** + * 获取用户加群申请列表 + * @param string $userID 用户ID + * @return array + */ + public function getUserReqGroupApplicationList(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getUserReqGroupApplicationList, ['userID' => $userID], '获取用户加群申请列表失败', $adminToken); + } + + /** + * 获取指定用户对指定群组的加群请求 + * @param string $groupID 群组ID + * @param array $userIDs 用户ID列表 + * @return array + */ + public function getGroupApplicationListByUserID(string $groupID, array $userIDs): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'userIDs' => $userIDs + ]; + return Utils::send(Url::$getGroupUsersReqApplicationList, $data, '获取指定用户对指定群组的加群请求失败', $adminToken); + } + + /** + * 处理群组申请 + * @param string $groupID 群组ID + * @param string $fromUserID 申请者ID + * @param string $handledUserID 处理者ID + * @param int $handleResult 处理结果,1同意,2拒绝 + * @param string $handleMsg 处理消息 + * @return array + */ + public function groupApplicationResponse(string $groupID, string $fromUserID, string $handledUserID, int $handleResult, string $handleMsg = ''): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'groupID' => $groupID, + 'fromUserID' => $fromUserID, + 'handledUserID' => $handledUserID, + 'handleResult' => $handleResult, + 'handleMsg' => $handleMsg + ]; + return Utils::send(Url::$groupApplicationResponse, $data, '处理群组申请失败', $adminToken); + } + + /** + * 禁言群组,只有群主和管理员可以发送消息 + * @param string $groupID 群组ID + * @return array + */ + public function muteGroup(string $groupID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$muteGroup, ['groupID' => $groupID], '禁言群组失败', $adminToken); + } + + /** + * 取消禁言群组,所有成员都可以发送消息 + * @param string $groupID 群组ID + * @return array + */ + public function cancelMuteGroup(string $groupID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$cancelMuteGroup, ['groupID' => $groupID], '取消禁言群组失败', $adminToken); + } + + /** + * 禁言群成员 + * @param string $groupID 群组ID + * @param string $userID 群成员ID + * @param int $mutedSeconds 禁言时间(秒) + * @return array + */ + public function muteGroupMember(string $groupID, string $userID, int $mutedSeconds = 0): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = ['groupID' => $groupID, 'userID' => $userID, 'mutedSeconds' => $mutedSeconds]; + return Utils::send(Url::$muteGroupMember, $data, '禁言群成员失败', $adminToken); + } + + /** + * 取消禁言群成员 + * @param string $groupID 群组ID + * @param string $userID 群成员ID + * @return array + */ + public function cancelMuteGroupMember(string $groupID, string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = ['groupID' => $groupID, 'userID' => $userID]; + return Utils::send(Url::$cancelMuteGroupMember, $data, '取消禁言群成员失败', $adminToken); + } +} diff --git a/support/OpenImSdk/Api/Message.php b/support/OpenImSdk/Api/Message.php new file mode 100644 index 0000000..1fb6041 --- /dev/null +++ b/support/OpenImSdk/Api/Message.php @@ -0,0 +1,217 @@ + $sendID, + 'senderNickname' => $senderNickname, + 'senderFaceURL' => $senderFaceURL, + 'senderPlatformID' => $senderPlatformID, + 'contentType' => $contentType, + 'sessionType' => $sessionType, + 'isOnlineOnly' => $isOnlineOnly, + 'notOfflinePush' => $notOfflinePush, + 'ex' => $ex + ]; + + // 根据会话类型设置recvID或groupID + if ($sessionType == 1 && !empty($recvID)) { + $data['recvID'] = $recvID; + } elseif ($sessionType == 2 && !empty($groupID)) { + $data['groupID'] = $groupID; + } + + // 设置消息内容 + if (empty($content)) { + $data['content'] = ['text' => '']; + } else { + $data['content'] = $content; + } + + // 设置发送时间,如果有的话 + if ($sendTime > 0) { + $data['sendTime'] = $sendTime; + } + + // 设置离线推送信息,如果有的话 + if (!empty($offlinePushInfo)) { + $data['offlinePushInfo'] = $offlinePushInfo; + } + + return Utils::send(Url::$sendMsg, $data, '发送消息失败', $adminToken); + } + + /** + * 批量发送消息 + * @param string $sendID 发送者ID + * @param string $senderNickname 发送者昵称 + * @param string $senderFaceURL 发送者头像 + * @param int $sessionType 会话类型 + * @param int $contentType 消息类型 + * @param string $content 消息内容 + * @return array + */ + public function batchSendMsg(string $sendID, string $senderNickname, string $senderFaceURL, int $sessionType, int $contentType, string $content): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'senderPlatformID' => 0, + 'sendID' => $sendID, + 'senderNickname' => $senderNickname, + 'senderFaceURL' => $senderFaceURL, + 'sessionType' => $sessionType, + 'contentType' => $contentType, + 'content' => ['text' => $content] + ]; + return Utils::send(Url::$batchSendMsg, $data, '批量发送消息失败', $adminToken); + } + + /** + * 清空用户消息 + * @param string $userID 用户ID + * @return array + */ + public function clearMsg(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$clearMsg, ['userID' => $userID], '清空用户消息失败', $adminToken); + } + + /** + * 根据seq列表删除消息 + * @param string $userID 用户ID + * @param string $conversationID 会话ID + * @param array $seqs seq列表 + * @return array + */ + public function delMsg(string $userID, string $conversationID, array $seqs): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'userID' => $userID, + 'conversationID' => $conversationID, + 'seqs' => $seqs + ]; + return Utils::send(Url::$delMsg, $data, '删除消息失败', $adminToken); + } + + /** + * 撤回消息 + * @param string $conversationID 会话ID + * @param string $seq 消息seq + * @param string $userID 用户ID + * @return array + */ + public function revokeMessage(string $conversationID, string $seq, string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'conversationID' => $conversationID, + 'seq' => intval($seq), + 'userID' => $userID + ]; + return Utils::send(Url::$revokeMessage, $data, '撤回消息失败', $adminToken); + } + + /** + * 发送业务通知 + * @param string $sendID 发送者ID + * @param string $recvID 接收者ID + * @param string $title 通知标题 + * @param string $content 通知内容 + * @param string $notificationUrl 通知点击跳转链接 + * @param string $ex 扩展字段 + * @return array + */ + public function sendBusinessNotification(string $sendID, string $recvID, string $title, string $content, string $notificationUrl = '', string $ex = ''): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'sendID' => $sendID, + 'recvID' => $recvID, + 'title' => $title, + 'content' => $content, + 'notificationUrl' => $notificationUrl, + 'ex' => $ex + ]; + return Utils::send(Url::$sendBusinessNotification, $data, '发送业务通知失败', $adminToken); + } + + /** + * 获取用户所有会话 + * @param string $userID 用户ID + * @return array + */ + public function getAllConversations(string $userID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getAllConversations, ['userID' => $userID], '获取用户所有会话失败', $adminToken); + } + + /** + * 根据会话ID获取会话 + * @param string $userID 用户ID + * @param string $conversationID 会话ID + * @return array + */ + public function getConversation(string $userID, string $conversationID): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'userID' => $userID, + 'conversationID' => $conversationID + ]; + return Utils::send(Url::$getConversation, $data, '获取会话失败', $adminToken); + } + + /** + * 根据会话ID列表获取会话 + * @param string $userID 用户ID + * @param array $conversationIDs 会话ID列表 + * @return array + */ + public function getConversations(string $userID, array $conversationIDs): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'userID' => $userID, + 'conversationIDs' => $conversationIDs + ]; + return Utils::send(Url::$getConversations, $data, '获取会话列表失败', $adminToken); + } +} diff --git a/support/OpenImSdk/Api/User.php b/support/OpenImSdk/Api/User.php new file mode 100644 index 0000000..478e46e --- /dev/null +++ b/support/OpenImSdk/Api/User.php @@ -0,0 +1,262 @@ + [ + 'pageNumber' => $pagination, + 'showNumber' => $showNumber + ] + ]; + return Utils::send(Url::$getUsers, $data, '获取用户列表错误', $adminToken); + } + + /** + * 获取用户在线状态 + * @param array $userIDList 用户ID列表 + * @return array + */ + public function getUsersOnlineStatus(array $userIDList): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getUsersOnlineStatus, ['userIDList' => $userIDList], '获取用户在线状态错误', $adminToken); + } + + /** + * 获取用户在线token详情 + * @param array $userIDList 用户ID列表 + * @return array + */ + public function getUsersOnlineTokenDetail(array $userIDList): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getUsersOnlineTokenDetail, ['userIDList' => $userIDList], '获取用户在线token详情错误', $adminToken); + } + + /** + * 获取订阅用户状态 + * @return array + */ + public function getSubscribeUsersStatus(): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getSubscribeUsersStatus, [], '获取订阅用户状态错误', $adminToken); + } + + /** + * 订阅用户状态 + * @param string $token 管理员token + * @param array $userIDList 用户ID列表 + * @return array + */ + public function subscribeUsersStatus(array $userIDList): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$subscribeUsersStatus, ['userIDList' => $userIDList], '订阅用户状态错误', $adminToken); + } + + /** + * 设置全局免打扰 + * @param int $globalRecvMsgOpt 全局消息接收选项 + * @return array + */ + public function setGlobalMsgRecvOpt(int $globalRecvMsgOpt): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$setGlobalMsgRecvOpt, ['globalRecvMsgOpt' => $globalRecvMsgOpt], '设置全局免打扰错误', $adminToken); + } + + /** + * 修改用户信息 + * @param string $userID 用户ID + * @param array $data 用户信息 + * @return array + */ + public function updateUserInfo(string $userID, array $data): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = array_merge(['userID' => $userID], $data); + return Utils::send(Url::$updateUserInfo, $data, '修改用户信息错误', $adminToken); + } + + /** + * 搜索通知账号 + * @param string $keyword 搜索关键词 + * @param int $pagination 页码 + * @param int $showNumber 每页数量 + * @return array + */ + public function searchNotificationAccount(string $keyword, int $pagination = 1, int $showNumber = 20): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'keyword' => $keyword, + 'pagination' => [ + 'pageNumber' => $pagination, + 'showNumber' => $showNumber + ] + ]; + return Utils::send(Url::$searchNotificationAccount, $data, '搜索通知账号错误', $adminToken); + } + + /** + * 添加通知账号 + * @param string $userID 用户ID + * @param string $nickname 昵称 + * @param string $faceURL 头像 + * @param int $gender 性别 + * @param string $phoneNumber 手机号 + * @param string $birth 生日 + * @param string $email 邮箱 + * @param string $ex 扩展字段 + * @return array + */ + public function addNotificationAccount(string $userID, string $nickname = '', string $faceURL = '', int $gender = 1, string $phoneNumber = '', string $birth = '', string $email = '', string $ex = ''): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'userID' => $userID, + 'nickname' => $nickname, + 'faceURL' => $faceURL, + 'gender' => $gender, + 'phoneNumber' => $phoneNumber, + 'birth' => $birth, + 'email' => $email, + 'ex' => $ex + ]; + return Utils::send(Url::$addNotificationAccount, $data, '添加通知账号错误'); + } + + /** + * 更新通知账号 + * @param string $userID 用户ID + * @param string $nickname 昵称 + * @param string $faceURL 头像 + * @param int $gender 性别 + * @param string $phoneNumber 手机号 + * @param string $birth 生日 + * @param string $email 邮箱 + * @param string $ex 扩展字段 + * @return array + */ + public function updateNotificationAccount(string $userID, string $nickname = '', string $faceURL = '', int $gender = 1, string $phoneNumber = '', string $birth = '', string $email = '', string $ex = ''): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'userID' => $userID, + 'nickname' => $nickname, + 'faceURL' => $faceURL, + 'gender' => $gender, + 'phoneNumber' => $phoneNumber, + 'birth' => $birth, + 'email' => $email, + 'ex' => $ex + ]; + return Utils::send(Url::$updateNotificationAccount, $data, '更新通知账号错误', $adminToken); + } + + /** + * 检查列表账户注册状态 + * @param array $checkUserIDList 用户ID列表 + * @return array + */ + public function accountCheck(array $checkUserIDList): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$accountCheck, ['checkUserIDList' => $checkUserIDList], '检查列表账户注册状态错误', $adminToken); + } + + /** + * 获取所有用户uid列表 + * @return array + */ + public function getAllUsersUid($page=1,$limit=100): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getAllUsersUid, ['pagination'=>['pageNumber'=>$page,"showNumber"=>$limit]], '获取所有用户uid列表错误', $adminToken); + } + + /** + * 获取自己的信息 + * @param string $userID 用户ID + * @return array + */ + public function getSelfUserInfo(string $userID): array + { + // 获取管理员token + $userToken = Utils::getUserToken($userID); + return Utils::send(Url::$getSelfUserInfo, ['userID' => $userID], '获取自己的信息错误', $userToken); + } + + /** + * 获取用户信息 + * @param array $userIDList 用户ID列表 + * @return array + */ + public function getUsersInfo(array $userIDList): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + return Utils::send(Url::$getUsersInfo, ['userIDList' => $userIDList], '获取用户信息错误', $adminToken); + } + + //Header + // + //Header Name Example Value Required Type Description + //operationID 1646445464564 Required string Used for global traceability, suggested as a unique timestamp per request + //token eyJhbxxxx3Xs Required string Admin token + + //Field Name Required Type Description + //users Required array List of users + //users.userID Required string User ID + //users.nickname Required string User nickname + //users.faceURL Required string User avatar URL + /** + * 用户注册 + * @param string $userID 用户ID + * @param string $nickname 昵称 + * @param string $faceURL 头像地址 + * @return array + */ + public function userRegister(string $userID, string $nickname = '', string $faceURL = ''): array + { + // 获取管理员token + $adminToken = Utils::getAdminToken(); + $data = [ + 'users' => [ + [ + 'userID' => $userID, + 'nickname' => $nickname, + 'faceURL' => $faceURL + ] + ] + ]; + return Utils::send(Url::$userRegister, $data, '注册IM错误', $adminToken); + } +} diff --git a/support/OpenImSdk/Client.php b/support/OpenImSdk/Client.php new file mode 100644 index 0000000..76eb4ac --- /dev/null +++ b/support/OpenImSdk/Client.php @@ -0,0 +1,75 @@ +auth = new Auth(); + $this->friend = new Friend(); + $this->group = new Group(); + $this->message = new Message(); + $this->user = new User(); + $this->conversation = new Conversation(); + } +} diff --git a/support/OpenImSdk/Core/Config.php b/support/OpenImSdk/Core/Config.php new file mode 100644 index 0000000..8aba346 --- /dev/null +++ b/support/OpenImSdk/Core/Config.php @@ -0,0 +1,39 @@ + 'http://127.0.0.1:10002', + 'secret' => 'openIM123', + ]; + + /** + * 设置配置项 + * @param array $config + * @return void + */ + public static function setConfig(array $config) + { + self::$config = array_merge(self::$config, $config); + } + + /** + * 获取密钥 + * @return string + */ + public static function getSecret(): string + { + return self::$config['secret']; + } + + /** + * 获取API主机地址 + * @return string + */ + public static function getHost(): string + { + return self::$config['host']; + } +} diff --git a/support/OpenImSdk/Core/TokenManager.php b/support/OpenImSdk/Core/TokenManager.php new file mode 100644 index 0000000..d83a125 --- /dev/null +++ b/support/OpenImSdk/Core/TokenManager.php @@ -0,0 +1,157 @@ +defaultTokenExpire = $seconds; + return $this; + } + + /** + * 获取管理员Token + * @param string $userID 管理员ID + * @return string|null + */ + public function getAdminToken(string $userID = 'imAdmin'): ?string + { + $key = "admin_token_{$userID}"; + $tokenData = $this->getCache($key); + + if (!$tokenData) { + // Token不存在或已过期,需要重新获取 + return null; + } + + $data = json_decode($tokenData, true); + return $data['token'] ?? null; + } + + /** + * 保存管理员Token + * @param string $userID 管理员ID + * @param string $token Token + * @param int|null $expireTimeSeconds Token过期时间(秒) + * @return bool + */ + public function saveAdminToken(string $userID, string $token, ?int $expireTimeSeconds = null): bool + { + $key = "admin_token_{$userID}"; + $expireTime = $expireTimeSeconds ?? $this->defaultTokenExpire; + + // 存储token和过期时间 + $data = [ + 'token' => $token, + 'expireTimeSeconds' => $expireTime + ]; + + return $this->setCache($key, json_encode($data), $expireTime); + } + + /** + * 获取用户Token + * @param string $userID 用户ID + * @return string|null + */ + public function getUserToken(string $userID): ?string + { + $key = "user_token_{$userID}"; + $tokenData = $this->getCache($key); + + if (!$tokenData) { + // Token不存在或已过期,需要重新获取 + return null; + } + + $data = json_decode($tokenData, true); + return $data['token'] ?? null; + } + + /** + * 保存用户Token + * @param string $userID 用户ID + * @param string $token Token + * @param int|null $expireTimeSeconds Token过期时间(秒) + * @return bool + */ + public function saveUserToken(string $userID, string $token, ?int $expireTimeSeconds = null): bool + { + $key = "user_token_{$userID}"; + $expireTime = $expireTimeSeconds ?? $this->defaultTokenExpire; + + // 存储token和过期时间 + $data = [ + 'token' => $token, + 'expireTimeSeconds' => $expireTime + ]; + + return $this->setCache($key, json_encode($data), $expireTime); + } + + /** + * 清除Token + * @param string $userID 用户ID + * @param bool $isAdmin 是否为管理员Token + * @return bool + */ + public function clearToken(string $userID, bool $isAdmin = false): bool + { + $key = $isAdmin ? "admin_token_{$userID}" : "user_token_{$userID}"; + return $this->deleteCache($key); + } + + /** + * 获取缓存 + * @param string $key 缓存键 + * @return string|null + */ + private function getCache(string $key): ?string + { + return cache($key); + } + + /** + * 设置缓存 + * @param string $key 缓存键 + * @param string $value 缓存值 + * @param int $expire 过期时间(秒) + * @return bool + */ + private function setCache(string $key, string $value, int $expire): bool + { + return cache($key,$value,$expire); + } + + /** + * 删除缓存 + * @param string $key 缓存键 + * @return bool + */ + private function deleteCache(string $key): bool + { + return cache($key,null); + } +} diff --git a/support/OpenImSdk/Core/Url.php b/support/OpenImSdk/Core/Url.php new file mode 100644 index 0000000..a0ce0b2 --- /dev/null +++ b/support/OpenImSdk/Core/Url.php @@ -0,0 +1,96 @@ +post($uri, $options)->getBody()->getContents(); + } + + /** + * 发送API请求 + * @param string $path API路径 + * @param array $data 请求数据 + * @param string $errMsg 错误信息 + * @param string $token 认证令牌 + * @return array 响应数据 + */ + public static function send(string $path, array $data, string $errMsg, string $token = ''): array|bool + { + $url = Url::buildUrl($path); + //cp($url); + $res = json_decode(self::request($url, $data, $token), true); + if($res['errCode'] !==0 ){ + throw new \Exception($res['errMsg'],$res['errCode']); + //throw new \Exception($res['errDlt'],$res['errCode']); + } + if(isset($res['data'])){ + return $res['data']; + } + return true; + + try { + $url = Url::buildUrl($path); + cp($url); + return json_decode(self::request($url, $data, $token), true); + } catch (GuzzleException $e) { + return ['errCode' => $e->getCode(), 'errMsg' => $errMsg, 'errDlt' => $e->getMessage()]; + } catch (ValidatorException $e) { + return ['errCode' => 400, 'errMsg' => $errMsg, 'errDlt' => $e->getMessage()]; + } + } + + /** + * 获取管理员Token + * 如果缓存中没有,则自动获取并缓存 + * @param string $userID 管理员ID + * @return string|null + */ + public static function getAdminToken(string $userID = 'imAdmin'): ?string + { + $tokenManager = self::getTokenManager(); + $token = $tokenManager->getAdminToken($userID); + + if (!$token) { + // 从服务器获取新的Token + $result = self::send(Url::$getAdminToken, [ + 'userID' => $userID, + 'secret' => Config::getSecret() + ], '获取管理员Token失败'); + $token = $result['token']; + // 使用API返回的过期时间 + $expireTimeSeconds = $result['expireTimeSeconds'] ?? null; + + // 保存token,使用API返回的过期时间 + $tokenManager->saveAdminToken($userID, $token, $expireTimeSeconds); + + } + + return $token; + } + + /** + * 获取用户Token + * 如果缓存中没有,则自动获取并缓存 + * @param string $userID 用户ID + * @param int $platformID 平台ID + * @return string|null + */ + public static function getUserToken(string $userID, int $platformID = 1): ?string + { + $tokenManager = self::getTokenManager(); + $token = $tokenManager->getUserToken($userID); + + if (!$token) { + // 从服务器获取新的Token + $adminToken = self::getAdminToken(); + if (!$adminToken) { + return null; + } + + $result = self::send(Url::$getUserToken, [ + 'userID' => $userID, + 'platformID' => $platformID + ], '获取用户Token失败', $adminToken); + + $token = $result['token']; + + // 使用API返回的过期时间 + $expireTimeSeconds = $result['expireTimeSeconds'] ?? null; + + // 保存token,使用API返回的过期时间 + $tokenManager->saveUserToken($userID, $token, $expireTimeSeconds); + } + + return $token; + } + + /** + * 清除Token缓存 + * @param string $userID 用户ID + * @param bool $isAdmin 是否为管理员Token + * @return bool + */ + public static function clearToken(string $userID, bool $isAdmin = false): bool + { + return self::getTokenManager()->clearToken($userID, $isAdmin); + } +} diff --git a/support/OpenImSdk/Core/Validator.php b/support/OpenImSdk/Core/Validator.php new file mode 100644 index 0000000..00e1511 --- /dev/null +++ b/support/OpenImSdk/Core/Validator.php @@ -0,0 +1,91 @@ + 'max:64', + 'userID1' => 'max:64', + 'userID2' => 'max:64', + 'ownerUserID' => 'max:64', + 'friendUserID' => 'max:64', + 'blackUserID' => 'max:64', + 'fromUserID' => 'max:64', + 'toUserID' => 'max:64', + 'sendID' => 'max:64', + 'recvID' => 'max:64', + 'inviterUserID' => 'max:64', + 'nickname' => 'max:255', + 'faceURL' => 'max:255', + 'gender' => 'in:1,2', + 'groupID' => 'max:64', + 'groupName' => 'max:255', + 'introduction' => 'max:255', + 'notification' => 'max:255', + 'groupType' => 'in:0,1,2', + 'oldOwnerUserID' => 'max:64', + 'newOwnerUserID' => 'max:64', + 'conversationID' => 'max:128', + 'handleResult' => 'in:1,2', + ]; + + /** + * 验证数组 + * @param array $data 要验证的数据 + * @return array 验证后的数据 + * @throws ValidatorException + */ + public static function validateArray(array $data): array + { + foreach ($data as $field => $value) { + foreach (self::$rules as $key => $rules) { + if ($field == $key) { + $ruleList = explode('|', $rules); + foreach ($ruleList as $rule) { + $ruleParts = explode(':', $rule); + $method = $ruleParts[0]; + $param = $ruleParts[1] ?? null; + self::$method($field, $value, $param); + } + } + } + } + return $data; + } + + /** + * 长度最大验证 + * @param string $field 字段名 + * @param mixed $value 字段值 + * @param int $maxLength 最大长度 + * @throws ValidatorException + */ + private static function max(string $field, $value, $maxLength) + { + if (strlen($value) > (int)$maxLength) { + throw new ValidatorException("参数 {$field} 长度不能超过 {$maxLength} 位"); + } + } + + /** + * 枚举值验证 + * @param string $field 字段名 + * @param mixed $value 字段值 + * @param string $allowedValues 允许的值(逗号分隔) + * @throws ValidatorException + */ + private static function in(string $field, $value, $allowedValues) + { + $allowed = explode(',', $allowedValues); + if (!in_array($value, $allowed)) { + throw new ValidatorException("参数 {$field} 的值必须是以下之一: {$allowedValues},当前值: {$value}"); + } + } +} diff --git a/support/OpenImSdk/Exception/ValidatorException.php b/support/OpenImSdk/Exception/ValidatorException.php new file mode 100644 index 0000000..f0a1b76 --- /dev/null +++ b/support/OpenImSdk/Exception/ValidatorException.php @@ -0,0 +1,8 @@ + 基于 [OpenIM](https://github.com/orgs/OpenIMSDK) 的 PHP SDK +> +> API文档: [https://docs.openim.io/restapi/apis/introduction](https://docs.openim.io/restapi/apis/introduction) + +## 安装 + +```bash +composer require MrYzYs/OpenImSdk +``` + +## 配置 + +```php +$config = [ + 'host' => 'http://127.0.0.1:10002', // OpenIM API地址 + 'secret' => 'openIM123', // OpenIM密钥 +]; +``` + +## 基本使用 + +### 初始化客户端 + +```php +// 使用文件缓存(默认) +$IM = new OpenImSdk\Client($config); + +// 使用Redis缓存 (phpredis) +$redis = new Redis(); +$redis->connect('127.0.0.1', 6379); +$IM = new OpenImSdk\Client($config, $redis); + +// 使用Redis缓存 (predis) +$redis = new Predis\Client([ + 'scheme' => 'tcp', + 'host' => '127.0.0.1', + 'port' => 6379, +]); +$IM = new OpenImSdk\Client($config, $redis); + +// 指定文件缓存目录 +$IM = new OpenImSdk\Client($config, null, '/path/to/cache'); +``` + +### 认证管理 + +```php +// 用户注册 +$result = $IM->auth->userRegister('user123', '测试用户', 'https://example.com/avatar.jpg'); + +// 强制登出 +$logout = $IM->auth->forceLogout('user123'); + +// 解析token +$tokenInfo = $IM->auth->parseToken($userToken); +``` + +### 用户管理 + +```php +// 获取用户列表 +$users = $IM->user->getUsers($adminToken); + +// 获取用户在线状态 +$onlineStatus = $IM->user->getUsersOnlineStatus($adminToken, ['user123', 'user456']); + +// 更新用户信息 +$updateUser = $IM->user->updateUserInfo($adminToken, 'user123', [ + 'nickname' => '新昵称', + 'faceURL' => 'https://example.com/new-avatar.jpg' +]); +``` + +### 消息管理 + +```php +// 发送消息 +$sendMsg = $IM->message->sendMsg( + $adminToken, + 'admin', // 发送者ID + 'user123', // 接收者ID + '', // 群组ID(单聊时为空) + '管理员', // 发送者昵称 + 'https://example.com/admin-avatar.jpg', // 发送者头像 + 1, // 发送者平台ID + ['text' => '你好,这是一条测试消息'], // 消息内容 + 101, // 消息类型(101为文本消息) + 1 // 会话类型(1为单聊) +); + +// 撤回消息 +$revokeMsg = $IM->message->revokeMessage( + $adminToken, + 'single_user123', // 会话ID + '123456', // 消息seq + 'user123' // 用户ID +); +``` + +### 会话管理 + +```php +// 获取用户分页会话列表 +$conversations = $IM->conversation->getOwnerConversation( + $adminToken, + 'user123', // 用户ID + 1, // 页码 + 20 // 每页数量 +); + +// 获取排序的会话列表 +$sortedConversations = $IM->conversation->getSortedConversationList( + $adminToken, + 'user123' // 用户ID +); +``` + +### 好友管理 + +```php +// 批量导入好友 +$importFriend = $IM->friend->importFriend( + $adminToken, + 'user123', // 用户ID + ['user456', 'user789'] // 好友ID列表 +); + +// 获取好友列表 +$friendList = $IM->friend->getFriendList( + $adminToken, + 'user123' // 用户ID +); + +// 检查是否为好友 +$isFriend = $IM->friend->isFriend( + $adminToken, + 'user123', // 用户ID1 + 'user456' // 用户ID2 +); +``` + +### 群组管理 + +```php +// 创建群组 +$createGroup = $IM->group->createGroup( + $adminToken, + 'user123', // 群主ID + [], // 普通成员ID列表 + [], // 管理员ID列表 + '测试群组', // 群名称 + '', // 群ID(可选) + 'https://example.com/group-avatar.jpg', // 群头像 + '群简介', // 群简介 + '群公告' // 群公告 +); + +// 邀请用户加入群组 +$inviteToGroup = $IM->group->inviteUserToGroup( + $adminToken, + 'group123', // 群组ID + 'user123', // 邀请者ID + ['user456', 'user789'] // 被邀请的用户ID列表 +); + +// 获取群成员列表 +$groupMembers = $IM->group->getGroupAllMemberList( + $adminToken, + 'group123', // 群组ID + 0, // 偏移量 + 100 // 数量 +); + +// 申请加入群组 +$joinGroup = $IM->group->joinGroup( + $userToken, // 用户token + 'group123', // 群组ID + '我想加入这个群组' // 申请消息 +); + +// 处理群组申请 +$handleApplication = $IM->group->groupApplicationResponse( + $adminToken, + 'group123', // 群组ID + 'user456', // 申请者ID + 'user123', // 处理者ID + 1, // 处理结果,1同意,2拒绝 + '欢迎加入' // 处理消息 +); + +// 踢出群成员 +$kickMember = $IM->group->kickGroupMember( + $adminToken, + 'group123', // 群组ID + 'user123', // 踢出者ID + ['user456'], // 被踢出的用户ID列表 + '违反群规' // 踢出原因 +); + +// 转让群主 +$transferOwner = $IM->group->transferGroupOwner( + $adminToken, + 'group123', // 群组ID + 'user123', // 原群主ID + 'user456' // 新群主ID +); +``` + +## 目录结构 + +``` +src/ +├── Api/ # API接口类 +│ ├── Auth.php # 认证相关API +│ ├── Conversation.php # 会话相关API +│ ├── Friend.php # 好友相关API +│ ├── Group.php # 群组相关API +│ ├── Message.php # 消息相关API +│ └── User.php # 用户相关API +├── Core/ # 核心类 +│ ├── Config.php # 配置类 +│ ├── TokenManager.php # Token管理类 +│ ├── Url.php # URL管理 +│ ├── Utils.php # 工具类 +│ └── Validator.php # 验证器 +├── Exception/ # 异常处理 +│ └── ValidatorException.php # 验证异常 +└── Client.php # 客户端入口 +``` + +许可协议: GPL-V3 \ No newline at end of file diff --git a/support/OpenImSdk/composer.json b/support/OpenImSdk/composer.json new file mode 100644 index 0000000..05d0f83 --- /dev/null +++ b/support/OpenImSdk/composer.json @@ -0,0 +1,24 @@ +{ + "name": "mryzys/openim-sdk", + "description": "OpenIM PHP SDK - A PHP client for OpenIM REST API", + "keywords": [ + "openimsdk", "openim", "im", "chat", "messaging" + ], + "license": "GPL-3.0-only", + "authors": [ + { + "name": "mryzys", + "email": "mryzys@163.com" + } + ], + "require": { + "php": "^8.0", + "guzzlehttp/guzzle": "^7.5.0", + "ext-json": "*" + }, + "autoload": { + "psr-4": { + "OpenImSdk\\": "src/" + } + } +} diff --git a/support/helpers.php b/support/helpers.php index efa8b7c..5ad4b10 100644 --- a/support/helpers.php +++ b/support/helpers.php @@ -1,4 +1,2 @@