Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
srako committed Apr 20, 2023
0 parents commit 0b4c64e
Show file tree
Hide file tree
Showing 16 changed files with 709 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/vendor
composer.lock
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 mradang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Webman Dingtalk

封装钉钉接口,处理钉钉事件订阅,触发事件

## 安装

```
composer require srako/wenman-dingtalk
```

## 配置

1. 添加 .env 环境变量

```
DING_CORP_ID=dingxxxxxxx
DING_AGENT_ID=xxxxxxxx
DING_CLIENT_ID=xxxxxxxx
DING_CLIENT_SECRET=xxxxxxxx
DING_AES_KEY=xxxxxxxx
DING_TOKEN=xxxxxxxx
```


## 添加的命令

1. 刷新部门和用户(触发变更事件)

```bash
php webman dingtalk:RefreshDepartmentsAndUsers
```

## 钉钉接口调用示例

### 发送工作通知消息

```
请求方式:POST(HTTPS)
请求地址:https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=ACCESS_TOKEN
```

```php
$params = [
'agent_id' => env('DINGTALK_AGENTID'),
'userid_list' => '0841582759859766',
'msg' => [
'msgtype' => 'text',
'text' => [
'content' => '当前时间:'.date('Y-m-d H:i:s'),
],
],
];

$ret = DingTalk::post('/topapi/message/corpconversation/asyncsend_v2', $params);
```
25 changes: 25 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "srako/webman-dingtalk",
"description": "webman-dingtalk",
"license": "MIT",
"authors": [
{
"name": "srako",
"email": "[email protected]"
}
],
"require": {
"php": "^8.0",
"illuminate/support": "^10.0",
"webman/console": "^1.2",
"workerman/webman-framework": "^1.5",
"ext-openssl": "*",
"guzzlehttp/guzzle": "^7.5",
"playcat/queue": "^2.1"
},
"autoload": {
"psr-4": {
"Webman\\DingTalk\\": "src"
}
}
}
39 changes: 39 additions & 0 deletions src/Console/RefreshDepartmentsAndUsersCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Webman\DingTalk\Console;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Webman\DingTalk\DingMessage;
use Webman\DingTalk\Services\DingTalkService;

class RefreshDepartmentsAndUsersCommand extends Command
{
protected static $defaultName = 'dingtalk:RefreshDepartmentsAndUsers';
protected static $defaultDescription = 'Refresh departments and users';

protected function execute(InputInterface $input, OutputInterface $output)
{

$dept_ids = DingTalkService::departmentIds();
DingMessage::dispatch([
'CorpId' => config('plugin.srako.dingtalk.app.corpid'),
'EventType' => 'org_dept_modify',
'DeptId' => $dept_ids
]);
foreach ($dept_ids as $dept_id) {
$user_ids = DingTalkService::getDeptUserIds($dept_id);
if (blank($user_ids)) {
continue;
}
DingMessage::dispatch([
'CorpId' => config('plugin.srako.dingtalk.app.corpid'),
'EventType' => 'user_modify_org',
'UserId' => $user_ids
]);
}

return 0;
}
}
32 changes: 32 additions & 0 deletions src/Controllers/DingTalkController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Webman\DingTalk\Controllers;

use support\Request;
use Webman\DingTalk\DingMessage;
use Webman\DingTalk\Services\CryptoService;

class DingTalkController
{

public function callback(Request $request)
{
try {
$text = CryptoService::decryptMsg(
$request->signature,
$request->timestamp,
$request->nonce,
$request->encrypt
);

DingMessage::dispatch(json_decode($text, true));

// 为钉钉服务器返回成功状态
return CryptoService::encryptMsg('success', $request->timestamp, $request->nonce);
} catch (\Exception $e) {
log('钉钉回调消息处理失败:' . $e->getMessage());
}
return CryptoService::encryptMsg('fail', $request->timestamp, $request->nonce);

}
}
27 changes: 27 additions & 0 deletions src/DingMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
/**
*
* @author srako
* @date 2023/4/19 15:56
* @page http://srako.github.io
*/

namespace Webman\DingTalk;

use Playcat\Queue\Manager;
use Playcat\Queue\Protocols\ProducerData;

class DingMessage extends ProducerData
{
protected $channel = 'ding-message';

public function __construct(array $message)
{
$this->setQueueData($message);
}

public static function dispatch(array $message): ?string
{
return Manager::getInstance()->push(new self($message));
}
}
20 changes: 20 additions & 0 deletions src/DingTalk.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Webman\DingTalk;

use support\Container;

/**
* Class Factory
* @method static request(string $url, string $method, array $options = [], bool $withToken = true) 发送任意请求至钉钉
* @method static get(string $api, array $params = []) 发送get请求至钉钉
* @method static post(string $api, array $params = []) 发送post请求至钉钉
* @package Webman\DingTalk
*/
class DingTalk
{
public static function __callStatic($name, $arguments)
{
return Container::get(DingTalkManager::class)->{$name}(...$arguments);
}
}
90 changes: 90 additions & 0 deletions src/DingTalkManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

namespace Webman\DingTalk;

use GuzzleHttp\Client;
use stdClass;
use support\Cache;
use support\Log;
use Webman\DingTalk\Exceptions\RequestException;

class DingTalkManager
{
protected array $config;

protected string $baseUrl = 'https://oapi.dingtalk.com';

public function __construct(array $config = [])
{
$this->config = $config ?: config('plugin.srako.dingtalk.app');
}

public function getAccessToken(): ?string
{
$key = 'dingtalk.' . $this->config['corpid'];
$token = Cache::get($key);
if ($token) {
return $token;
}
$res = $this->request('/gettoken', 'get', [
'query' => [
'appkey' => $this->config['appkey'],
'appsecret' => $this->config['appsecret'],
]
], false);
if (is_null($res) || $res->errcode !== 0) {
return null;
}
Cache::set($key, $res->access_token, $res->expires_in - 60);
return $res->access_token;
}

private function request(string $url, string $method, array $params = [], bool $withToken = true)
{
if ($withToken) {
$params['query']['access_token'] = $this->getAccessToken();
}
$client = new Client(['base_uri' => $this->baseUrl]);
$response = $client->request($method, $url, $params);
if ($response->getStatusCode() === 200) {
$string = $response->getBody()->getContents();
Log::info("[webman-dingtalk][$method][$this->baseUrl][$url]" . $string);
return json_decode($string) ?: $string;
}
Log::error("[webman-dingtalk][$method][$this->baseUrl][$url]" . $response->getBody());
throw new RequestException($response->getBody());
}

/**
* 发送get请求至钉钉
* @param string $api
* @param array $params
* @return stdClass
*/
public function get(string $api, array $params = []): stdClass
{
$res = $this->request($api, 'get', ['query' => $params]);
if (is_string($res) || $res->errcode !== 0) {
throw new RequestException($res);
}
return $res;
}

/**
* 发送post请求至钉钉
* @param string $api
* @param array $params
* @return stdClass
*/
public function post(string $api, array $params = []): stdClass
{
$res = $this->request($api, 'post', [
'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
'form_params' => $params
]);
if (is_string($res) || $res->errcode !== 0) {
throw new RequestException($res);
}
return $res;
}
}
29 changes: 29 additions & 0 deletions src/Exceptions/RequestException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
/**
*
*/

namespace Webman\DingTalk\Exceptions;


use stdClass;

class RequestException extends \Exception
{
public int $status = 500;
public int $errorCode = 0;
public string $errorMessage;


public function __construct(string|stdClass $response)
{
if ($response instanceof stdClass) {
$this->errorCode = $response->errcode;
$this->errorMessage = $response->errmsg;
} else {
$this->errorMessage = $response;
}

parent::__construct('钉钉接口未正确响应', 500);
}
}
Loading

0 comments on commit 0b4c64e

Please sign in to comment.