2
2
3
3
namespace Calchen \LaravelDingtalkRobot ;
4
4
5
- use Calchen \LaravelDingtalkRobot \Exception \Exception ;
6
- use Calchen \LaravelDingtalkRobot \Exception \HttpException ;
7
- use Calchen \LaravelDingtalkRobot \Exception \InvalidConfigurationException ;
8
5
use Calchen \LaravelDingtalkRobot \Exceptions \ErrorCodes ;
6
+ use Calchen \LaravelDingtalkRobot \Exceptions \Exception ;
7
+ use Calchen \LaravelDingtalkRobot \Exceptions \HttpException ;
8
+ use Calchen \LaravelDingtalkRobot \Exceptions \InvalidConfigurationException ;
9
9
use Calchen \LaravelDingtalkRobot \Message \Message ;
10
- use GuzzleHttp \Client ;
10
+ use GuzzleHttp \Client as GuzzleClient ;
11
+ use GuzzleHttp \ClientInterface ;
12
+ use Illuminate \Support \Facades \App ;
13
+ use Psr \Http \Message \ResponseInterface ;
11
14
12
15
/**
13
16
* 钉钉群消息机器 API.
16
19
*/
17
20
class DingtalkRobot
18
21
{
22
+ /**
23
+ * @var GuzzleClient
24
+ */
25
+ protected static $ httpClient ;
26
+
19
27
/**
20
28
* 允许的安全设置
21
29
*/
@@ -26,6 +34,9 @@ class DingtalkRobot
26
34
'ip ' , // IP地址(段)
27
35
];
28
36
37
+ /**
38
+ * @var array
39
+ */
29
40
protected $ config ;
30
41
/**
31
42
* @var string
@@ -40,11 +51,11 @@ class DingtalkRobot
40
51
protected $ message = null ;
41
52
42
53
/**
43
- * Dingtalk constructor.
54
+ * DingtalkRobot constructor.
44
55
*/
45
56
public function __construct ()
46
57
{
47
- $ this -> robot ();
58
+ //
48
59
}
49
60
50
61
/**
@@ -53,12 +64,17 @@ public function __construct()
53
64
* @param string $name
54
65
*
55
66
* @return $this
56
- * @throws \ Exception
67
+ * @throws Exception
57
68
*/
58
69
public function robot ($ name = 'default ' ): self
59
70
{
60
71
$ configs = config ('dingtalk_robot ' );
61
72
73
+ // http_client_name 只能是 string
74
+ if (isset ($ configs ['http_client_name ' ]) && !is_string ($ configs ['http_client_name ' ])) {
75
+ throw new InvalidConfigurationException (null , ErrorCodes::HTTP_CLIENT_NAME_INVALID );
76
+ }
77
+
62
78
// name 必须存在
63
79
if (!isset ($ configs [$ name ])) {
64
80
$ message = __ (ErrorCodes::MESSAGES [ErrorCodes::INVALID_ROBOT_NAME ], [
@@ -110,7 +126,7 @@ public function robot($name = 'default'): self
110
126
* @param Message $message
111
127
*
112
128
* @return $this
113
- * @throws \ Exception
129
+ * @throws Exception
114
130
*/
115
131
public function setMessage (Message $ message ): self
116
132
{
@@ -130,50 +146,95 @@ public function getMessage(): array
130
146
return $ this ->message ->getMessage ();
131
147
}
132
148
149
+ /**
150
+ * 获取 http 客户端
151
+ * 如果容器已经注入了可记录日志的 guzzle 优先使用
152
+ *
153
+ * @return ClientInterface
154
+ */
155
+ private function getHttpClient (): ClientInterface
156
+ {
157
+ if (!(self ::$ httpClient instanceof ClientInterface)) {
158
+ $ configs = config ('dingtalk_robot ' );
159
+ if (isset ($ configs ['http_client_name ' ]) && class_exists ($ configs ['http_client_name ' ])) {
160
+ self ::$ httpClient = App::make ($ configs ['http_client_name ' ]);
161
+ }
162
+
163
+ if (!(self ::$ httpClient instanceof ClientInterface)) {
164
+ self ::$ httpClient = new GuzzleClient ([
165
+ 'timeout ' => $ this ->config ['timeout ' ] ?? 2.0 ,
166
+ ]);
167
+ }
168
+ }
169
+
170
+ return self ::$ httpClient ;
171
+ }
172
+
133
173
/**
134
174
* 发起请求,返回的内容与直接调用钉钉接口返回的内容一致.
135
175
*
136
- * @return bool|string
176
+ * @return array
137
177
* @throws Exception
138
178
*/
139
- public function send (): string
179
+ public function send (): array
140
180
{
141
181
if (is_null ($ this ->message )) {
142
182
throw new InvalidConfigurationException ('Please set message object ' );
143
183
}
144
184
145
- $ client = new Client ([
146
- 'timeout ' => $ this ->config ['timeout ' ] ?? 2.0 ,
147
- ]);
148
-
149
185
$ query = [
150
- 'access_token ' => $ this ->config ['access_token ' ]
186
+ 'access_token ' => $ this ->config ['access_token ' ],
151
187
];
152
188
153
189
// 在请求接口前根据安全设置进行处理
154
190
$ securityType = $ this ->config ['security_type ' ];
155
191
$ securityValues = $ this ->config ['security_values ' ];
192
+ // 签名的安全设置
156
193
if ($ securityType == self ::SECURITY_TYPES [2 ]) {
157
- $ query ['timestamp ' ] = time ();
194
+ // 这里出文档要求:当前时间戳,单位是毫秒,与请求调用时间误差不能超过1小时
195
+ // 故此简单乘以1000,没有用 microtime
196
+ $ query ['timestamp ' ] = time () * 1000 ;
158
197
$ strToSign = $ query ['timestamp ' ]."\n" .$ securityValues ;
159
198
$ query ['sign ' ] = base64_encode (hash_hmac ('sha256 ' , $ strToSign , $ securityValues , true ));
160
199
}
161
200
162
- try {
163
- $ response = $ client ->post (
164
- $ this ->robotUrl ,
165
- [
166
- 'json ' => $ this ->message ->getMessage (),
167
- 'headers ' => [
168
- 'Content-Type ' => 'application/json ' ,
169
- ],
170
- 'query ' => $ query ,
171
- ]
172
- );
173
-
174
- return $ response ->getBody ()->getContents ();
175
- } catch (Exception $ e ) {
176
- throw new HttpException ($ e ->getMessage (), $ e ->getCode (), $ e );
201
+ /** @var ResponseInterface $response */
202
+ $ response = $ this ->getHttpClient ()->post (
203
+ $ this ->robotUrl ,
204
+ [
205
+ 'json ' => $ this ->message ->getMessage (),
206
+ 'headers ' => [
207
+ 'Content-Type ' => 'application/json ' ,
208
+ ],
209
+ 'query ' => $ query ,
210
+ ]
211
+ );
212
+
213
+ $ result = $ response ->getBody ()->getContents ();
214
+ if ($ response ->getStatusCode () !== 200 ) {
215
+ throw new HttpException ($ result , ErrorCodes::RESPONSE_FAILED );
216
+ }
217
+ $ result = json_decode ($ result , true );
218
+ if (is_null ($ result )) {
219
+ throw new Exception ($ result , ErrorCodes::RESPONSE_BODY_ERROR );
220
+ }
221
+
222
+ if (isset ($ result ['errcode ' ]) && $ result ['errcode ' ] != 0 ) {
223
+ // 单独处理安全设置失败
224
+ if ($ result ['errcode ' ] === 310000 ) {
225
+ $ message = __ (ErrorCodes::MESSAGES [ErrorCodes::SECURITY_VERIFICATION_FAILED ], [
226
+ 'message ' => $ result ['errmsg ' ],
227
+ ]);
228
+ throw new Exception ($ message , ErrorCodes::SECURITY_VERIFICATION_FAILED );
229
+ } else {
230
+ $ message = __ (ErrorCodes::MESSAGES [ErrorCodes::RESPONSE_RESULT_UNKNOWN_ERROR ], [
231
+ 'code ' => $ result ['errcode ' ],
232
+ 'message ' => $ result ['errmsg ' ],
233
+ ]);
234
+ throw new Exception ($ message , ErrorCodes::RESPONSE_RESULT_UNKNOWN_ERROR );
235
+ }
177
236
}
237
+
238
+ return $ result ;
178
239
}
179
240
}
0 commit comments