├── .gitignore ├── .php_cs ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Application.php ├── Attendance │ ├── Client.php │ └── ServiceProvider.php ├── Auth │ ├── HasStateParameter.php │ ├── InvalidStateException.php │ ├── OAuthClient.php │ ├── ServiceProvider.php │ └── SsoClient.php ├── Blackboard │ ├── Client.php │ └── ServiceProvider.php ├── Calendar │ ├── Client.php │ └── ServiceProvider.php ├── Callback │ ├── Client.php │ └── ServiceProvider.php ├── Chat │ ├── Client.php │ └── ServiceProvider.php ├── Checkin │ ├── Client.php │ └── ServiceProvider.php ├── Contact │ ├── Client.php │ └── ServiceProvider.php ├── Conversation │ ├── Client.php │ └── ServiceProvider.php ├── Department │ ├── Client.php │ └── ServiceProvider.php ├── H5app │ ├── Client.php │ └── ServiceProvider.php ├── Health │ ├── Client.php │ └── ServiceProvider.php ├── Kernel │ ├── AccessToken.php │ ├── BaseClient.php │ ├── Concerns │ │ └── InteractsWithCache.php │ ├── Encryption │ │ └── Encryptor.php │ ├── Exceptions │ │ ├── Exception.php │ │ ├── InvalidArgumentException.php │ │ ├── InvalidCredentialsException.php │ │ └── RuntimeException.php │ ├── Http │ │ └── Client.php │ ├── Providers │ │ ├── AccessTokenServiceProvider.php │ │ ├── ClientServiceProvider.php │ │ ├── EncryptionServiceProvider.php │ │ ├── LoggerServiceProvider.php │ │ ├── RequestServiceProvider.php │ │ └── ServerServiceProvider.php │ └── Server.php ├── Media │ ├── Client.php │ └── ServiceProvider.php ├── Messages │ ├── File.php │ ├── Image.php │ ├── Link.php │ ├── Message.php │ ├── Text.php │ └── Voice.php ├── Microapp │ ├── Client.php │ └── ServiceProvider.php ├── Process │ ├── Client.php │ └── ServiceProvider.php ├── Report │ ├── Client.php │ └── ServiceProvider.php ├── Robot.php ├── Role │ ├── Client.php │ └── ServiceProvider.php ├── Schedule │ ├── Client.php │ └── ServiceProvider.php ├── User │ ├── Client.php │ └── ServiceProvider.php └── helpers.php └── tests ├── ApplicationTest.php ├── Blackboard └── ClientTest.php ├── Calendar └── ClientTest.php ├── Callback └── ClientTest.php ├── Chat └── ClientTest.php ├── Contact └── ClientTest.php ├── Conversation └── ClientTest.php ├── Department └── ClientTest.php ├── Health └── ClientTest.php ├── Kernel ├── Concerns │ └── InteractsWithCacheTest.php └── Encryption │ └── EncryptorTest.php ├── Media ├── ClientTest.php └── __fixtures__ │ └── foo.stub ├── Messages ├── FileTest.php ├── ImageTest.php ├── LinkTest.php ├── TextTest.php └── VoiceTest.php ├── Microapp └── ClientTest.php ├── Report └── ClientTest.php ├── Role └── ClientTest.php ├── Schedule └── ClientTest.php ├── TestCase.php ├── TestResponse.php └── User └── ClientTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | .idea/ -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | 7 | 8 | This source file is subject to the MIT license that is bundled 9 | with this source code in the file LICENSE. 10 | EOF; 11 | 12 | return PhpCsFixer\Config::create() 13 | ->setRules([ 14 | '@Symfony' => true, 15 | 'phpdoc_summary' => false, 16 | 'header_comment' => ['header' => $header], 17 | 'ordered_imports' => true, 18 | 'phpdoc_no_empty_return' => false, 19 | 'no_empty_comment' => false, 20 | ]) 21 | ->setFinder( 22 | PhpCsFixer\Finder::create() 23 | ->exclude('vendor') 24 | ->in(__DIR__) 25 | ) 26 | ->setUsingCache(false) 27 | ; 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | env: 4 | global: 5 | - setup=stable 6 | 7 | matrix: 8 | fast_finish: true 9 | include: 10 | - php: 7.0 11 | - php: 7.0 12 | env: setup=lowest 13 | - php: 7.1 14 | - php: 7.1 15 | env: setup=lowest 16 | - php: 7.2 17 | - php: 7.2 18 | env: setup=lowest 19 | - php: 7.3 20 | - php: 7.3 21 | env: setup=lowest 22 | 23 | install: 24 | - if [[ $setup = 'stable' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-stable --no-suggest; fi 25 | - if [[ $setup = 'lowest' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-lowest --prefer-stable --no-suggest; fi 26 | 27 | script: ./vendor/bin/phpunit 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 张铭阳 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

EasyDingTalk

3 |

4 | 5 |

6 | Build Status 7 | Scrutinizer Code Quality 8 | Latest Stable Version 9 | Total Downloads 10 | License 11 |

12 | 13 | ## 介绍 14 | 15 | EasyDingTalk 封装了钉钉身份验证、通讯录管理、消息通知、审批、群机器人、业务事件回调管理等服务端接口,让开发者可以使用简单的配置,提供简洁的 API 以供方便快速地调用钉钉接口。 16 | 17 | ## 环境要求 18 | 19 | - PHP 7.0+ 20 | - [Composer](https://getcomposer.org/) 21 | 22 | ## 安装 23 | 24 | ```bash 25 | composer require mingyoung/dingtalk:^2.0 26 | ``` 27 | 28 | ## 使用 29 | 30 | ```php 31 | use EasyDingTalk\Application; 32 | 33 | $config = [ 34 | 'corp_id' => 'dingd3ir8195906jfo93', 35 | 36 | 'app_key' => 'dingwu33fo1fjc0fszad', 37 | 'app_secret' => 'RsuMFgEIY3jg5UMidkvwpzEobWjf9Fcu3oLqLyCUIgzULm54WcV7j9fi3fJlUshk', 38 | ]; 39 | 40 | $app = new Application($config); 41 | ``` 42 | 43 | ## 文档 44 | 45 | [https://docs.easydingtalk.org](https://docs.easydingtalk.org) 46 | 47 | 48 | ## License 49 | 50 | MIT 51 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mingyoung/dingtalk", 3 | "description": "EasyDingTalk, Alibaba Dingtalk SDK for PHP", 4 | "keywords": [ 5 | "dingtalk", 6 | "dingding", 7 | "easydingtalk", 8 | "sdk", 9 | "钉钉" 10 | 11 | ], 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "张铭阳", 16 | "email": "mingyoungcheung@gmail.com" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=7.0", 21 | "overtrue/http": "1.1.3", 22 | "pimple/pimple": "^3.0", 23 | "psr/simple-cache": "^1.0", 24 | "monolog/monolog": "^1.23 || ^2.0", 25 | "symfony/cache": "^3.3 || ^4.0 || ^5.0", 26 | "symfony/http-foundation": "^3.2 || ^4.0 || ^5.0" 27 | }, 28 | "require-dev": { 29 | "mockery/mockery": "^1.0", 30 | "phpunit/phpunit": "^6.5 || ^7.0" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "EasyDingTalk\\": "src/" 35 | }, 36 | "files": [ 37 | "src/helpers.php" 38 | ] 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "EasyDingTalk\\Tests\\": "tests/" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | tests 14 | 15 | 16 | 17 | 18 | 19 | src 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Application.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk; 13 | 14 | use Overtrue\Http\Support\Collection; 15 | use Pimple\Container; 16 | 17 | /** 18 | * @property \EasyDingTalk\Auth\SsoClient $sso 19 | * @property \EasyDingTalk\Auth\OAuthClient $oauth 20 | * @property \EasyDingTalk\Chat\Client $chat 21 | * @property \EasyDingTalk\Role\Client $role 22 | * @property \EasyDingTalk\User\Client $user 23 | * @property \EasyDingTalk\Media\Client $media 24 | * @property \EasyDingTalk\H5app\Client $h5app 25 | * @property \EasyDingTalk\Health\Client $health 26 | * @property \EasyDingTalk\Report\Client $report 27 | * @property \EasyDingTalk\Checkin\Client $checkin 28 | * @property \EasyDingTalk\Contact\Client $contact 29 | * @property \EasyDingTalk\Process\Client $process 30 | * @property \EasyDingTalk\Calendar\Client $calendar 31 | * @property \EasyDingTalk\Callback\Client $callback 32 | * @property \EasyDingTalk\Microapp\Client $microapp 33 | * @property \EasyDingTalk\Schedule\Client $schedule 34 | * @property \EasyDingTalk\Blackboard\Client $blackboard 35 | * @property \EasyDingTalk\Attendance\Client $attendance 36 | * @property \EasyDingTalk\Department\Client $department 37 | * @property \EasyDingTalk\Conversation\Client $conversation 38 | * @property \EasyDingTalk\Kernel\Http\Client $client 39 | * @property \Monolog\Logger $logger 40 | * @property \EasyDingTalk\Kernel\Server $server 41 | * @property \Symfony\Component\HttpFoundation\Request $request 42 | * @property \EasyDingTalk\Kernel\Encryption\Encryptor $encryptor 43 | * @property \EasyDingTalk\Kernel\AccessToken $access_token 44 | */ 45 | class Application extends Container 46 | { 47 | /** 48 | * @var array 49 | */ 50 | protected $providers = [ 51 | Auth\ServiceProvider::class, 52 | Chat\ServiceProvider::class, 53 | Role\ServiceProvider::class, 54 | User\ServiceProvider::class, 55 | Media\ServiceProvider::class, 56 | H5app\ServiceProvider::class, 57 | Health\ServiceProvider::class, 58 | Report\ServiceProvider::class, 59 | Checkin\ServiceProvider::class, 60 | Contact\ServiceProvider::class, 61 | Process\ServiceProvider::class, 62 | Calendar\ServiceProvider::class, 63 | Callback\ServiceProvider::class, 64 | Microapp\ServiceProvider::class, 65 | Schedule\ServiceProvider::class, 66 | Blackboard\ServiceProvider::class, 67 | Attendance\ServiceProvider::class, 68 | Department\ServiceProvider::class, 69 | Conversation\ServiceProvider::class, 70 | Kernel\Providers\ClientServiceProvider::class, 71 | Kernel\Providers\LoggerServiceProvider::class, 72 | Kernel\Providers\ServerServiceProvider::class, 73 | Kernel\Providers\RequestServiceProvider::class, 74 | Kernel\Providers\EncryptionServiceProvider::class, 75 | Kernel\Providers\AccessTokenServiceProvider::class, 76 | ]; 77 | 78 | /** 79 | * Application constructor. 80 | * 81 | * @param array $config 82 | * @param array $values 83 | */ 84 | public function __construct($config = [], array $values = []) 85 | { 86 | parent::__construct($values); 87 | 88 | $this['config'] = function () use ($config) { 89 | return new Collection($config); 90 | }; 91 | 92 | foreach ($this->providers as $provider) { 93 | $this->register(new $provider()); 94 | } 95 | } 96 | 97 | /** 98 | * @param $name 99 | * 100 | * @return mixed 101 | */ 102 | public function __get($name) 103 | { 104 | return $this[$name]; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Attendance/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Attendance; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 企业考勤排班详情 20 | * 21 | * @param string $date 22 | * @param int|null $offset 23 | * @param int|null $size 24 | * 25 | * @return mixed 26 | */ 27 | public function schedules($date, $offset = null, $size = null) 28 | { 29 | return $this->client->postJson('topapi/attendance/listschedule', [ 30 | 'workDate' => $date, 'offset' => $offset, 'size' => $size, 31 | ]); 32 | } 33 | 34 | /** 35 | * 企业考勤组详情 36 | * 37 | * @param int|null $offset 38 | * @param int|null $size 39 | * 40 | * @return mixed 41 | */ 42 | public function groups($offset = null, $size = null) 43 | { 44 | return $this->client->postJson('topapi/attendance/getsimplegroups', compact('offset', 'size')); 45 | } 46 | 47 | /** 48 | * 获取用户考勤组 49 | * 50 | * @param string $userId 51 | * 52 | * @return mixed 53 | */ 54 | public function userGroup($userId) 55 | { 56 | return $this->client->postJson('topapi/attendance/getusergroup', ['userid' => $userId]); 57 | } 58 | 59 | /** 60 | * 获取打卡详情 61 | * 62 | * @param array $params 63 | * 64 | * @return mixed 65 | */ 66 | public function records($params) 67 | { 68 | return $this->client->postJson('attendance/listRecord', $params); 69 | } 70 | 71 | /** 72 | * 获取打卡结果 73 | * 74 | * @param array $params 75 | * 76 | * @return mixed 77 | */ 78 | public function results($params) 79 | { 80 | return $this->client->postJson('attendance/list', $params); 81 | } 82 | 83 | /** 84 | * 获取请假时长 85 | * 86 | * @param string $userId 87 | * @param string $from 88 | * @param string $to 89 | * 90 | * @return mixed 91 | */ 92 | public function duration($userId, $from, $to) 93 | { 94 | return $this->client->postJson('topapi/attendance/getleaveapproveduration', [ 95 | 'userid' => $userId, 'from_date' => $from, 'to_date' => $to, 96 | ]); 97 | } 98 | 99 | /** 100 | * 查询请假状态 101 | * 102 | * @param array $params 103 | * 104 | * @return mixed 105 | */ 106 | public function status($params) 107 | { 108 | return $this->client->postJson('topapi/attendance/getleavestatus', $params); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Attendance/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Attendance; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['attendance'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Auth/HasStateParameter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Auth; 13 | 14 | use function EasyDingTalk\str_random; 15 | use function EasyDingTalk\tap; 16 | 17 | trait HasStateParameter 18 | { 19 | /** 20 | * @var bool 21 | */ 22 | protected $stateless = false; 23 | 24 | /** 25 | * @return $this 26 | */ 27 | public function stateless() 28 | { 29 | $this->stateless = true; 30 | 31 | return $this; 32 | } 33 | 34 | /** 35 | * Generate state. 36 | * 37 | * @return string 38 | */ 39 | protected function makeState() 40 | { 41 | return tap(str_random(64), function ($state) { 42 | $this->app['request']->getSession()->set('state', $state); 43 | }); 44 | } 45 | 46 | /** 47 | * @param string|null $state 48 | * 49 | * @return bool 50 | */ 51 | protected function hasValidState($state) 52 | { 53 | if ($this->stateless) { 54 | return true; 55 | } 56 | 57 | return !is_null($state) && ($state === $this->app['request']->getSession()->get('state')); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Auth/InvalidStateException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Auth; 13 | 14 | use EasyDingTalk\Kernel\Exceptions\Exception; 15 | 16 | class InvalidStateException extends Exception 17 | { 18 | // 19 | } 20 | -------------------------------------------------------------------------------- /src/Auth/OAuthClient.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Auth; 13 | 14 | use EasyDingTalk\Kernel\Http\Client; 15 | use Symfony\Component\HttpFoundation\RedirectResponse; 16 | 17 | class OAuthClient extends Client 18 | { 19 | use HasStateParameter; 20 | 21 | /** 22 | * @var array 23 | */ 24 | protected $credential; 25 | 26 | /** 27 | * @var bool 28 | */ 29 | protected $withQrConnect = false; 30 | 31 | /** 32 | * @var string|null 33 | */ 34 | protected $redirect; 35 | 36 | /** 37 | * @param string $name 38 | * 39 | * @return $this 40 | */ 41 | public function use($name) 42 | { 43 | $this->credential = $this->app['config']->get('oauth')[$name]; 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function getRedirectUrl() 52 | { 53 | return $this->redirect ?: $this->credential['redirect']; 54 | } 55 | 56 | /** 57 | * @param string $url 58 | * 59 | * @return $this 60 | */ 61 | public function setRedirectUrl($url) 62 | { 63 | $this->redirect = $url; 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * @return $this 70 | */ 71 | public function withQrConnect() 72 | { 73 | $this->withQrConnect = true; 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * Redirect to the authentication page. 80 | * 81 | * @param string|null $url 82 | * 83 | * @return \Symfony\Component\HttpFoundation\RedirectResponse 84 | */ 85 | public function redirect($url = null) 86 | { 87 | $query = [ 88 | 'appid' => $this->credential['client_id'], 89 | 'response_type' => 'code', 90 | 'scope' => $this->credential['scope'], 91 | 'state' => $this->makeState(), 92 | 'redirect_uri' => $url ?: $this->getRedirectUrl(), 93 | ]; 94 | 95 | return new RedirectResponse( 96 | sprintf('https://oapi.dingtalk.com/connect/%s?%s', $this->withQrConnect ? 'qrconnect' : 'oauth2/sns_authorize', http_build_query(array_filter($query))) 97 | ); 98 | } 99 | 100 | /** 101 | * @return array 102 | * 103 | * @throws \EasyDingTalk\Auth\InvalidStateException 104 | */ 105 | public function user() 106 | { 107 | if (!$this->hasValidState($this->app['request']->get('state'))) { 108 | throw new InvalidStateException(); 109 | } 110 | 111 | $data = [ 112 | 'tmp_auth_code' => $this->app['request']->get('code'), 113 | ]; 114 | 115 | $query = [ 116 | 'accessKey' => $this->credential['client_id'], 117 | 'timestamp' => $timestamp = (int) microtime(true) * 1000, 118 | 'signature' => $this->signature($timestamp), 119 | ]; 120 | 121 | return $this->postJson('sns/getuserinfo_bycode', $data, $query); 122 | } 123 | 124 | /** 125 | * 计算签名 126 | * 127 | * @param int $timestamp 128 | * 129 | * @return string 130 | */ 131 | public function signature($timestamp) 132 | { 133 | return base64_encode(hash_hmac('sha256', $timestamp, $this->credential['client_secret'], true)); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Auth/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Auth; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | use Symfony\Component\HttpFoundation\Session\Session; 17 | 18 | class ServiceProvider implements ServiceProviderInterface 19 | { 20 | /** 21 | * Registers services on the given container. 22 | * This method should only be used to configure services and parameters. 23 | * It should not get services. 24 | * 25 | * @param \Pimple\Container $pimple A container instance 26 | */ 27 | public function register(Container $pimple) 28 | { 29 | $pimple['sso'] = function ($app) { 30 | return new SsoClient($app); 31 | }; 32 | 33 | $pimple['oauth'] = function ($app) { 34 | if (!$app['request']->hasSession()) { 35 | $app['request']->setSession(new Session()); 36 | } 37 | 38 | return new OAuthClient($app); 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Auth/SsoClient.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Auth; 13 | 14 | use EasyDingTalk\Kernel\Http\Client; 15 | 16 | class SsoClient extends Client 17 | { 18 | /** 19 | * 获取应用后台免登 AccessToken 20 | * 21 | * @return mixed 22 | */ 23 | public function getToken() 24 | { 25 | return $this->get('sso/gettoken', [ 26 | 'corpid' => $this->app['config']->get('corp_id'), 27 | 'corpsecret' => $this->app['config']->get('sso_secret'), 28 | ]); 29 | } 30 | 31 | /** 32 | * 获取用户身份信息 33 | * 34 | * @return mixed 35 | */ 36 | public function user() 37 | { 38 | return $this->get('sso/getuserinfo', [ 39 | 'access_token' => $this->getToken()['access_token'], 40 | 'code' => $this->app['request']->get('code'), 41 | ]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Blackboard/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Blackboard; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取用户公告数据 20 | * 21 | * @param string $userid 22 | * 23 | * @return mixed 24 | */ 25 | public function list($userid) 26 | { 27 | return $this->client->postJson('topapi/blackboard/listtopten', compact('userid')); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Blackboard/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Blackboard; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['blackboard'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Calendar/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Calendar; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 创建日程 20 | * 21 | * @param array $params 22 | * 23 | * @return mixed 24 | */ 25 | public function create($params) 26 | { 27 | return $this->client->postJson('topapi/calendar/create', $params); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Calendar/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Calendar; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['calendar'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Callback/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Callback; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 注册业务事件回调接口 20 | * 21 | * @param array $params 22 | * 23 | * @return mixed 24 | */ 25 | public function register($params) 26 | { 27 | $params['token'] = $this->app['config']->get('token'); 28 | $params['aes_key'] = $this->app['config']->get('aes_key'); 29 | 30 | return $this->client->postJson('call_back/register_call_back', $params); 31 | } 32 | 33 | /** 34 | * 查询事件回调接口 35 | * 36 | * @return mixed 37 | */ 38 | public function list() 39 | { 40 | return $this->client->get('call_back/get_call_back'); 41 | } 42 | 43 | /** 44 | * 更新事件回调接口 45 | * 46 | * @return mixed 47 | */ 48 | public function update($params) 49 | { 50 | $params['token'] = $this->app['config']->get('token'); 51 | $params['aes_key'] = $this->app['config']->get('aes_key'); 52 | 53 | return $this->client->postJson('call_back/update_call_back', $params); 54 | } 55 | 56 | /** 57 | * 删除事件回调接口 58 | * 59 | * @return mixed 60 | */ 61 | public function delete() 62 | { 63 | return $this->client->get('call_back/delete_call_back'); 64 | } 65 | 66 | /** 67 | * 获取回调失败结果 68 | * 69 | * @return mixed 70 | */ 71 | public function failed() 72 | { 73 | return $this->client->get('call_back/get_call_back_failed_result'); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Callback/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Callback; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['callback'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Chat/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Chat; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 发送群消息 20 | * 21 | * @param string $chatId 22 | * @param string $message 23 | * 24 | * @return mixed 25 | */ 26 | public function send($chatId, $message) 27 | { 28 | return $this->client->postJson('chat/send', [ 29 | 'chatid' => $chatId, 'msg' => $message, 30 | ]); 31 | } 32 | 33 | /** 34 | * 查询群消息已读人员列表 35 | * 36 | * @param string $messageId 37 | * @param int $cursor 38 | * @param int $size 39 | * 40 | * @return mixed 41 | */ 42 | public function result($messageId, $cursor, $size) 43 | { 44 | return $this->client->get('chat/getReadList', [ 45 | 'messageId' => $messageId, 'cursor' => $cursor, 'size' => $size, 46 | ]); 47 | } 48 | 49 | /** 50 | * 创建会话 51 | * 52 | * @param array $params 53 | * 54 | * @return mixed 55 | */ 56 | public function create($params) 57 | { 58 | return $this->client->postJson('chat/create', $params); 59 | } 60 | 61 | /** 62 | * 修改会话 63 | * 64 | * @param string $chatId 65 | * @param array $params 66 | * 67 | * @return mixed 68 | */ 69 | public function update($chatId, $params) 70 | { 71 | return $this->client->postJson('chat/update', ['chatid' => $chatId] + $params); 72 | } 73 | 74 | /** 75 | * 获取会话 76 | * 77 | * @param string $chatId 78 | * 79 | * @return mixed 80 | */ 81 | public function get($chatId) 82 | { 83 | return $this->client->get('chat/get', ['chatid' => $chatId]); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Chat/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Chat; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['chat'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Checkin/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Checkin; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取部门用户签到记录 20 | * 21 | * @param array $params 22 | * 23 | * @return mixed 24 | */ 25 | public function records($params) 26 | { 27 | return $this->client->get('checkin/record', $params); 28 | } 29 | 30 | /** 31 | * 获取用户签到记录 32 | * 33 | * @param array $params 34 | * 35 | * @return mixed 36 | */ 37 | public function get($params) 38 | { 39 | return $this->client->postJson('topapi/checkin/record/get', $params); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Checkin/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Checkin; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['checkin'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Contact/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Contact; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取外部联系人标签列表 20 | * 21 | * @param int $offset 22 | * @param int $size 23 | * 24 | * @return mixed 25 | */ 26 | public function labels($offset = 0, $size = 100) 27 | { 28 | return $this->client->postJson('topapi/extcontact/listlabelgroups', compact('offset', 'size')); 29 | } 30 | 31 | /** 32 | * 获取外部联系人列表 33 | * 34 | * @param int $offset 35 | * @param int $size 36 | * 37 | * @return mixed 38 | */ 39 | public function list($offset = 0, $size = 100) 40 | { 41 | return $this->client->postJson('topapi/extcontact/list', compact('offset', 'size')); 42 | } 43 | 44 | /** 45 | * 获取企业外部联系人详情 46 | * 47 | * @param string $userId 48 | * 49 | * @return mixed 50 | */ 51 | public function get($userId) 52 | { 53 | return $this->client->postJson('topapi/extcontact/get', ['user_id' => $userId]); 54 | } 55 | 56 | /** 57 | * 添加外部联系人 58 | * 59 | * @param array $contact 60 | * 61 | * @return mixed 62 | */ 63 | public function create($contact) 64 | { 65 | return $this->client->postJson('topapi/extcontact/create', compact('contact')); 66 | } 67 | 68 | /** 69 | * 更新外部联系人 70 | * 71 | * @param string $userId 72 | * @param array $contact 73 | * 74 | * @return mixed 75 | */ 76 | public function update($userId, $contact) 77 | { 78 | $contact = ['user_id' => $userId] + $contact; 79 | 80 | return $this->client->postJson('topapi/extcontact/update', compact('contact')); 81 | } 82 | 83 | /** 84 | * 删除外部联系人 85 | * 86 | * @param string $userId 87 | * 88 | * @return mixed 89 | */ 90 | public function delete($userId) 91 | { 92 | return $this->client->postJson('topapi/extcontact/delete', ['user_id' => $userId]); 93 | } 94 | 95 | /** 96 | * 获取通讯录权限范围 97 | * 98 | * @return mixed 99 | */ 100 | public function scopes() 101 | { 102 | return $this->client->get('auth/scopes'); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Contact/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Contact; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['contact'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Conversation/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Conversation; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | use function EasyDingTalk\tap; 16 | 17 | class Client extends BaseClient 18 | { 19 | /** 20 | * 发送普通消息 21 | * 22 | * @param string $sender 23 | * @param string $cid 24 | * @param array $message 25 | * 26 | * @return mixed 27 | */ 28 | public function sendGeneralMessage($sender, $cid, $message) 29 | { 30 | return $this->client->postJson('message/send_to_conversation', [ 31 | 'sender' => $sender, 'cid' => $cid, 'msg' => $message, 32 | ]); 33 | } 34 | 35 | /** 36 | * 发送工作通知消息 37 | * 38 | * @param array $params 39 | * 40 | * @return mixed 41 | */ 42 | public function sendCorporationMessage($params) 43 | { 44 | return $this->client->post('topapi/message/corpconversation/asyncsend_v2', $params); 45 | } 46 | 47 | /** 48 | * @param int $taskId 49 | * 50 | * @return mixed 51 | */ 52 | public function corporationMessage($taskId) 53 | { 54 | $client = new class($this->app) extends BaseClient { 55 | /** 56 | * 任务 ID 57 | * 58 | * @var int 59 | */ 60 | protected $taskId; 61 | 62 | /** 63 | * @param int 64 | */ 65 | public function setTaskId($taskId) 66 | { 67 | $this->taskId = $taskId; 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * 查询工作通知消息的发送进度 74 | * 75 | * @return mixed 76 | */ 77 | public function progress() 78 | { 79 | return $this->client->postJson('topapi/message/corpconversation/getsendprogress', [ 80 | 'agent_id' => $this->app['config']['agent_id'], 'task_id' => $this->taskId, 81 | ]); 82 | } 83 | 84 | /** 85 | * 查询工作通知消息的发送结果 86 | * 87 | * @return mixed 88 | */ 89 | public function result() 90 | { 91 | return $this->client->postJson('topapi/message/corpconversation/getsendresult', [ 92 | 'agent_id' => $this->app['config']['agent_id'], 'task_id' => $this->taskId, 93 | ]); 94 | } 95 | }; 96 | 97 | return tap($client, function ($client) use ($taskId) { 98 | $client->setTaskId($taskId); 99 | }); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Conversation/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Conversation; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['conversation'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Department/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Department; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取子部门 ID 列表 20 | * 21 | * @param string $id 部门ID 22 | * 23 | * @return mixed 24 | */ 25 | public function getSubDepartmentIds($id) 26 | { 27 | return $this->client->get('department/list_ids', compact('id')); 28 | } 29 | 30 | /** 31 | * 获取部门列表 32 | * 33 | * @param bool $isFetchChild 34 | * @param string $id 35 | * @param string $lang 36 | * 37 | * @return mixed 38 | */ 39 | public function list($id = null, bool $isFetchChild = false, $lang = null) 40 | { 41 | return $this->client->get('department/list', [ 42 | 'id' => $id, 'lang' => $lang, 'fetch_child' => $isFetchChild ? 'true' : 'false', 43 | ]); 44 | } 45 | 46 | /** 47 | * 获取部门详情 48 | * 49 | * @param string $id 50 | * @param string $lang 51 | * 52 | * @return mixed 53 | */ 54 | public function get($id, $lang = null) 55 | { 56 | return $this->client->get('department/get', compact('id', 'lang')); 57 | } 58 | 59 | /** 60 | * 查询部门的所有上级父部门路径 61 | * 62 | * @param string $id 63 | * 64 | * @return mixed 65 | */ 66 | public function getParentsById($id) 67 | { 68 | return $this->client->get('department/list_parent_depts_by_dept', compact('id')); 69 | } 70 | 71 | /** 72 | * 查询指定用户的所有上级父部门路径 73 | * 74 | * @param string $userId 75 | * 76 | * @return mixed 77 | */ 78 | public function getParentsByUserId($userId) 79 | { 80 | return $this->client->get('department/list_parent_depts', compact('userId')); 81 | } 82 | 83 | /** 84 | * 创建部门 85 | * 86 | * @param array $params 87 | * 88 | * @return mixed 89 | */ 90 | public function create(array $params) 91 | { 92 | return $this->client->postJson('department/create', $params); 93 | } 94 | 95 | /** 96 | * 更新部门 97 | * 98 | * @param string $id 99 | * @param array $params 100 | * 101 | * @return mixed 102 | */ 103 | public function update($id, array $params) 104 | { 105 | return $this->client->postJson('department/update', compact('id') + $params); 106 | } 107 | 108 | /** 109 | * 删除部门 110 | * 111 | * @param string $id 112 | * 113 | * @return mixed 114 | */ 115 | public function delete($id) 116 | { 117 | return $this->client->get('department/delete', compact('id')); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Department/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Department; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['department'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/H5app/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\H5app; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | use EasyDingTalk\Kernel\Concerns\InteractsWithCache; 16 | use EasyDingTalk\Kernel\Exceptions\InvalidCredentialsException; 17 | 18 | class Client extends BaseClient 19 | { 20 | use InteractsWithCache; 21 | 22 | /** 23 | * 获取 jsapi_ticket 24 | * 25 | * @return mixed 26 | */ 27 | public function get() 28 | { 29 | if ($value = $this->getCache()->get($this->cacheFor())) { 30 | return $value; 31 | } 32 | $value = $this->client->get('get_jsapi_ticket'); 33 | if (0 !== $value['errcode']) { 34 | throw new InvalidCredentialsException(json_encode($value)); 35 | } 36 | $this->getCache()->set($this->cacheFor(), $value, $value['expires_in']); 37 | return $value; 38 | } 39 | 40 | /** 41 | * 获取 ticket 42 | * 43 | * @return string 44 | */ 45 | public function getTicket() 46 | { 47 | return $this->get()['ticket']; 48 | } 49 | 50 | /** 51 | * 获取签名相关信息 52 | * 53 | * @param string $url 54 | * 55 | * @return mixed 56 | */ 57 | public function getSignature($url) 58 | { 59 | $nonceStr = $this->getNonceStr(); 60 | $timeStamp = time(); 61 | $plain = 'jsapi_ticket=' . $this->getTicket() . '&noncestr=' . $nonceStr . '×tamp=' . $timeStamp . '&url=' . $url; 62 | $signature = sha1($plain); 63 | return [ 64 | 'agentId' => $this->app['config']->get('agent_id'), 65 | 'corpId' => $this->app['config']->get('corp_id'), 66 | 'timeStamp' => $timeStamp, 67 | 'nonceStr' => $nonceStr, 68 | 'signature' => $signature, 69 | 'url' => $url 70 | ]; 71 | } 72 | 73 | /** 74 | * 缓存 Key 75 | * 76 | * @return string 77 | */ 78 | protected function cacheFor() 79 | { 80 | return sprintf('jsapi_ticket.%s', $this->app['config']->get('app_key')); 81 | } 82 | 83 | /** 84 | * 生产 随机字符串 85 | * 86 | * @return string 87 | */ 88 | protected function getNonceStr($length=16) 89 | { 90 | $strs = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm"; 91 | return substr(str_shuffle($strs), mt_rand(0, strlen($strs)-11), $length); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/H5app/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\H5app; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['h5app'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Health/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Health; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取用户钉钉运动开启状态 20 | * 21 | * @param string $userId 22 | * 23 | * @return mixed 24 | */ 25 | public function status($userId) 26 | { 27 | return $this->client->postJson('topapi/health/stepinfo/getuserstatus', ['userid' => $userId]); 28 | } 29 | 30 | /** 31 | * 获取个人钉钉运动数据 32 | * 33 | * @param string $id 34 | * @param string $dates 35 | * 36 | * @return mixed 37 | */ 38 | public function byUser($id, $dates) 39 | { 40 | return $this->client->postJson('topapi/health/stepinfo/list', ['type' => 0, 'object_id' => $id, 'stat_dates' => $dates]); 41 | } 42 | 43 | /** 44 | * 获取部门钉钉运动数据 45 | * 46 | * @param string $id 47 | * @param string $dates 48 | * 49 | * @return mixed 50 | */ 51 | public function byDepartment($id, $dates) 52 | { 53 | return $this->client->postJson('topapi/health/stepinfo/list', ['type' => 1, 'object_id' => $id, 'stat_dates' => $dates]); 54 | } 55 | 56 | /** 57 | * 批量获取钉钉运动数据 58 | * 59 | * @param array $userIds 60 | * @param string $date 61 | * 62 | * @return mixed 63 | */ 64 | public function byUsers(array $userIds, $date) 65 | { 66 | $userIds = implode(',', $userIds); 67 | 68 | return $this->client->postJson('topapi/health/stepinfo/listbyuserid', ['userids' => $userIds, 'stat_date' => $date]); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Health/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Health; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['health'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Kernel/AccessToken.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel; 13 | 14 | use EasyDingTalk\Kernel\Exceptions\InvalidCredentialsException; 15 | use EasyDingTalk\Kernel\Http\Client; 16 | use function EasyDingTalk\tap; 17 | use Overtrue\Http\Traits\ResponseCastable; 18 | 19 | class AccessToken 20 | { 21 | use Concerns\InteractsWithCache, ResponseCastable; 22 | 23 | /** 24 | * @var \EasyDingTalk\Application 25 | */ 26 | protected $app; 27 | 28 | /** 29 | * AccessToken constructor. 30 | * 31 | * @param \EasyDingTalk\Application 32 | */ 33 | public function __construct($app) 34 | { 35 | $this->app = $app; 36 | } 37 | 38 | /** 39 | * 获取钉钉 AccessToken 40 | * 41 | * @return array 42 | * 43 | * @throws \Psr\SimpleCache\InvalidArgumentException 44 | */ 45 | public function get() 46 | { 47 | if ($value = $this->getCache()->get($this->cacheFor())) { 48 | return $value; 49 | } 50 | 51 | return $this->refresh(); 52 | } 53 | 54 | /** 55 | * 获取 AccessToken 56 | * 57 | * @return string 58 | * 59 | * @throws \Psr\SimpleCache\InvalidArgumentException 60 | */ 61 | public function getToken() 62 | { 63 | return $this->get()['access_token']; 64 | } 65 | 66 | /** 67 | * 刷新钉钉 AccessToken 68 | * 69 | * @return array 70 | */ 71 | public function refresh() 72 | { 73 | $response = (new Client($this->app))->requestRaw('gettoken', 'GET', ['query' => [ 74 | 'appkey' => $this->app['config']->get('app_key'), 75 | 'appsecret' => $this->app['config']->get('app_secret'), 76 | ]]); 77 | 78 | return tap($this->castResponseToType($response, 'array'), function ($value) { 79 | if (0 !== $value['errcode']) { 80 | throw new InvalidCredentialsException(json_encode($value)); 81 | } 82 | $this->getCache()->set($this->cacheFor(), $value, $value['expires_in']); 83 | }); 84 | } 85 | 86 | /** 87 | * 缓存 Key 88 | * 89 | * @return string 90 | */ 91 | protected function cacheFor() 92 | { 93 | return sprintf('access_token.%s', $this->app['config']->get('app_key')); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Kernel/BaseClient.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel; 13 | 14 | class BaseClient 15 | { 16 | /** 17 | * @var \EasyDingTalk\Application 18 | */ 19 | protected $app; 20 | 21 | /** 22 | * @var \EasyDingTalk\Kernel\Http\Client 23 | */ 24 | protected $client; 25 | 26 | /** 27 | * Client constructor. 28 | * 29 | * @param \EasyDingTalk\Application $app 30 | */ 31 | public function __construct($app) 32 | { 33 | $this->app = $app; 34 | $this->client = $this->app['client']->withAccessTokenMiddleware()->withRetryMiddleware(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Kernel/Concerns/InteractsWithCache.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Concerns; 13 | 14 | use Psr\SimpleCache\CacheInterface; 15 | use Symfony\Component\Cache\Adapter\FilesystemAdapter; 16 | use Symfony\Component\Cache\Psr16Cache; 17 | use Symfony\Component\Cache\Simple\FilesystemCache; 18 | 19 | trait InteractsWithCache 20 | { 21 | /** 22 | * @var \Psr\SimpleCache\CacheInterface 23 | */ 24 | protected $cache; 25 | 26 | /** 27 | * @return \Psr\SimpleCache\CacheInterface 28 | */ 29 | public function getCache() 30 | { 31 | if ($this->cache) { 32 | return $this->cache; 33 | } 34 | 35 | if (property_exists($this, 'app') && $this->app->offsetExists('cache') && ($this->app['cache'] instanceof CacheInterface)) { 36 | return $this->cache = $this->app['cache']; 37 | } 38 | 39 | return $this->cache = $this->createDefaultCache(); 40 | } 41 | 42 | /** 43 | * @return \Psr\SimpleCache\CacheInterface 44 | */ 45 | protected function createDefaultCache() 46 | { 47 | if (class_exists(Psr16Cache::class)) { 48 | return new Psr16Cache(new FilesystemAdapter('easydingtalk')); 49 | } 50 | 51 | return new FilesystemCache('easydingtalk'); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Kernel/Encryption/Encryptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Encryption; 13 | 14 | use function EasyDingTalk\str_random; 15 | 16 | class Encryptor 17 | { 18 | /** 19 | * @var string 20 | */ 21 | protected $key; 22 | 23 | /** 24 | * @var string 25 | */ 26 | protected $token; 27 | 28 | /** 29 | * @var string 30 | */ 31 | protected $aesKey; 32 | 33 | /** 34 | * @var int 35 | */ 36 | protected $blockSize = 32; 37 | 38 | /** 39 | * Encryptor Constructor. 40 | * 41 | * @param string $key 42 | * @param string $token 43 | * @param string $aesKey 44 | */ 45 | public function __construct($key, $token, $aesKey) 46 | { 47 | $this->key = $key; 48 | $this->token = $token; 49 | $this->aesKey = base64_decode($aesKey.'=', true); 50 | } 51 | 52 | /** 53 | * Encrypt the data. 54 | * 55 | * @param string $data 56 | * @param string $nonce 57 | * @param int $timestamp 58 | * 59 | * @return string 60 | */ 61 | public function encrypt($data, $nonce = null, $timestamp = null) 62 | { 63 | $string = str_random().pack('N', strlen($data)).$data.$this->key; 64 | 65 | $result = base64_encode( 66 | openssl_encrypt($this->pkcs7Pad($string), 'AES-256-CBC', $this->aesKey, OPENSSL_NO_PADDING, substr($this->aesKey, 0, 16)) 67 | ); 68 | 69 | !is_null($nonce) || $nonce = uniqid(); 70 | !is_null($timestamp) || $timestamp = time(); 71 | 72 | return json_encode([ 73 | 'msg_signature' => $this->signature($this->token, $nonce, $timestamp, $result), 74 | 'timeStamp' => $timestamp, 75 | 'nonce' => $nonce, 76 | 'encrypt' => $result, 77 | ]); 78 | } 79 | 80 | /** 81 | * Decrypt the data. 82 | * 83 | * @param string $data 84 | * @param string $signature 85 | * @param string $nonce 86 | * @param int $timestamp 87 | * 88 | * @return string 89 | */ 90 | public function decrypt($data, $signature, $nonce, $timestamp) 91 | { 92 | if ($signature !== $this->signature($this->token, $nonce, $timestamp, $data)) { 93 | throw new \RuntimeException('Invalid Signature.'); 94 | } 95 | 96 | $decrypted = openssl_decrypt( 97 | base64_decode($data, true), 'AES-256-CBC', $this->aesKey, OPENSSL_NO_PADDING, substr($this->aesKey, 0, 16) 98 | ); 99 | 100 | $result = $this->pkcs7Unpad($decrypted); 101 | 102 | $data = substr($result, 16, strlen($result)); 103 | 104 | $contentLen = unpack('N', substr($data, 0, 4))[1]; 105 | 106 | if (substr($data, $contentLen + 4) !== $this->key) { 107 | throw new \RuntimeException('Invalid CorpId.'); 108 | } 109 | 110 | return substr($data, 4, $contentLen); 111 | } 112 | 113 | /** 114 | * Get SHA1. 115 | * 116 | * @return string 117 | */ 118 | public function signature() 119 | { 120 | $array = func_get_args(); 121 | sort($array, SORT_STRING); 122 | 123 | return sha1(implode($array)); 124 | } 125 | 126 | /** 127 | * PKCS#7 pad. 128 | * 129 | * @param string $text 130 | * 131 | * @return string 132 | */ 133 | public function pkcs7Pad(string $text) 134 | { 135 | $padding = $this->blockSize - (strlen($text) % $this->blockSize); 136 | $pattern = chr($padding); 137 | 138 | return $text.str_repeat($pattern, $padding); 139 | } 140 | 141 | /** 142 | * PKCS#7 unpad. 143 | * 144 | * @param string $text 145 | * 146 | * @return string 147 | */ 148 | public function pkcs7Unpad(string $text) 149 | { 150 | $pad = ord(substr($text, -1)); 151 | if ($pad < 1 || $pad > $this->blockSize) { 152 | $pad = 0; 153 | } 154 | 155 | return substr($text, 0, (strlen($text) - $pad)); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Kernel/Exceptions/Exception.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Exceptions; 13 | 14 | use Exception as BaseException; 15 | 16 | class Exception extends BaseException 17 | { 18 | // 19 | } 20 | -------------------------------------------------------------------------------- /src/Kernel/Exceptions/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Exceptions; 13 | 14 | class InvalidArgumentException extends Exception 15 | { 16 | // 17 | } 18 | -------------------------------------------------------------------------------- /src/Kernel/Exceptions/InvalidCredentialsException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Exceptions; 13 | 14 | class InvalidCredentialsException extends Exception 15 | { 16 | // 17 | } 18 | -------------------------------------------------------------------------------- /src/Kernel/Exceptions/RuntimeException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Exceptions; 13 | 14 | class RuntimeException extends Exception 15 | { 16 | // 17 | } 18 | -------------------------------------------------------------------------------- /src/Kernel/Http/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Http; 13 | 14 | use GuzzleHttp\Middleware; 15 | use Overtrue\Http\Client as BaseClient; 16 | use Psr\Http\Message\RequestInterface; 17 | use Psr\Http\Message\ResponseInterface; 18 | 19 | class Client extends BaseClient 20 | { 21 | /** 22 | * @var \EasyDingTalk\Application 23 | */ 24 | protected $app; 25 | 26 | /** 27 | * @var array 28 | */ 29 | protected static $httpConfig = [ 30 | 'base_uri' => 'https://oapi.dingtalk.com', 31 | ]; 32 | 33 | /** 34 | * @param \EasyDingTalk\Application $app 35 | */ 36 | public function __construct($app) 37 | { 38 | $this->app = $app; 39 | 40 | parent::__construct(array_merge(static::$httpConfig, $this->app['config']->get('http', []))); 41 | } 42 | 43 | /** 44 | * @param array $config 45 | */ 46 | public function setHttpConfig(array $config) 47 | { 48 | static::$httpConfig = array_merge(static::$httpConfig, $config); 49 | } 50 | 51 | /** 52 | * @return $this 53 | */ 54 | public function withAccessTokenMiddleware() 55 | { 56 | if (isset($this->getMiddlewares()['access_token'])) { 57 | return $this; 58 | } 59 | 60 | $middleware = function (callable $handler) { 61 | return function (RequestInterface $request, array $options) use ($handler) { 62 | if ($this->app['access_token']) { 63 | parse_str($request->getUri()->getQuery(), $query); 64 | 65 | $request = $request->withUri( 66 | $request->getUri()->withQuery(http_build_query(['access_token' => $this->app['access_token']->getToken()] + $query)) 67 | ); 68 | } 69 | 70 | return $handler($request, $options); 71 | }; 72 | }; 73 | 74 | $this->pushMiddleware($middleware, 'access_token'); 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * @return $this 81 | */ 82 | public function withRetryMiddleware() 83 | { 84 | if (isset($this->getMiddlewares()['retry'])) { 85 | return $this; 86 | } 87 | 88 | $middleware = Middleware::retry(function ($retries, RequestInterface $request, ResponseInterface $response = null) { 89 | if (is_null($response) || $retries < 1) { 90 | return false; 91 | } 92 | 93 | if (in_array(json_decode($response->getBody(), true)['errcode'] ?? null, [40001])) { 94 | $this->app['access_token']->refresh(); 95 | 96 | return true; 97 | } 98 | }); 99 | 100 | $this->pushMiddleware($middleware, 'retry'); 101 | 102 | return $this; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Kernel/Providers/AccessTokenServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Providers; 13 | 14 | use EasyDingTalk\Kernel\AccessToken; 15 | use Pimple\Container; 16 | use Pimple\ServiceProviderInterface; 17 | 18 | class AccessTokenServiceProvider implements ServiceProviderInterface 19 | { 20 | /** 21 | * Registers services on the given container. 22 | * This method should only be used to configure services and parameters. 23 | * It should not get services. 24 | * 25 | * @param \Pimple\Container $pimple A container instance 26 | */ 27 | public function register(Container $pimple) 28 | { 29 | isset($pimple['access_token']) || $pimple['access_token'] = function ($app) { 30 | return new AccessToken($app); 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Kernel/Providers/ClientServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Providers; 13 | 14 | use EasyDingTalk\Kernel\Http\Client; 15 | use Pimple\Container; 16 | use Pimple\ServiceProviderInterface; 17 | 18 | class ClientServiceProvider implements ServiceProviderInterface 19 | { 20 | /** 21 | * Registers services on the given container. 22 | * This method should only be used to configure services and parameters. 23 | * It should not get services. 24 | * 25 | * @param \Pimple\Container $pimple A container instance 26 | */ 27 | public function register(Container $pimple) 28 | { 29 | isset($pimple['client']) || $pimple['client'] = function ($app) { 30 | return new Client($app); 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Kernel/Providers/EncryptionServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Providers; 13 | 14 | use EasyDingTalk\Kernel\Encryption\Encryptor; 15 | use Pimple\Container; 16 | use Pimple\ServiceProviderInterface; 17 | 18 | class EncryptionServiceProvider implements ServiceProviderInterface 19 | { 20 | /** 21 | * Registers services on the given container. 22 | * This method should only be used to configure services and parameters. 23 | * It should not get services. 24 | * 25 | * @param \Pimple\Container $pimple A container instance 26 | */ 27 | public function register(Container $pimple) 28 | { 29 | $pimple['encryptor'] = function ($app) { 30 | return new Encryptor( 31 | $app['config']->get('corp_id'), 32 | $app['config']->get('token'), 33 | $app['config']->get('aes_key') 34 | ); 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Kernel/Providers/LoggerServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Providers; 13 | 14 | use Monolog\Logger; 15 | use Pimple\Container; 16 | use Pimple\ServiceProviderInterface; 17 | 18 | class LoggerServiceProvider implements ServiceProviderInterface 19 | { 20 | /** 21 | * Registers services on the given container. 22 | * This method should only be used to configure services and parameters. 23 | * It should not get services. 24 | * 25 | * @param \Pimple\Container $pimple A container instance 26 | */ 27 | public function register(Container $pimple) 28 | { 29 | isset($pimple['logger']) || $pimple['logger'] = function ($app) { 30 | return new Logger('EasyDingTalk'); 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Kernel/Providers/RequestServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Providers; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | use Symfony\Component\HttpFoundation\Request; 17 | 18 | class RequestServiceProvider implements ServiceProviderInterface 19 | { 20 | /** 21 | * Registers services on the given container. 22 | * This method should only be used to configure services and parameters. 23 | * It should not get services. 24 | * 25 | * @param \Pimple\Container $pimple A container instance 26 | */ 27 | public function register(Container $pimple) 28 | { 29 | $pimple['request'] = function ($app) { 30 | return Request::createFromGlobals(); 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Kernel/Providers/ServerServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel\Providers; 13 | 14 | use EasyDingTalk\Kernel\Server; 15 | use Pimple\Container; 16 | use Pimple\ServiceProviderInterface; 17 | 18 | class ServerServiceProvider implements ServiceProviderInterface 19 | { 20 | /** 21 | * Registers services on the given container. 22 | * This method should only be used to configure services and parameters. 23 | * It should not get services. 24 | * 25 | * @param \Pimple\Container $pimple A container instance 26 | */ 27 | public function register(Container $pimple) 28 | { 29 | $pimple['server'] = function ($app) { 30 | return new Server($app); 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Kernel/Server.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Kernel; 13 | 14 | use EasyDingTalk\Kernel\Exceptions\InvalidArgumentException; 15 | use EasyDingTalk\Kernel\Exceptions\RuntimeException; 16 | use function EasyDingTalk\tap; 17 | use Symfony\Component\HttpFoundation\Response; 18 | 19 | class Server 20 | { 21 | /** 22 | * @var \EasyDingTalk\Application 23 | */ 24 | protected $app; 25 | 26 | /** 27 | * @var array 28 | */ 29 | protected $handlers = []; 30 | 31 | /** 32 | * @param \EasyDingTalk\Application $app 33 | */ 34 | public function __construct($app) 35 | { 36 | $this->app = $app; 37 | } 38 | 39 | /** 40 | * Handle the request. 41 | * 42 | * @return \Symfony\Component\HttpFoundation\Response 43 | */ 44 | public function serve() 45 | { 46 | foreach ($this->handlers as $handler) { 47 | $handler->__invoke($this->getPayload()); 48 | } 49 | 50 | $this->app['logger']->debug('Request received: ', [ 51 | 'method' => $this->app['request']->getMethod(), 52 | 'uri' => $this->app['request']->getUri(), 53 | 'content' => $this->app['request']->getContent(), 54 | ]); 55 | 56 | return tap(new Response( 57 | $this->app['encryptor']->encrypt('success'), 200, ['Content-Type' => 'application/json'] 58 | ), function ($response) { 59 | $this->app['logger']->debug('Response created:', ['content' => $response->getContent()]); 60 | }); 61 | } 62 | 63 | /** 64 | * Push handler. 65 | * 66 | * @param \Closure|string|object $handler 67 | * 68 | * @return void 69 | * 70 | * @throws \EasyDingTalk\Kernel\Exceptions\InvalidArgumentException 71 | */ 72 | public function push($handler) 73 | { 74 | if (is_string($handler)) { 75 | $handler = function ($payload) use ($handler) { 76 | return (new $handler($this->app))->__invoke($payload); 77 | }; 78 | } 79 | 80 | if (!is_callable($handler)) { 81 | throw new InvalidArgumentException('Invalid handler'); 82 | } 83 | 84 | array_push($this->handlers, $handler); 85 | } 86 | 87 | /** 88 | * Get request payload. 89 | * 90 | * @return array 91 | */ 92 | public function getPayload() 93 | { 94 | $payload = json_decode($this->app['request']->getContent(), true); 95 | 96 | if (JSON_ERROR_NONE !== json_last_error()) { 97 | throw new RuntimeException('No payload received'); 98 | } 99 | 100 | $result = $this->app['encryptor']->decrypt( 101 | $payload['encrypt'], $this->app['request']->get('signature'), $this->app['request']->get('nonce'), $this->app['request']->get('timestamp') 102 | ); 103 | 104 | return json_decode($result, true); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Media/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Media; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 上传图片 20 | * 21 | * @param mixed $media 22 | * 23 | * @return mixed 24 | */ 25 | public function uploadImage($media) 26 | { 27 | return $this->upload('image', $media); 28 | } 29 | 30 | /** 31 | * 上传语音 32 | * 33 | * @param mixed $media 34 | * 35 | * @return mixed 36 | */ 37 | public function uploadVoice($media) 38 | { 39 | return $this->upload('voice', $media); 40 | } 41 | 42 | /** 43 | * 上传普通文件 44 | * 45 | * @param mixed $media 46 | * 47 | * @return mixed 48 | */ 49 | public function uploadFile($media) 50 | { 51 | return $this->upload('file', $media); 52 | } 53 | 54 | /** 55 | * 上传媒体文件 56 | * 57 | * @param string $type 58 | * @param mixed $media 59 | * 60 | * @return mixed 61 | */ 62 | public function upload($type, $media) 63 | { 64 | return $this->client->upload('media/upload', ['media' => $media], compact('type')); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Media/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Media; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['media'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Messages/File.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Messages; 13 | 14 | class File extends Message 15 | { 16 | protected $type = 'file'; 17 | 18 | protected function transform($value) 19 | { 20 | list($mediaId) = $value; 21 | 22 | return ['media_id' => $mediaId]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Messages/Image.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Messages; 13 | 14 | class Image extends Message 15 | { 16 | protected $type = 'image'; 17 | 18 | protected function transform($value) 19 | { 20 | list($mediaId) = $value; 21 | 22 | return ['media_id' => $mediaId]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Messages/Link.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Messages; 13 | 14 | class Link extends Message 15 | { 16 | protected $type = 'link'; 17 | 18 | public function setPictureUrl($value) 19 | { 20 | return $this->setAttribute('picUrl', $value); 21 | } 22 | 23 | public function setTitle($value) 24 | { 25 | return $this->setAttribute('title', $value); 26 | } 27 | 28 | public function setText($value) 29 | { 30 | return $this->setAttribute('text', $value); 31 | } 32 | 33 | protected function transform($value) 34 | { 35 | list($url) = $value; 36 | 37 | return ['messageUrl' => $url]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Messages/Message.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Messages; 13 | 14 | class Message 15 | { 16 | protected $value; 17 | protected $type; 18 | protected $attributes = []; 19 | 20 | public function __construct(...$value) 21 | { 22 | $this->value = $value; 23 | } 24 | 25 | public static function make() 26 | { 27 | return new static(...func_get_args()); 28 | } 29 | 30 | public function type() 31 | { 32 | return $this->type; 33 | } 34 | 35 | protected function transform($value) 36 | { 37 | return $value; 38 | } 39 | 40 | public function setAttribute($key, $value) 41 | { 42 | $this->attributes[$key] = $value; 43 | 44 | return $this; 45 | } 46 | 47 | public function toArray() 48 | { 49 | return [ 50 | 'msgtype' => $this->type(), 51 | $this->type() => array_merge($this->transform($this->value), $this->attributes), 52 | ]; 53 | } 54 | 55 | public function toJson() 56 | { 57 | return json_encode($this->toArray()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Messages/Text.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Messages; 13 | 14 | class Text extends Message 15 | { 16 | protected $type = 'text'; 17 | 18 | protected function transform($value) 19 | { 20 | list($content) = $value; 21 | 22 | return ['content' => $content]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Messages/Voice.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Messages; 13 | 14 | class Voice extends Message 15 | { 16 | protected $type = 'voice'; 17 | 18 | protected function transform($value) 19 | { 20 | list($mediaId, $duration) = $value; 21 | 22 | return ['media_id' => $mediaId, 'duration' => $duration]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Microapp/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Microapp; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取应用列表 20 | * 21 | * @return mixed 22 | */ 23 | public function list() 24 | { 25 | return $this->client->postJson('microapp/list'); 26 | } 27 | 28 | /** 29 | * 获取员工可见的应用列表 30 | * 31 | * @param string $userId 32 | * 33 | * @return mixed 34 | */ 35 | public function listByUserId($userId) 36 | { 37 | return $this->client->get('microapp/list_by_userid', [ 38 | 'userid' => $userId, 39 | ]); 40 | } 41 | 42 | /** 43 | * 获取应用的可见范围 44 | * 45 | * @param int $agentId 46 | * 47 | * @return mixed 48 | */ 49 | public function getVisibility($agentId) 50 | { 51 | return $this->client->postJson('microapp/visible_scopes', compact('agentId')); 52 | } 53 | 54 | /** 55 | * 设置应用的可见范围 56 | * 57 | * @param array $params 58 | * 59 | * @return mixed 60 | */ 61 | public function setVisibility($params) 62 | { 63 | return $this->client->postJson('microapp/set_visible_scopes', $params); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Microapp/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Microapp; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['microapp'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Process/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Process; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 发起审批实例 20 | * 21 | * @param array $params 22 | * 23 | * @return mixed 24 | */ 25 | public function create($params) 26 | { 27 | return $this->client->postJson('topapi/processinstance/create', $params); 28 | } 29 | 30 | /** 31 | * 批量获取审批实例 ID 32 | * 33 | * @param array $params 34 | * 35 | * @return mixed 36 | */ 37 | public function getIds($params) 38 | { 39 | return $this->client->postJson('topapi/processinstance/listids', $params); 40 | } 41 | 42 | /** 43 | * 获取单个审批实例 44 | * 45 | * @param string $id 46 | * 47 | * @return mixed 48 | */ 49 | public function get($id) 50 | { 51 | return $this->client->postJson('topapi/processinstance/get', ['process_instance_id' => $id]); 52 | } 53 | 54 | /** 55 | * 获取用户待审批数量 56 | * 57 | * @param string $userId 58 | * 59 | * @return mixed 60 | */ 61 | public function count($userId) 62 | { 63 | return $this->client->postJson('topapi/process/gettodonum', ['userid' => $userId]); 64 | } 65 | 66 | /** 67 | * 获取用户可见的审批模板 68 | * 69 | * @param string|null $userId 70 | * @param int $offset 71 | * @param int $size 72 | * 73 | * @return mixed 74 | */ 75 | public function listByUserId($userId = null, $offset = 0, $size = 100) 76 | { 77 | return $this->client->postJson('topapi/process/listbyuserid', ['userid' => $userId, 'offset' => $offset, 'size' => $size]); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Process/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Process; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['process'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Report/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Report; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取用户日志数据 20 | * 21 | * @param array $params 22 | * 23 | * @return mixed 24 | */ 25 | public function list($params) 26 | { 27 | return $this->client->postJson('topapi/report/list', $params); 28 | } 29 | 30 | /** 31 | * 获取用户可见的日志模板 32 | * 33 | * @param string|null $userId 34 | * @param int $offset 35 | * @param int $size 36 | * 37 | * @return mixed 38 | */ 39 | public function templates($userId = null, $offset = 0, $size = 100) 40 | { 41 | return $this->client->postJson('topapi/report/template/listbyuserid', [ 42 | 'userid' => $userId, 'offset' => $offset, 'size' => $size, 43 | ]); 44 | } 45 | 46 | /** 47 | * 获取用户日志未读数 48 | * 49 | * @param string $userid 50 | * 51 | * @return mixed 52 | */ 53 | public function unreadCount($userid) 54 | { 55 | return $this->client->postJson('topapi/report/getunreadcount', compact('userid')); 56 | } 57 | 58 | /** 59 | * 获取日志的已读人数、评论条数、评论人数、点赞人数。 60 | * 61 | * @param $report_id 62 | * 63 | * @return mixed 64 | */ 65 | public function statistics($report_id) 66 | { 67 | return $this->client->postJson('topapi/report/statistics', compact('report_id')); 68 | } 69 | 70 | /** 71 | * 获取日志相关人员列表,包括已读人员列表、评论人员列表、点赞人员列表 72 | * 73 | * @param string $report_id 74 | * @param int $type 75 | * @param int $offset 76 | * @param int $size 77 | * 78 | * @return mixed 79 | */ 80 | public function statisticsByType($report_id, $type, $offset = 0, $size = 100) 81 | { 82 | return $this->client->postJson('topapi/report/statistics/listbytype', [ 83 | 'report_id' => $report_id, 'type' => $type, 'offset' => $offset, 'size' => $size 84 | ]); 85 | } 86 | 87 | /** 88 | * 获取日志接收人员列表 89 | * 90 | * @param string $report_id 91 | * @param int $offset 92 | * @param int $size 93 | * 94 | * @return mixed 95 | */ 96 | public function getReceivers($report_id, $offset = 0, $size = 100) 97 | { 98 | return $this->client->postJson('topapi/report/receiver/list', [ 99 | 'report_id' => $report_id, 'offset' => $offset, 'size' => $size 100 | ]); 101 | } 102 | 103 | /** 104 | * 获取日志评论详情,包括评论人userid、评论内容、评论时间 105 | * 106 | * @param string $report_id 107 | * @param int $offset 108 | * @param int $size 109 | * 110 | * @return mixed 111 | */ 112 | public function getComments($report_id, $offset = 0, $size = 100) 113 | { 114 | return $this->client->postJson('topapi/report/comment/list', [ 115 | 'report_id' => $report_id, 'offset' => $offset, 'size' => $size 116 | ]); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/Report/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Report; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['report'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Robot.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk; 13 | 14 | use Overtrue\Http\Traits\HasHttpRequests; 15 | 16 | class Robot 17 | { 18 | use HasHttpRequests; 19 | 20 | /** 21 | * 机器人 AccessToken 22 | * 23 | * @var string 24 | */ 25 | protected $accessToken; 26 | 27 | /** 28 | * 加签 没有勾选,不用填写 29 | * 30 | * @var string 31 | */ 32 | protected $secret; 33 | 34 | /** 35 | * @param string $accessToken 36 | * @param string|null $secret 37 | */ 38 | public function __construct($accessToken, $secret = null) 39 | { 40 | $this->accessToken = $accessToken; 41 | $this->secret = $secret; 42 | } 43 | 44 | /** 45 | * @param string $accessToken 46 | * @param string|null $secret 47 | * 48 | * @return self 49 | */ 50 | public static function create($accessToken, $secret = null) 51 | { 52 | return new static($accessToken, $secret); 53 | } 54 | 55 | /** 56 | * 发送消息 57 | * 58 | * @param array $message 59 | * 60 | * @return array 61 | * 62 | * @throws \GuzzleHttp\Exception\GuzzleException 63 | */ 64 | public function send($message) 65 | { 66 | $url = 'https://oapi.dingtalk.com/robot/send?access_token='.$this->accessToken; 67 | 68 | if ($this->secret) { 69 | $timestamp = time().'000'; 70 | $url .= sprintf( 71 | '&sign=%s×tamp=%s', 72 | urlencode(base64_encode(hash_hmac('sha256', $timestamp."\n".$this->secret, $this->secret, true))), $timestamp 73 | ); 74 | } 75 | 76 | $response = $this->getHttpClient()->request( 77 | 'POST', $url, ['json' => $message] 78 | ); 79 | 80 | return $this->castResponseToType($response); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Role/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Role; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取角色列表 20 | * 21 | * @param int $offset 22 | * @param int $size 23 | * 24 | * @return mixed 25 | */ 26 | public function list($offset = null, $size = null) 27 | { 28 | return $this->client->postJson('topapi/role/list', compact('offset', 'size')); 29 | } 30 | 31 | /** 32 | * 获取角色下的员工列表 33 | * 34 | * @param int $roleId 35 | * @param int $offset 36 | * @param int $size 37 | * 38 | * @return mixed 39 | */ 40 | public function getUsers($roleId, $offset = null, $size = null) 41 | { 42 | return $this->client->postJson('topapi/role/simplelist', compact('offset', 'size') + ['role_id' => $roleId]); 43 | } 44 | 45 | /** 46 | * 获取角色组 47 | * 48 | * @param int $groupId 49 | * 50 | * @return mixed 51 | */ 52 | public function getGroups($groupId) 53 | { 54 | return $this->client->postJson('topapi/role/getrolegroup', ['group_id' => $groupId]); 55 | } 56 | 57 | /** 58 | * 获取角色详情 59 | * 60 | * @param int $roleId 61 | * 62 | * @return mixed 63 | */ 64 | public function get($roleId) 65 | { 66 | return $this->client->postJson('topapi/role/getrole', compact('roleId')); 67 | } 68 | 69 | /** 70 | * 创建角色 71 | * 72 | * @param int $groupId 73 | * @param string $roleName 74 | * 75 | * @return mixed 76 | */ 77 | public function create($groupId, $roleName) 78 | { 79 | return $this->client->postJson('role/add_role', compact('groupId', 'roleName')); 80 | } 81 | 82 | /** 83 | * 更新角色 84 | * 85 | * @param int $roleId 86 | * @param string $roleName 87 | * 88 | * @return mixed 89 | */ 90 | public function update($roleId, $roleName) 91 | { 92 | return $this->client->postJson('role/update_role', compact('roleId', 'roleName')); 93 | } 94 | 95 | /** 96 | * 删除角色 97 | * 98 | * @param int $roleId 99 | * 100 | * @return mixed 101 | */ 102 | public function delete($roleId) 103 | { 104 | return $this->client->postJson('topapi/role/deleterole', ['role_id' => $roleId]); 105 | } 106 | 107 | /** 108 | * 创建角色组 109 | * 110 | * @param string $name 111 | * 112 | * @return mixed 113 | */ 114 | public function createGroup($name) 115 | { 116 | return $this->client->postJson('role/add_role_group', compact('name')); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Role/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Role; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['role'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Schedule/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Schedule; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 发起待办 20 | * 21 | * @param array $params 22 | * 23 | * @return mixed 24 | */ 25 | public function add($params) 26 | { 27 | return $this->client->postJson('topapi/workrecord/add', $params); 28 | } 29 | 30 | /** 31 | * 更新待办 32 | * 33 | * @param string $userId 34 | * @param string $recordId 35 | * 36 | * @return mixed 37 | */ 38 | public function update($userId, $recordId) 39 | { 40 | return $this->client->postJson('topapi/workrecord/update', ['userid' => $userId, 'record_id' => $recordId]); 41 | } 42 | 43 | /** 44 | * 获取用户待办事项 45 | * 46 | * @param string $userId 47 | * @param bool $completed 48 | * @param int $offset 49 | * @param int $limit 50 | * 51 | * @return mixed 52 | */ 53 | public function list($userId, $completed, $offset, $limit) 54 | { 55 | return $this->client->postJson('topapi/workrecord/getbyuserid', [ 56 | 'userid' => $userId, 57 | 'status' => (int) $completed, 58 | 'offset' => $offset, 59 | 'limit' => $limit, 60 | ]); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Schedule/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Schedule; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['schedule'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/User/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\User; 13 | 14 | use EasyDingTalk\Kernel\BaseClient; 15 | 16 | class Client extends BaseClient 17 | { 18 | /** 19 | * 获取用户详情 20 | * 21 | * @param string $userid 22 | * @param string|null $lang 23 | * 24 | * @return mixed 25 | */ 26 | public function get($userid, $lang = null) 27 | { 28 | return $this->client->get('user/get', compact('userid', 'lang')); 29 | } 30 | 31 | /** 32 | * 获取部门用户 Userid 列表 33 | * 34 | * @param int $departmentId 35 | * 36 | * @return mixed 37 | */ 38 | public function getUserIds($departmentId) 39 | { 40 | return $this->client->get('user/getDeptMember', ['deptId' => $departmentId]); 41 | } 42 | 43 | /** 44 | * 获取部门用户 45 | * 46 | * @param int $departmentId 47 | * @param int $offset 48 | * @param int $size 49 | * @param string $order 50 | * @param string $lang 51 | * 52 | * @return mixed 53 | */ 54 | public function getUsers($departmentId, $offset, $size, $order = null, $lang = null) 55 | { 56 | return $this->client->get('user/simplelist', [ 57 | 'department_id' => $departmentId, 'offset' => $offset, 'size' => $size, 'order' => $order, 'lang' => $lang, 58 | ]); 59 | } 60 | 61 | /** 62 | * 获取部门用户详情 63 | * 64 | * @param int $departmentId 65 | * @param int $offset 66 | * @param int $size 67 | * @param string $order 68 | * @param string $lang 69 | * 70 | * @return mixed 71 | */ 72 | public function getDetailedUsers($departmentId, $offset, $size, $order = null, $lang = null) 73 | { 74 | return $this->client->get('user/listbypage', [ 75 | 'department_id' => $departmentId, 'offset' => $offset, 'size' => $size, 'order' => $order, 'lang' => $lang, 76 | ]); 77 | } 78 | 79 | /** 80 | * 获取管理员列表 81 | * 82 | * @return mixed 83 | */ 84 | public function administrators() 85 | { 86 | return $this->client->get('user/get_admin'); 87 | } 88 | 89 | /** 90 | * 获取管理员通讯录权限范围 91 | * 92 | * @param string $userid 93 | * 94 | * @return mixed 95 | */ 96 | public function administratorScope($userid) 97 | { 98 | return $this->client->get('topapi/user/get_admin_scope', compact('userid')); 99 | } 100 | 101 | /** 102 | * 根据 Unionid 获取 Userid 103 | * 104 | * @param string $unionid 105 | * 106 | * @return mixed 107 | */ 108 | public function getUseridByUnionid($unionid) 109 | { 110 | return $this->client->get('user/getUseridByUnionid', compact('unionid')); 111 | } 112 | 113 | /** 114 | * 创建用户 115 | * 116 | * @param array $params 117 | * 118 | * @return mixed 119 | */ 120 | public function create(array $params) 121 | { 122 | return $this->client->postJson('user/create', $params); 123 | } 124 | 125 | /** 126 | * 更新用户 127 | * 128 | * @param string $userid 129 | * @param array $params 130 | * 131 | * @return mixed 132 | */ 133 | public function update($userid, array $params) 134 | { 135 | return $this->client->postJson('user/update', compact('userid') + $params); 136 | } 137 | 138 | /** 139 | * 删除用户 140 | * 141 | * @param $userid 142 | * 143 | * @return mixed 144 | */ 145 | public function delete($userid) 146 | { 147 | return $this->client->get('user/delete', compact('userid')); 148 | } 149 | 150 | /** 151 | * 企业内部应用免登获取用户 Userid 152 | * 153 | * @param string $code 154 | * 155 | * @return mixed 156 | */ 157 | public function getUserByCode($code) 158 | { 159 | return $this->client->get('user/getuserinfo', compact('code')); 160 | } 161 | 162 | /** 163 | * 批量增加员工角色 164 | * 165 | * @param array|string $userIds 166 | * @param array|string $roleIds 167 | * 168 | * @return mixed 169 | */ 170 | public function addRoles($userIds, $roleIds) 171 | { 172 | $userIds = is_array($userIds) ? implode(',', $userIds) : $userIds; 173 | $roleIds = is_array($roleIds) ? implode(',', $roleIds) : $roleIds; 174 | 175 | return $this->client->postJson('topapi/role/addrolesforemps', compact('userIds', 'roleIds')); 176 | } 177 | 178 | /** 179 | * 批量删除员工角色 180 | * 181 | * @param array|string $userIds 182 | * @param array|string $roleIds 183 | * 184 | * @return mixed 185 | */ 186 | public function removeRoles($userIds, $roleIds) 187 | { 188 | $userIds = is_array($userIds) ? implode(',', $userIds) : $userIds; 189 | $roleIds = is_array($roleIds) ? implode(',', $roleIds) : $roleIds; 190 | 191 | return $this->client->postJson('topapi/role/removerolesforemps', compact('userIds', 'roleIds')); 192 | } 193 | 194 | /** 195 | * 获取企业员工人数 196 | * 197 | * @param int $onlyActive 198 | * 199 | * @return mixed 200 | */ 201 | public function getCount($onlyActive = 0) 202 | { 203 | return $this->client->get('user/get_org_user_count', compact('onlyActive')); 204 | } 205 | 206 | /** 207 | * 获取企业已激活的员工人数 208 | * 209 | * @return mixed 210 | */ 211 | public function getActivatedCount() 212 | { 213 | return $this->getCount(1); 214 | } 215 | 216 | /** 217 | * 根据员工手机号获取 Userid 218 | * 219 | * @param string $mobile 220 | * 221 | * @return mixed 222 | */ 223 | public function getUserIdByPhone($mobile = '') 224 | { 225 | return $this->client->get('user/get_by_mobile', compact('mobile')); 226 | } 227 | 228 | /** 229 | * 未登录钉钉的员工列表 230 | * 231 | * @param string $query_date 232 | * @param int $offset 233 | * @param int $size 234 | * 235 | * @return mixed 236 | */ 237 | public function getInactiveUsers($query_date, $offset, $size) 238 | { 239 | return $this->client->postJson('topapi/inactive/user/get', [ 240 | 'query_date' => $query_date, 'offset' => $offset, 'size' => $size 241 | ]); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/User/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\User; 13 | 14 | use Pimple\Container; 15 | use Pimple\ServiceProviderInterface; 16 | 17 | class ServiceProvider implements ServiceProviderInterface 18 | { 19 | /** 20 | * Registers services on the given container. 21 | * This method should only be used to configure services and parameters. 22 | * It should not get services. 23 | * 24 | * @param \Pimple\Container $pimple A container instance 25 | */ 26 | public function register(Container $pimple) 27 | { 28 | $pimple['user'] = function ($app) { 29 | return new Client($app); 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk; 13 | 14 | /** 15 | * @param mixed $value 16 | * @param callable $callback 17 | * 18 | * @return mixed 19 | */ 20 | function tap($value, $callback) 21 | { 22 | $callback($value); 23 | 24 | return $value; 25 | } 26 | 27 | /** 28 | * Generate a more truly "random" alpha-numeric string. 29 | * 30 | * @param int $length 31 | * 32 | * @return string 33 | */ 34 | function str_random($length = 16) 35 | { 36 | $string = ''; 37 | 38 | while (($len = strlen($string)) < $length) { 39 | $size = $length - $len; 40 | $bytes = random_bytes($size); 41 | $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size); 42 | } 43 | 44 | return $string; 45 | } 46 | -------------------------------------------------------------------------------- /tests/ApplicationTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests; 13 | 14 | use EasyDingTalk\Application; 15 | 16 | class ApplicationTest extends TestCase 17 | { 18 | /** @test */ 19 | public function services() 20 | { 21 | $app = new Application(); 22 | 23 | $services = [ 24 | 'logger' => \Monolog\Logger::class, 25 | 'chat' => \EasyDingTalk\Chat\Client::class, 26 | 'user' => \EasyDingTalk\User\Client::class, 27 | 'role' => \EasyDingTalk\Role\Client::class, 28 | 'media' => \EasyDingTalk\Media\Client::class, 29 | 'sso' => \EasyDingTalk\Auth\SsoClient::class, 30 | 'server' => \EasyDingTalk\Kernel\Server::class, 31 | 'report' => \EasyDingTalk\Report\Client::class, 32 | 'health' => \EasyDingTalk\Health\Client::class, 33 | 'checkin' => \EasyDingTalk\Checkin\Client::class, 34 | 'contact' => \EasyDingTalk\Contact\Client::class, 35 | 'oauth' => \EasyDingTalk\Auth\OAuthClient::class, 36 | 'process' => \EasyDingTalk\Process\Client::class, 37 | 'callback' => \EasyDingTalk\Callback\Client::class, 38 | 'calendar' => \EasyDingTalk\Calendar\Client::class, 39 | 'schedule' => \EasyDingTalk\Schedule\Client::class, 40 | 'microapp' => \EasyDingTalk\Microapp\Client::class, 41 | 'h5app' => \EasyDingTalk\H5app\Client::class, 42 | 'client' => \EasyDingTalk\Kernel\Http\Client::class, 43 | 'config' => \Overtrue\Http\Support\Collection::class, 44 | 'blackboard' => \EasyDingTalk\Blackboard\Client::class, 45 | 'attendance' => \EasyDingTalk\Attendance\Client::class, 46 | 'department' => \EasyDingTalk\Department\Client::class, 47 | 'access_token' => \EasyDingTalk\Kernel\AccessToken::class, 48 | 'conversation' => \EasyDingTalk\Conversation\Client::class, 49 | 'request' => \Symfony\Component\HttpFoundation\Request::class, 50 | 'encryptor' => \EasyDingTalk\Kernel\Encryption\Encryptor::class, 51 | ]; 52 | 53 | $this->assertCount(count($services), $app->keys()); 54 | foreach ($services as $name => $service) { 55 | $this->assertInstanceof($service, $app->{$name}); 56 | $this->assertInstanceof($service, $app[$name]); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Blackboard/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Blackboard; 13 | 14 | use EasyDingTalk\Blackboard\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function list() 21 | { 22 | $this->make(Client::class)->list('mingyoung') 23 | ->assertUri('topapi/blackboard/listtopten')->assertPostJson(['userid' => 'mingyoung']); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Calendar/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Calendar; 13 | 14 | use EasyDingTalk\Calendar\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function create() 21 | { 22 | $this->make(Client::class)->create($expected = ['foo' => 'bar']) 23 | ->assertUri('topapi/calendar/create')->assertPostJson($expected); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Callback/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Callback; 13 | 14 | use EasyDingTalk\Callback\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function register() 21 | { 22 | $this->make(Client::class)->register($params = ['call_back_tag' => ['foo', 'bar']]) 23 | ->assertPostUri('call_back/register_call_back')->assertPostJson(array_merge($params, ['token' => 'test-token', 'aes_key' => 'test-aes-key'])); 24 | } 25 | 26 | /** @test */ 27 | public function list() 28 | { 29 | $this->make(Client::class)->list() 30 | ->assertGetUri('call_back/get_call_back'); 31 | } 32 | 33 | /** @test */ 34 | public function update() 35 | { 36 | $this->make(Client::class)->update($params = ['call_back_tag' => ['foo', 'bar']]) 37 | ->assertPostUri('call_back/update_call_back')->assertPostJson(array_merge($params, ['token' => 'test-token', 'aes_key' => 'test-aes-key'])); 38 | } 39 | 40 | /** @test */ 41 | public function delete() 42 | { 43 | $this->make(Client::class)->delete() 44 | ->assertGetUri('call_back/delete_call_back'); 45 | } 46 | 47 | /** @test */ 48 | public function failed() 49 | { 50 | $this->make(Client::class)->failed() 51 | ->assertGetUri('call_back/get_call_back_failed_result'); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Chat/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Chat; 13 | 14 | use EasyDingTalk\Chat\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function send() 21 | { 22 | $this->make(Client::class)->send('foobar', ['foo' => 'bar']) 23 | ->assertPostUri('chat/send')->assertPostJson([ 24 | 'chatid' => 'foobar', 25 | 'msg' => ['foo' => 'bar'], 26 | ]); 27 | } 28 | 29 | /** @test */ 30 | public function result() 31 | { 32 | $this->make(Client::class)->result('message-id', 0, 100) 33 | ->assertGetUri('chat/getReadList')->assertQuery([ 34 | 'messageId' => 'message-id', 35 | 'cursor' => 0, 36 | 'size' => 100, 37 | ]); 38 | } 39 | 40 | /** @test */ 41 | public function create() 42 | { 43 | $this->make(Client::class)->create($params = ['name' => 'EasyDingTalk', 'owner' => 'mingyoung', 'useridlist' => ['mingyoung', 'member']]) 44 | ->assertPostUri('chat/create')->assertPostJson($params); 45 | } 46 | 47 | /** @test */ 48 | public function update() 49 | { 50 | $this->make(Client::class)->update('chat-id', $params = ['name' => 'EasyDingTalk', 'owner' => 'mingyoung', 'add_useridlist' => ['member']]) 51 | ->assertPostUri('chat/update')->assertPostJson(array_merge(['chatid' => 'chat-id'], $params)); 52 | } 53 | 54 | /** @test */ 55 | public function get() 56 | { 57 | $this->make(Client::class)->get('chat-id') 58 | ->assertGetUri('chat/get')->assertQuery(['chatid' => 'chat-id']); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Contact/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Contact; 13 | 14 | use EasyDingTalk\Contact\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function labels() 21 | { 22 | $this->make(Client::class)->labels(0, 20) 23 | ->assertUri('topapi/extcontact/listlabelgroups')->assertPostJson(['offset' => 0, 'size' => 20]); 24 | } 25 | 26 | /** @test */ 27 | public function list() 28 | { 29 | $this->make(Client::class)->list(0, 20) 30 | ->assertUri('topapi/extcontact/list')->assertPostJson(['offset' => 0, 'size' => 20]); 31 | } 32 | 33 | /** @test */ 34 | public function get() 35 | { 36 | $this->make(Client::class)->get('mingyoung') 37 | ->assertUri('topapi/extcontact/get')->assertPostJson(['user_id' => 'mingyoung']); 38 | } 39 | 40 | /** @test */ 41 | public function create() 42 | { 43 | $this->make(Client::class)->create(['name' => 'MINGYOUNG']) 44 | ->assertUri('topapi/extcontact/create')->assertPostJson(['contact' => [ 45 | 'name' => 'MINGYOUNG', 46 | ]]); 47 | } 48 | 49 | /** @test */ 50 | public function update() 51 | { 52 | $this->make(Client::class)->update(123, ['name' => 'MINGYOUNG']) 53 | ->assertUri('topapi/extcontact/update')->assertPostJson(['contact' => [ 54 | 'user_id' => 123, 'name' => 'MINGYOUNG', 55 | ]]); 56 | } 57 | 58 | /** @test */ 59 | public function delete() 60 | { 61 | $this->make(Client::class)->delete('mingyoung') 62 | ->assertUri('topapi/extcontact/delete')->assertPostJson(['user_id' => 'mingyoung']); 63 | } 64 | 65 | /** @test */ 66 | public function scopes() 67 | { 68 | $this->make(Client::class)->scopes() 69 | ->assertUri('auth/scopes'); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/Conversation/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Conversation; 13 | 14 | use EasyDingTalk\Conversation\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function sendGeneralMessage() 21 | { 22 | $this->make(Client::class)->sendGeneralMessage('sender-foo', 'cid-bar', ['foo' => 'bar']) 23 | ->assertPostUri('message/send_to_conversation') 24 | ->assertPostJson([ 25 | 'sender' => 'sender-foo', 'cid' => 'cid-bar', 26 | 'msg' => ['foo' => 'bar'], 27 | ]); 28 | } 29 | 30 | /** @test */ 31 | public function sendCorporationMessage() 32 | { 33 | $this->make(Client::class)->sendCorporationMessage($params = ['foo' => 'bar']) 34 | ->assertPostUri('topapi/message/corpconversation/asyncsend_v2') 35 | ->assertPostFormParams($params); 36 | } 37 | 38 | /** @test */ 39 | public function progress() 40 | { 41 | $this->make(Client::class)->corporationMessage('task-id')->progress() 42 | ->assertPostUri('topapi/message/corpconversation/getsendprogress') 43 | ->assertPostJson(['agent_id' => 'mock-agent', 'task_id' => 'task-id']); 44 | } 45 | 46 | /** @test */ 47 | public function result() 48 | { 49 | $this->make(Client::class)->corporationMessage('task-id')->result() 50 | ->assertPostUri('topapi/message/corpconversation/getsendresult') 51 | ->assertPostJson(['agent_id' => 'mock-agent', 'task_id' => 'task-id']); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Department/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Department; 13 | 14 | use EasyDingTalk\Department\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function getSubDepartmentIds() 21 | { 22 | $this->make(Client::class)->getSubDepartmentIds('test-id') 23 | ->assertUri('department/list_ids')->assertQuery(['id' => 'test-id']); 24 | } 25 | 26 | /** @test */ 27 | public function list() 28 | { 29 | $this->make(Client::class)->list() 30 | ->assertUri('department/list')->assertQuery(['id' => null, 'lang' => null, 'fetch_child' => 'false']); 31 | } 32 | 33 | /** @test */ 34 | public function get() 35 | { 36 | $this->make(Client::class)->get(1) 37 | ->assertUri('department/get')->assertQuery(['id' => 1, 'lang' => null]); 38 | } 39 | 40 | /** @test */ 41 | public function getParentsById() 42 | { 43 | $this->make(Client::class)->getParentsById(1) 44 | ->assertUri('department/list_parent_depts_by_dept')->assertQuery(['id' => 1]); 45 | } 46 | 47 | /** @test */ 48 | public function getParentsByUserId() 49 | { 50 | $this->make(Client::class)->getParentsByUserId('mingyoung') 51 | ->assertUri('department/list_parent_depts')->assertQuery(['userId' => 'mingyoung']); 52 | } 53 | 54 | /** @test */ 55 | public function create() 56 | { 57 | $this->make(Client::class)->create(['name' => 'EasyDingTalk']) 58 | ->assertUri('department/create')->assertPostJson(['name' => 'EasyDingTalk']); 59 | } 60 | 61 | /** @test */ 62 | public function update() 63 | { 64 | $this->make(Client::class)->update(1, ['name' => 'EasyDingTalk']) 65 | ->assertUri('department/update')->assertPostJson(['id' => 1, 'name' => 'EasyDingTalk']); 66 | } 67 | 68 | /** @test */ 69 | public function delete() 70 | { 71 | $this->make(Client::class)->delete(1) 72 | ->assertUri('department/delete')->assertQuery(['id' => 1]); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Health/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Health; 13 | 14 | use EasyDingTalk\Health\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function status() 21 | { 22 | $this->make(Client::class)->status('mingyoung') 23 | ->assertPostUri('topapi/health/stepinfo/getuserstatus') 24 | ->assertPostJson(['userid' => 'mingyoung']); 25 | } 26 | 27 | /** @test */ 28 | public function byUser() 29 | { 30 | $this->make(Client::class)->byUser('mingyoung', '20180101,20180102') 31 | ->assertPostUri('topapi/health/stepinfo/list') 32 | ->assertPostJson(['type' => 0, 'object_id' => 'mingyoung', 'stat_dates' => '20180101,20180102']); 33 | } 34 | 35 | /** @test */ 36 | public function byDepartment() 37 | { 38 | $this->make(Client::class)->byDepartment('mingyoung', '20180101,20180102') 39 | ->assertPostUri('topapi/health/stepinfo/list') 40 | ->assertPostJson(['type' => 1, 'object_id' => 'mingyoung', 'stat_dates' => '20180101,20180102']); 41 | } 42 | 43 | /** @test */ 44 | public function byUsers() 45 | { 46 | $this->make(Client::class)->byUsers(['mingyoung', 'cc'], '20180101') 47 | ->assertPostUri('topapi/health/stepinfo/listbyuserid') 48 | ->assertPostJson(['userids' => 'mingyoung,cc', 'stat_date' => '20180101']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Kernel/Concerns/InteractsWithCacheTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Kernel\Concerns; 13 | 14 | use EasyDingTalk\Kernel\Concerns\InteractsWithCache; 15 | use EasyDingTalk\Tests\TestCase; 16 | use Mockery; 17 | use Psr\SimpleCache\CacheInterface; 18 | 19 | class InteractsWithCacheTest extends TestCase 20 | { 21 | /** @test */ 22 | public function getCache() 23 | { 24 | $cache = Mockery::mock(InteractsWithCache::class); 25 | 26 | $this->assertInstanceof(CacheInterface::class, $cache->getCache()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Kernel/Encryption/EncryptorTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Kernel\Encryption; 13 | 14 | use EasyDingTalk\Kernel\Encryption\Encryptor; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class EncryptorTest extends TestCase 18 | { 19 | /** 20 | * @return \EasyDingTalk\Kernel\Encryption\Encryptor 21 | */ 22 | public function makeEncryptor() 23 | { 24 | return new Encryptor('suite4xxxxxxxxxxxxxxx', '123456', '4g5j64qlyl3zvetqxz5jiocdr586fn2zvjpa8zls3ij'); 25 | } 26 | 27 | /** @test */ 28 | public function encrypt() 29 | { 30 | $encryptor = $this->makeEncryptor(); 31 | $result = json_decode($encryptor->encrypt('{"EventType":"check_create_suite_url","Random":"LPIdSnlF","TestSuiteKey":"suite4xxxxxxxxxxxxxxx"}'), true); 32 | 33 | $result = $encryptor->decrypt( 34 | $result['encrypt'], $result['msg_signature'], $result['nonce'], $result['timeStamp'] 35 | ); 36 | 37 | $this->assertSameDecryptedData($result); 38 | } 39 | 40 | /** @test */ 41 | public function decrypt() 42 | { 43 | $encryptor = $this->makeEncryptor(); 44 | 45 | $result = $encryptor->decrypt( 46 | '1a3NBxmCFwkCJvfoQ7WhJHB+iX3qHPsc9JbaDznE1i03peOk1LaOQoRz3+nlyGNhwmwJ3vDMG+OzrHMeiZI7gTRWVdUBmfxjZ8Ej23JVYa9VrYeJ5as7XM/ZpulX8NEQis44w53h1qAgnC3PRzM7Zc/D6Ibr0rgUathB6zRHP8PYrfgnNOS9PhSBdHlegK+AGGanfwjXuQ9+0pZcy0w9lQ==', 47 | '5a65ceeef9aab2d149439f82dc191dd6c5cbe2c0', 48 | 'nEXhMP4r', 49 | '1445827045067' 50 | ); 51 | 52 | $this->assertSameDecryptedData($result); 53 | } 54 | 55 | protected function assertSameDecryptedData($result) 56 | { 57 | $this->assertSame('{"EventType":"check_create_suite_url","Random":"LPIdSnlF","TestSuiteKey":"suite4xxxxxxxxxxxxxxx"}', $result); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Media/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Media; 13 | 14 | use EasyDingTalk\Media\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function uploadImage() 21 | { 22 | $this->make(Client::class)->uploadImage(__DIR__.'/__fixtures__/foo.stub') 23 | ->assertPostUri('media/upload'); 24 | } 25 | 26 | /** @test */ 27 | public function uploadVoice() 28 | { 29 | $this->make(Client::class)->uploadVoice(__DIR__.'/__fixtures__/foo.stub') 30 | ->assertPostUri('media/upload'); 31 | } 32 | 33 | /** @test */ 34 | public function uploadFile() 35 | { 36 | $this->make(Client::class)->uploadFile(__DIR__.'/__fixtures__/foo.stub') 37 | ->assertPostUri('media/upload'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Media/__fixtures__/foo.stub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingyoung/dingtalk/f00500f40514015429b18bc9178c7270a1d7dea9/tests/Media/__fixtures__/foo.stub -------------------------------------------------------------------------------- /tests/Messages/FileTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Messages; 13 | 14 | use EasyDingTalk\Messages\File; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class FileTest extends TestCase 18 | { 19 | /** @test */ 20 | public function staticMake() 21 | { 22 | $message = File::make('media-id'); 23 | $expected = [ 24 | 'msgtype' => 'file', 25 | 'file' => [ 26 | 'media_id' => 'media-id', 27 | ], 28 | ]; 29 | 30 | $this->assertSame($expected, $message->toArray()); 31 | $this->assertSame(json_encode($expected), $message->toJson()); 32 | } 33 | 34 | /** @test */ 35 | public function new() 36 | { 37 | $message = new File('media-id'); 38 | $expected = [ 39 | 'msgtype' => 'file', 40 | 'file' => [ 41 | 'media_id' => 'media-id', 42 | ], 43 | ]; 44 | 45 | $this->assertSame($expected, $message->toArray()); 46 | $this->assertSame(json_encode($expected), $message->toJson()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Messages/ImageTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Messages; 13 | 14 | use EasyDingTalk\Messages\Image; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ImageTest extends TestCase 18 | { 19 | /** @test */ 20 | public function staticMake() 21 | { 22 | $message = Image::make('media-id'); 23 | $expected = [ 24 | 'msgtype' => 'image', 25 | 'image' => [ 26 | 'media_id' => 'media-id', 27 | ], 28 | ]; 29 | 30 | $this->assertSame($expected, $message->toArray()); 31 | $this->assertSame(json_encode($expected), $message->toJson()); 32 | } 33 | 34 | /** @test */ 35 | public function new() 36 | { 37 | $message = new Image('media-id'); 38 | 39 | $expected = [ 40 | 'msgtype' => 'image', 41 | 'image' => [ 42 | 'media_id' => 'media-id', 43 | ], 44 | ]; 45 | 46 | $this->assertSame($expected, $message->toArray()); 47 | $this->assertSame(json_encode($expected), $message->toJson()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Messages/LinkTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Messages; 13 | 14 | use EasyDingTalk\Messages\Link; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class LinkTest extends TestCase 18 | { 19 | /** @test */ 20 | public function staticMake() 21 | { 22 | $message = Link::make('https://example.com')->setPictureUrl('@lALOACZwe2Rk')->setTitle('测试')->setText('测试'); 23 | $expected = [ 24 | 'msgtype' => 'link', 25 | 'link' => [ 26 | 'messageUrl' => 'https://example.com', 27 | 'picUrl' => '@lALOACZwe2Rk', 28 | 'title' => '测试', 29 | 'text' => '测试', 30 | ], 31 | ]; 32 | 33 | $this->assertSame($expected, $message->toArray()); 34 | $this->assertSame(json_encode($expected), $message->toJson()); 35 | } 36 | 37 | /** @test */ 38 | public function new() 39 | { 40 | $message = (new Link('https://example.com'))->setPictureUrl('@lALOACZwe2Rk')->setTitle('测试')->setText('测试'); 41 | $expected = [ 42 | 'msgtype' => 'link', 43 | 'link' => [ 44 | 'messageUrl' => 'https://example.com', 45 | 'picUrl' => '@lALOACZwe2Rk', 46 | 'title' => '测试', 47 | 'text' => '测试', 48 | ], 49 | ]; 50 | 51 | $this->assertSame($expected, $message->toArray()); 52 | $this->assertSame(json_encode($expected), $message->toJson()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Messages/TextTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Messages; 13 | 14 | use EasyDingTalk\Messages\Text; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class TextTest extends TestCase 18 | { 19 | /** @test */ 20 | public function staticMake() 21 | { 22 | $message = Text::make('mock'); 23 | $expected = [ 24 | 'msgtype' => 'text', 25 | 'text' => [ 26 | 'content' => 'mock', 27 | ], 28 | ]; 29 | 30 | $this->assertSame($expected, $message->toArray()); 31 | $this->assertSame(json_encode($expected), $message->toJson()); 32 | } 33 | 34 | /** @test */ 35 | public function new() 36 | { 37 | $message = new Text('mock'); 38 | $expected = [ 39 | 'msgtype' => 'text', 40 | 'text' => [ 41 | 'content' => 'mock', 42 | ], 43 | ]; 44 | 45 | $this->assertSame($expected, $message->toArray()); 46 | $this->assertSame(json_encode($expected), $message->toJson()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Messages/VoiceTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Messages; 13 | 14 | use EasyDingTalk\Messages\Voice; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class VoiceTest extends TestCase 18 | { 19 | /** @test */ 20 | public function staticMake() 21 | { 22 | $message = Voice::make('media-id', 10); 23 | $expected = [ 24 | 'msgtype' => 'voice', 25 | 'voice' => [ 26 | 'media_id' => 'media-id', 27 | 'duration' => 10, 28 | ], 29 | ]; 30 | 31 | $this->assertSame($expected, $message->toArray()); 32 | $this->assertSame(json_encode($expected), $message->toJson()); 33 | } 34 | 35 | /** @test */ 36 | public function new() 37 | { 38 | $message = new Voice('media-id', 10); 39 | 40 | $expected = [ 41 | 'msgtype' => 'voice', 42 | 'voice' => [ 43 | 'media_id' => 'media-id', 44 | 'duration' => 10, 45 | ], 46 | ]; 47 | 48 | $this->assertSame($expected, $message->toArray()); 49 | $this->assertSame(json_encode($expected), $message->toJson()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Microapp/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Microapp; 13 | 14 | use EasyDingTalk\Microapp\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function list() 21 | { 22 | $this->make(Client::class)->list() 23 | ->assertUri('microapp/list')->assertEmptyQuery(); 24 | } 25 | 26 | /** @test */ 27 | public function listByUserId() 28 | { 29 | $this->make(Client::class)->listByUserId('mingyoung') 30 | ->assertUri('microapp/list_by_userid')->assertQuery(['userid' => 'mingyoung']); 31 | } 32 | 33 | /** @test */ 34 | public function getVisibility() 35 | { 36 | $this->make(Client::class)->getVisibility('123') 37 | ->assertUri('microapp/visible_scopes')->assertPostJson(['agentId' => '123']); 38 | } 39 | 40 | /** @test */ 41 | public function setVisibility() 42 | { 43 | $this->make(Client::class)->setVisibility([ 44 | 'agentId' => 123456, 45 | 'isHidden' => false, 46 | 'deptVisibleScopes' => [1, 2], 47 | 'userVisibleScopes' => ['user1', 'user2'], 48 | ])->assertUri('microapp/set_visible_scopes')->assertPostJson([ 49 | 'agentId' => 123456, 50 | 'isHidden' => false, 51 | 'deptVisibleScopes' => [1, 2], 52 | 'userVisibleScopes' => ['user1', 'user2'], 53 | ]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/Report/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Report; 13 | 14 | use EasyDingTalk\Report\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function list() 21 | { 22 | $expected = [ 23 | 'start_time' => 1507564800000, 24 | 'end_time' => 1507564800000, 25 | 'userid' => 'mingyoung', 26 | ]; 27 | 28 | $this->make(Client::class)->list($expected) 29 | ->assertUri('topapi/report/list')->assertPostJson($expected); 30 | } 31 | 32 | /** @test */ 33 | public function templates() 34 | { 35 | $expected = [ 36 | 'userid' => 'mingyoung', 37 | 'offset' => 100, 38 | 'size' => 50, 39 | ]; 40 | 41 | $this->make(Client::class)->templates('mingyoung', 100, 50) 42 | ->assertUri('topapi/report/template/listbyuserid')->assertPostJson($expected); 43 | } 44 | 45 | /** @test */ 46 | public function unreadCount() 47 | { 48 | $this->make(Client::class)->unreadCount('mingyoung') 49 | ->assertUri('topapi/report/getunreadcount')->assertPostJson(['userid' => 'mingyoung']); 50 | } 51 | 52 | /** @test */ 53 | public function statistics() 54 | { 55 | $this->make(Client::class)->statistics('xxxxxxx') 56 | ->assertUri('topapi/report/statistics')->assertPostJson(['report_id' => 'xxxxxxx']); 57 | } 58 | 59 | /** @test */ 60 | public function statisticsByType() 61 | { 62 | $this->make(Client::class)->statisticsByType('xxxxxxx',0) 63 | ->assertUri('topapi/report/statistics/listbytype')->assertPostJson([ 64 | 'report_id' => 'xxxxxxx', 'type' => 0, 'offset' => 0, 'size' => 100, 65 | ]); 66 | } 67 | 68 | /** @test */ 69 | public function getReceivers() 70 | { 71 | $this->make(Client::class)->getReceivers('xxxxxxx') 72 | ->assertUri('topapi/report/receiver/list')->assertPostJson([ 73 | 'report_id' => 'xxxxxxx', 'offset' => 0, 'size' => 100, 74 | ]); 75 | } 76 | 77 | /** @test */ 78 | public function getComments() 79 | { 80 | $this->make(Client::class)->getComments('mingyoung') 81 | ->assertUri('topapi/report/comment/list')->assertPostJson([ 82 | 'report_id' => 'mingyoung', 'offset' => 0, 'size' => 100, 83 | ]); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/Role/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Role; 13 | 14 | use EasyDingTalk\Role\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function list() 21 | { 22 | $this->make(Client::class)->list() 23 | ->assertUri('topapi/role/list')->assertPostJson(['offset' => null, 'size' => null]); 24 | } 25 | 26 | /** @test */ 27 | public function getUsers() 28 | { 29 | $this->make(Client::class)->getUsers(123) 30 | ->assertUri('topapi/role/simplelist')->assertPostJson(['offset' => null, 'size' => null, 'role_id' => 123]); 31 | } 32 | 33 | /** @test */ 34 | public function getRoleGroups() 35 | { 36 | $this->make(Client::class)->getGroups(123) 37 | ->assertUri('topapi/role/getrolegroup')->assertPostJson(['group_id' => 123]); 38 | } 39 | 40 | /** @test */ 41 | public function get() 42 | { 43 | $this->make(Client::class)->get(123) 44 | ->assertUri('topapi/role/getrole')->assertPostJson(['roleId' => 123]); 45 | } 46 | 47 | /** @test */ 48 | public function create() 49 | { 50 | $this->make(Client::class)->create(123, 'Admin') 51 | ->assertUri('role/add_role')->assertPostJson(['groupId' => 123, 'roleName' => 'Admin']); 52 | } 53 | 54 | /** @test */ 55 | public function update() 56 | { 57 | $this->make(Client::class)->update(123, 'Admin') 58 | ->assertUri('role/update_role')->assertPostJson(['roleId' => 123, 'roleName' => 'Admin']); 59 | } 60 | 61 | /** @test */ 62 | public function delete() 63 | { 64 | $this->make(Client::class)->delete(123) 65 | ->assertUri('topapi/role/deleterole')->assertPostJson(['role_id' => 123]); 66 | } 67 | 68 | /** @test */ 69 | public function createGroup() 70 | { 71 | $this->make(Client::class)->createGroup('Group') 72 | ->assertUri('role/add_role_group')->assertPostJson(['name' => 'Group']); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Schedule/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\Schedule; 13 | 14 | use EasyDingTalk\Schedule\Client; 15 | use EasyDingTalk\Tests\TestCase; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function add() 21 | { 22 | $this->make(Client::class)->add($params = [ 23 | 'userid' => 'mingyoung', 24 | 'create_time' => 1496678400000, 25 | 'title' => '标题', 26 | 'url' => 'https://easydingtalk.org', 27 | 'formItemList' => [ 28 | [ 29 | 'title' => '标题', 30 | 'content' => '内容', 31 | ], 32 | ], 33 | ]) 34 | ->assertUri('topapi/workrecord/add') 35 | ->assertPostJson($params); 36 | } 37 | 38 | /** @test */ 39 | public function update() 40 | { 41 | $this->make(Client::class)->update('mingyoung', 'record123') 42 | ->assertUri('topapi/workrecord/update') 43 | ->assertPostJson(['userid' => 'mingyoung', 'record_id' => 'record123']); 44 | } 45 | 46 | /** @test */ 47 | public function completedList() 48 | { 49 | $this->make(Client::class)->list('mingyoung', true, 0, 50) 50 | ->assertUri('topapi/workrecord/getbyuserid') 51 | ->assertPostJson([ 52 | 'userid' => 'mingyoung', 53 | 'status' => 1, 54 | 'offset' => 0, 55 | 'limit' => 50, 56 | ]); 57 | } 58 | 59 | /** @test */ 60 | public function incompletedList() 61 | { 62 | $this->make(Client::class)->list('mingyoung', false, 0, 50) 63 | ->assertUri('topapi/workrecord/getbyuserid') 64 | ->assertPostJson([ 65 | 'userid' => 'mingyoung', 66 | 'status' => 0, 67 | 'offset' => 0, 68 | 'limit' => 50, 69 | ]); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests; 13 | 14 | use EasyDingTalk\Application; 15 | use GuzzleHttp\ClientInterface; 16 | use Mockery; 17 | use PHPUnit\Framework\TestCase as BaseTestCase; 18 | 19 | class TestCase extends BaseTestCase 20 | { 21 | /** 22 | * @param \EasyDingTalk\Kernel\BaseClient $client 23 | * 24 | * @return \EasyDingTalk\Kernel\BaseClient 25 | */ 26 | protected function make($client) 27 | { 28 | $app = $this->newApplication([ 29 | 'token' => 'test-token', 30 | 'aes_key' => 'test-aes-key', 31 | 'http' => ['response_type' => 'raw'], 32 | ]); 33 | 34 | $response = new TestResponse(200, [], '{"mock": "test"}'); 35 | 36 | $app['client']->setHttpClient(Mockery::mock(ClientInterface::class, function ($mock) use ($response) { 37 | $mock->shouldReceive('request')->withArgs($response->setExpectedArguments())->andReturn($response); 38 | })); 39 | 40 | return new $client($app); 41 | } 42 | 43 | /** 44 | * @param array $config 45 | * @param array $overrides 46 | * 47 | * @return \EasyDingTalk\Application 48 | */ 49 | protected function newApplication(array $config = [], array $overrides = []) 50 | { 51 | return new Application(array_merge(['appkey' => 'mock-appkey', 'appsecret' => 'mock-appsecret', 'agent_id' => 'mock-agent'], $config), $overrides); 52 | } 53 | 54 | protected function tearDown() 55 | { 56 | Mockery::close(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/TestResponse.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests; 13 | 14 | use GuzzleHttp\Psr7\Response; 15 | use PHPUnit\Framework\Assert; 16 | 17 | class TestResponse extends Response 18 | { 19 | /** 20 | * @var string 21 | */ 22 | protected $method; 23 | 24 | /** 25 | * @var string 26 | */ 27 | protected $uri; 28 | 29 | /** 30 | * @var array 31 | */ 32 | protected $options; 33 | 34 | /** 35 | * Request arguments. 36 | * 37 | * @return \Closure 38 | */ 39 | public function setExpectedArguments() 40 | { 41 | return function () { 42 | list($this->method, $this->uri, $this->options) = func_get_args(); 43 | 44 | return true; 45 | }; 46 | } 47 | 48 | /** 49 | * @throws \PHPUnit\Framework\ExpectationFailedException 50 | * 51 | * @param string $method 52 | * 53 | * @return $this 54 | */ 55 | public function assertMethod($method) 56 | { 57 | Assert::assertSame($this->method, $method); 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * @throws \PHPUnit\Framework\ExpectationFailedException 64 | * 65 | * @param string $uri 66 | * 67 | * @return $this 68 | */ 69 | public function assertUri($uri) 70 | { 71 | Assert::assertSame($this->uri, $uri); 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * @throws \PHPUnit\Framework\ExpectationFailedException 78 | * 79 | * @param string $uri 80 | * 81 | * @return $this 82 | */ 83 | public function assertGetUri($uri) 84 | { 85 | return $this->assertMethod('GET')->assertUri($uri); 86 | } 87 | 88 | /** 89 | * @throws \PHPUnit\Framework\ExpectationFailedException 90 | * 91 | * @param string $uri 92 | * 93 | * @return $this 94 | */ 95 | public function assertPostUri($uri) 96 | { 97 | return $this->assertMethod('POST')->assertUri($uri); 98 | } 99 | 100 | /** 101 | * @throws \PHPUnit\Framework\ExpectationFailedException 102 | * 103 | * @param array $query 104 | * 105 | * @return $this 106 | */ 107 | public function assertQuery($query) 108 | { 109 | Assert::assertSame($this->options['query'], $query); 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * @throws \PHPUnit\Framework\ExpectationFailedException 116 | * 117 | * @return $this 118 | */ 119 | public function assertEmptyQuery() 120 | { 121 | return $this->assertQuery([]); 122 | } 123 | 124 | /** 125 | * @throws \PHPUnit\Framework\ExpectationFailedException 126 | * 127 | * @param array $json 128 | * 129 | * @return $this 130 | */ 131 | public function assertPostJson($json) 132 | { 133 | Assert::assertSame($this->options['json'], $json); 134 | 135 | return $this; 136 | } 137 | 138 | /** 139 | * @param array $params 140 | * 141 | * @return $this 142 | */ 143 | public function assertPostFormParams($params) 144 | { 145 | Assert::assertSame($this->options['form_params'], $params); 146 | 147 | return $this; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /tests/User/ClientTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace EasyDingTalk\Tests\User; 13 | 14 | use EasyDingTalk\Tests\TestCase; 15 | use EasyDingTalk\User\Client; 16 | 17 | class ClientTest extends TestCase 18 | { 19 | /** @test */ 20 | public function get() 21 | { 22 | $this->make(Client::class)->get('mingyoung') 23 | ->assertUri('user/get')->assertQuery(['userid' => 'mingyoung', 'lang' => null]); 24 | 25 | $this->make(Client::class)->get('mingyoung', 'zh-CN') 26 | ->assertUri('user/get')->assertQuery(['userid' => 'mingyoung', 'lang' => 'zh-CN']); 27 | } 28 | 29 | /** @test */ 30 | public function getUserIds() 31 | { 32 | $this->make(Client::class)->getUserIds('123') 33 | ->assertUri('user/getDeptMember')->assertQuery(['deptId' => '123']); 34 | } 35 | 36 | /** @test */ 37 | public function getUsers() 38 | { 39 | $this->make(Client::class)->getUsers('123', 10, 20) 40 | ->assertUri('user/simplelist')->assertQuery(['department_id' => '123', 'offset' => 10, 'size' => 20, 'order' => null, 'lang' => null]); 41 | } 42 | 43 | /** @test */ 44 | public function getDetailedUsers() 45 | { 46 | $this->make(Client::class)->getDetailedUsers('123', 10, 20) 47 | ->assertUri('user/listbypage')->assertQuery(['department_id' => '123', 'offset' => 10, 'size' => 20, 'order' => null, 'lang' => null]); 48 | } 49 | 50 | /** @test */ 51 | public function administrators() 52 | { 53 | $this->make(Client::class)->administrators() 54 | ->assertUri('user/get_admin'); 55 | } 56 | 57 | /** @test */ 58 | public function administratorScope() 59 | { 60 | $this->make(Client::class)->administratorScope('mingyoung') 61 | ->assertUri('topapi/user/get_admin_scope')->assertQuery(['userid' => 'mingyoung']); 62 | } 63 | 64 | /** @test */ 65 | public function getUseridByUnionid() 66 | { 67 | $this->make(Client::class)->getUseridByUnionid('mingyoung') 68 | ->assertUri('user/getUseridByUnionid')->assertQuery(['unionid' => 'mingyoung']); 69 | } 70 | 71 | /** @test */ 72 | public function create() 73 | { 74 | $this->make(Client::class)->create(['userid' => 'mingyoung', 'name' => 'MINGYOUNG']) 75 | ->assertUri('user/create')->assertPostJson([ 76 | 'userid' => 'mingyoung', 'name' => 'MINGYOUNG', 77 | ]); 78 | } 79 | 80 | /** @test */ 81 | public function update() 82 | { 83 | $this->make(Client::class)->update('mingyoung', ['name' => 'MINGYOUNG']) 84 | ->assertUri('user/update')->assertPostJson([ 85 | 'userid' => 'mingyoung', 86 | 'name' => 'MINGYOUNG', 87 | ]); 88 | } 89 | 90 | /** @test */ 91 | public function delete() 92 | { 93 | $this->make(Client::class)->delete('mingyoung') 94 | ->assertUri('user/delete')->assertQuery(['userid' => 'mingyoung']); 95 | } 96 | 97 | /** @test */ 98 | public function getUserByCode() 99 | { 100 | $this->make(Client::class)->getUserByCode('code') 101 | ->assertUri('user/getuserinfo')->assertQuery(['code' => 'code']); 102 | } 103 | 104 | /** @test */ 105 | public function addRoles() 106 | { 107 | $this->make(Client::class)->addRoles('user1,user2', 'role1,role2') 108 | ->assertUri('topapi/role/addrolesforemps')->assertPostJson(['userIds' => 'user1,user2', 'roleIds' => 'role1,role2']); 109 | 110 | $this->make(Client::class)->addRoles(['user1', 'user2'], ['role1', 'role2']) 111 | ->assertUri('topapi/role/addrolesforemps')->assertPostJson(['userIds' => 'user1,user2', 'roleIds' => 'role1,role2']); 112 | } 113 | 114 | /** @test */ 115 | public function removeRoles() 116 | { 117 | $this->make(Client::class)->removeRoles('user1,user2', 'role1,role2') 118 | ->assertUri('topapi/role/removerolesforemps')->assertPostJson(['userIds' => 'user1,user2', 'roleIds' => 'role1,role2']); 119 | 120 | $this->make(Client::class)->removeRoles(['user1', 'user2'], ['role1', 'role2']) 121 | ->assertUri('topapi/role/removerolesforemps')->assertPostJson(['userIds' => 'user1,user2', 'roleIds' => 'role1,role2']); 122 | } 123 | 124 | /** @test */ 125 | public function getUserCount() 126 | { 127 | $this->make(Client::class)->getCount() 128 | ->assertUri('user/get_org_user_count')->assertQuery(['onlyActive' => 0]); 129 | } 130 | 131 | /** @test */ 132 | public function getActivatedCount() 133 | { 134 | $this->make(Client::class)->getActivatedCount() 135 | ->assertUri('user/get_org_user_count')->assertQuery(['onlyActive' => 1]); 136 | } 137 | 138 | /** @test */ 139 | public function getUserIdByPhone() 140 | { 141 | $this->make(Client::class)->getUserIdByPhone('18888888888') 142 | ->assertUri('user/get_by_mobile')->assertQuery(['mobile' => '18888888888']); 143 | } 144 | 145 | /** @test */ 146 | public function getInactiveUsers() 147 | { 148 | $this->make(Client::class)->getInactiveUsers('20190808',0,100) 149 | ->assertUri('topapi/inactive/user/get')->assertPostJson(['query_date' => '20190808', 'offset' => 0, 'size' => 100]); 150 | } 151 | } 152 | --------------------------------------------------------------------------------