CloudPrinter.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <?php
  2. namespace common\components;
  3. /**
  4. * 链科云打印 v3 API 客户端(参考 cloud_printer_demo/php)
  5. */
  6. class CloudPrinterApiException extends \Exception
  7. {
  8. }
  9. class CloudPrinterHttpException extends \Exception
  10. {
  11. }
  12. class CloudPrinter
  13. {
  14. /** @var string */
  15. public $apiKey = '';
  16. /** @var string */
  17. public $server = 'https://cloud.liankenet.com/';
  18. /** @var int */
  19. public $timeout = 10;
  20. /** @var bool */
  21. public $debug = false;
  22. /** @var string */
  23. public $deviceId = '';
  24. /** @var string */
  25. public $deviceKey = '';
  26. public function __construct($apiKey, $deviceId, $deviceKey, $debug = false, $timeout = 10, $server = '')
  27. {
  28. $this->apiKey = (string)$apiKey;
  29. $this->deviceId = (string)$deviceId;
  30. $this->deviceKey = (string)$deviceKey;
  31. $this->debug = (bool)$debug;
  32. $this->timeout = max(1, intval($timeout));
  33. if (!empty($server)) {
  34. $this->server = rtrim((string)$server, '/') . '/';
  35. }
  36. }
  37. /**
  38. * 统一 HTTP 请求
  39. */
  40. private function requests($method, $endpoint, $fields = [], $contentType = 'application/json')
  41. {
  42. $curl = curl_init();
  43. $headers = ['ApiKey: ' . $this->apiKey];
  44. $postFields = $fields;
  45. if ($method === 'POST') {
  46. if ($contentType === 'application/json') {
  47. $postFields = json_encode($fields, JSON_UNESCAPED_UNICODE);
  48. $headers[] = 'Content-Type: application/json';
  49. }
  50. // multipart/form-data 由 curl 自动带 boundary,不能手动设 Content-Type
  51. }
  52. curl_setopt_array($curl, [
  53. CURLOPT_URL => $this->server . ltrim($endpoint, '/'),
  54. CURLOPT_RETURNTRANSFER => true,
  55. CURLOPT_MAXREDIRS => 1,
  56. CURLOPT_TIMEOUT => $this->timeout,
  57. CURLOPT_FOLLOWLOCATION => true,
  58. CURLINFO_HEADER_OUT => $this->debug,
  59. CURLOPT_VERBOSE => $this->debug,
  60. CURLOPT_CUSTOMREQUEST => $method,
  61. CURLOPT_POSTFIELDS => $postFields,
  62. CURLOPT_HTTPHEADER => $headers,
  63. ]);
  64. $response = curl_exec($curl);
  65. $info = curl_getinfo($curl);
  66. $errno = curl_errno($curl);
  67. $error = curl_error($curl);
  68. curl_close($curl);
  69. if ($this->debug) {
  70. \Yii::info('链科云打印请求: ' . json_encode($info, JSON_UNESCAPED_UNICODE), __METHOD__);
  71. }
  72. if ($response === false) {
  73. throw new CloudPrinterHttpException($error ?: 'curl 请求失败', $errno);
  74. }
  75. if (intval($info['http_code'] ?? 0) !== 200) {
  76. throw new CloudPrinterHttpException('HTTP ' . ($info['http_code'] ?? 0), intval($info['http_code'] ?? 0));
  77. }
  78. $data = json_decode($response);
  79. if (!is_object($data)) {
  80. throw new CloudPrinterApiException('接口返回格式错误');
  81. }
  82. if (intval($data->code ?? 0) !== 200) {
  83. throw new CloudPrinterApiException($data->msg ?? '链科云打印接口错误', intval($data->code ?? 0));
  84. }
  85. return $data;
  86. }
  87. /** 获取设备信息 */
  88. public function getDeviceInfo()
  89. {
  90. $data = [
  91. 'deviceId' => $this->deviceId,
  92. 'deviceKey' => $this->deviceKey,
  93. ];
  94. $response = $this->requests('GET', 'api/device/device_info?' . http_build_query($data));
  95. return $response->data;
  96. }
  97. /** 异步刷新设备信息(含打印机列表) */
  98. public function asyncRefreshDeviceInfo()
  99. {
  100. $data = [
  101. 'deviceId' => $this->deviceId,
  102. 'deviceKey' => $this->deviceKey,
  103. ];
  104. return $this->requests('GET', 'api/device/async_refresh_device_info?' . http_build_query($data));
  105. }
  106. /** 获取打印机列表 */
  107. public function getPrinterList()
  108. {
  109. $data = [
  110. 'deviceId' => $this->deviceId,
  111. 'deviceKey' => $this->deviceKey,
  112. ];
  113. $response = $this->requests('GET', 'api/external_api/printer_list?' . http_build_query($data));
  114. return $response->data->row ?? [];
  115. }
  116. /** 获取打印机参数 */
  117. public function getPrinterParams($printerModel)
  118. {
  119. $data = [
  120. 'deviceId' => $this->deviceId,
  121. 'deviceKey' => $this->deviceKey,
  122. 'printerModel' => $printerModel,
  123. ];
  124. $response = $this->requests('GET', 'api/print/printer_params?' . http_build_query($data));
  125. return $response->data;
  126. }
  127. /**
  128. * 发起打印任务(文件上传)
  129. *
  130. * @param string $devicePort USB 口
  131. * @param string $printerModel 驱动名
  132. * @param int $paperSize 纸张代码,A4 一般为 9
  133. * @param \CURLFile $file 待打印文件
  134. * @param array $optionalArray 额外参数(份数、颜色等)
  135. */
  136. public function addJob($devicePort, $printerModel, $paperSize, $file, $optionalArray = [])
  137. {
  138. $data = array_merge([
  139. 'deviceId' => $this->deviceId,
  140. 'deviceKey' => $this->deviceKey,
  141. 'devicePort' => $devicePort,
  142. 'printerModel' => $printerModel,
  143. 'dmPaperSize' => $paperSize,
  144. 'jobFile' => $file,
  145. ], $optionalArray);
  146. $response = $this->requests('POST', 'api/print/job', $data, 'multipart/form-data');
  147. \Yii::info("发起打印任务",json_encode($response->data));
  148. return $response->data;
  149. }
  150. /**
  151. * 通过本地文件路径发起打印
  152. */
  153. public function addJobByPath($devicePort, $printerModel, $paperSize, $filePath, $optionalArray = [])
  154. {
  155. $realPath = realpath($filePath);
  156. if ($realPath === false || !is_file($realPath)) {
  157. throw new CloudPrinterApiException('打印文件不存在');
  158. }
  159. $mime = function_exists('mime_content_type') ? mime_content_type($realPath) : '';
  160. if (empty($mime)) {
  161. $mime = 'application/octet-stream';
  162. }
  163. $file = new \CURLFile($realPath, $mime, basename($realPath));
  164. return $this->addJob($devicePort, $printerModel, $paperSize, $file, $optionalArray);
  165. }
  166. /** 查询任务状态 */
  167. public function getJobStatus($devicePort, $taskId)
  168. {
  169. $data = [
  170. 'deviceId' => $this->deviceId,
  171. 'deviceKey' => $this->deviceKey,
  172. 'devicePort' => $devicePort,
  173. 'task_id' => $taskId,
  174. ];
  175. $response = $this->requests('GET', 'api/print/job?' . http_build_query($data));
  176. return $response->data;
  177. }
  178. /** 取消任务 */
  179. public function cancelJob($devicePort, $taskId)
  180. {
  181. $data = [
  182. 'deviceId' => $this->deviceId,
  183. 'deviceKey' => $this->deviceKey,
  184. 'devicePort' => $devicePort,
  185. 'task_id' => $taskId,
  186. ];
  187. return $this->requests('DELETE', 'api/print/job?' . http_build_query($data));
  188. }
  189. }