FeiEYunService.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. <?php
  2. namespace services\extend\printer;
  3. use common\components\Service;
  4. use common\helpers\StringHelper;
  5. use common\models\extend\printer\FeiE;
  6. use linslin\yii2\curl\Curl;
  7. use Yii;
  8. use yii\helpers\Json;
  9. use yii\web\UnprocessableEntityHttpException;
  10. use function services\extend\iconv;
  11. use function services\extend\mb_strlen;
  12. use function services\extend\mb_strwidth;
  13. use function services\extend\mb_substr;
  14. /**
  15. * 飞鹅云打印机
  16. *
  17. * Class FeiEYunService
  18. * @package services\extend
  19. * @author jianyan74 <751393839@qq.com>
  20. */
  21. class FeiEYunService extends Service
  22. {
  23. const IP = 'https://api.feieyun.cn'; //接口IP或域名
  24. const PORT = 80; // 接口IP端口
  25. const PATH = '/Api/Open/'; //接口路径
  26. protected $user;
  27. protected $uKey;
  28. protected $sn;
  29. protected $printNum;
  30. /**
  31. * @param FeiE $config
  32. */
  33. public function initConfig(FeiE $config)
  34. {
  35. $this->user = $config->user;
  36. $this->uKey = $config->ukey;
  37. $this->sn = $config->sn;
  38. $this->printNum = $config->print_num;
  39. parent::init();
  40. }
  41. /**
  42. * @param $content
  43. * @param int $times
  44. * @param FeiE|array $config
  45. */
  46. public function print($content, $config = [])
  47. {
  48. if (!empty($config)) {
  49. $this->initConfig($config);
  50. }
  51. // 格式化内容
  52. $content = $this->formattedContent($content);
  53. $time = time(); // 请求时间
  54. $msgInfo = [
  55. 'user' => $this->user,
  56. 'stime' => $time,
  57. 'sig' => $this->signature($time),
  58. 'apiname' => 'Open_printMsg',
  59. 'sn' => $this->sn,
  60. 'content' => $content,
  61. 'times' => $this->printNum // 打印次数
  62. ];
  63. $curl = new Curl();
  64. $result = $curl->setHeaders([
  65. 'Content-Type' => 'application/x-www-form-urlencoded',
  66. ])->setPostParams($msgInfo)->post(self::IP . self::PATH);
  67. $result = Json::decode($result);
  68. if ($result['ret'] != 0) {
  69. throw new UnprocessableEntityHttpException($result['msg']);
  70. }
  71. return $result['data'];
  72. }
  73. /**
  74. * @param $data
  75. * @return string
  76. * @throws \yii\base\InvalidConfigException
  77. */
  78. protected function formattedContent($data)
  79. {
  80. if (!is_array($data)) {
  81. return $data;
  82. }
  83. $content = "<BOLD><C>****** {$data['title']} ******</C></BOLD>" . "<BR>";
  84. $content .= str_repeat('.', 32) . "<BR>";
  85. $content .= "<BOLD><C>---{$data['payType']}---</C></BOLD>" . "<BR>";
  86. // $content .= "<C>{$data['merchantTitle']}</C>";
  87. $content .= "打印时间:" . Yii::$app->formatter->asDatetime(time()) . "<BR>";
  88. $content .= "下单时间:" . $data['orderTime'] . "<BR>";
  89. $content .= str_repeat('*', 13) . " 商品 " . str_repeat("*", 13) . "<BR>";
  90. $content .= $this->composing($data['products']);
  91. $content .= "商品总价:¥{$data['productOriginalMoney']}<BR>";
  92. foreach ($data['marketingDetails'] as $marketingDetail) {
  93. $content .= "{$marketingDetail['marketing_name']}:-¥{$marketingDetail['discount_money']}<BR>";
  94. }
  95. $data['pointMoney'] > 0 && $content .= "积分抵扣:-¥{$data['pointMoney']}<BR>";
  96. $data['taxMoney'] > 0 && $content .= "发票税额:-¥{$data['taxMoney']}<BR>";
  97. $content .= "配送费:¥{$data['shippingMoney']}<BR>";
  98. $content .= "应付金额:¥{$data['payMoney']}<BR>";
  99. $content .= str_repeat('.', 32) . "<BR>";
  100. $content .= "昵称: " . $data['nickname'] . "<BR>";
  101. isset($data['receiverName']) && $content .= "客户: " . $data['receiverName'] . "<BR>";
  102. isset($data['receiverMobile']) && $content .= "电话: " . StringHelper::hideStr($data['receiverMobile'],
  103. 3) . "<BR>";
  104. isset($data['receiverAddress']) && $content .= "地址: " . $data['receiverRegionName'] . $data['receiverAddress'] . "<BR>";
  105. isset($data['buyerMessage']) && $content .= "备注: " . !empty($data['buyerMessage']) ? $data['buyerMessage'] : '无' . "<BR>";
  106. if (!empty($data['qr'])) {
  107. $content .= str_repeat('.', 32) . "<BR>";
  108. $content .= "<C>二维码</C>" . "<BR>";
  109. $content .= "<QR>{$data['qr']}</QR>" . "<BR>";
  110. } else {
  111. $content .= str_repeat('.', 32) . "<BR>";
  112. $content .= "<C>二维码</C>" . "<BR>";
  113. $content .= "<QR>{$data['orderSn']}</QR>" . "<BR>";
  114. }
  115. $content .= str_repeat('.', 32) . "<BR>";
  116. $content .= "<C>订单号</C>" . "<BR>";
  117. $content .= $this->barCode($data['orderSn']) . "<BR>";
  118. $content .= "<BOLD><C>****** 完 ******</C></BOLD>" . "<BR>";
  119. return $content;
  120. }
  121. /**
  122. *
  123. * @param $products
  124. * @param int $A 名称
  125. * @param int $B 单价
  126. * @param int $C 数量
  127. * @param int $D 金额
  128. * @return string
  129. */
  130. protected function composing($products, $A = 14, $B = 6, $C = 3, $D = 6)
  131. {
  132. $orderInfo = '商品名称      数量 金额<BR>';
  133. $orderInfo .= '--------------------------------<BR>';
  134. foreach ($products as $k5 => $v5) {
  135. $name = $v5['title'];
  136. // $price = $v5['price'];
  137. $price = '';
  138. $num = $v5['num'];
  139. $prices = $v5['price'];
  140. $kw3 = '';
  141. $kw1 = '';
  142. $kw2 = '';
  143. $kw4 = '';
  144. $str = $name;
  145. $blankNum = $A;//名称控制为14个字节
  146. $lan = mb_strlen($str, 'utf-8');
  147. $m = 0;
  148. $j = 1;
  149. $blankNum++;
  150. $result = array();
  151. if (strlen($price) < $B) {
  152. $k1 = $B - strlen($price);
  153. for ($q = 0; $q < $k1; $q++) {
  154. $kw1 .= ' ';
  155. }
  156. $price = $price . $kw1;
  157. }
  158. if (strlen($num) < $C) {
  159. $k2 = $C - strlen($num);
  160. for ($q = 0; $q < $k2; $q++) {
  161. $kw2 .= ' ';
  162. }
  163. $num = $num . $kw2;
  164. }
  165. if (strlen($prices) < $D) {
  166. $k3 = $D - strlen($prices);
  167. for ($q = 0; $q < $k3; $q++) {
  168. $kw4 .= ' ';
  169. }
  170. $prices = $prices . $kw4;
  171. }
  172. for ($i = 0; $i < $lan; $i++) {
  173. $new = mb_substr($str, $m, $j, 'utf-8');
  174. $j++;
  175. if (mb_strwidth($new, 'utf-8') < $blankNum) {
  176. if ($m + $j > $lan) {
  177. $m = $m + $j;
  178. $tail = $new;
  179. $lenght = iconv("UTF-8", "GBK//IGNORE", $new);
  180. $k = $A - strlen($lenght);
  181. for ($q = 0; $q < $k; $q++) {
  182. $kw3 .= ' ';
  183. }
  184. if ($m == $j) {
  185. $tail .= $kw3 . ' ' . $price . ' ' . $num . ' ' . $prices;
  186. } else {
  187. $tail .= $kw3 . '<BR>';
  188. }
  189. break;
  190. } else {
  191. $next_new = mb_substr($str, $m, $j, 'utf-8');
  192. if (mb_strwidth($next_new, 'utf-8') < $blankNum) {
  193. continue;
  194. } else {
  195. $m = $i + 1;
  196. $result[] = $new;
  197. $j = 1;
  198. }
  199. }
  200. }
  201. }
  202. $head = '';
  203. foreach ($result as $key => $value) {
  204. if ($key < 1) {
  205. $v_lenght = iconv("UTF-8", "GBK//IGNORE", $value);
  206. $v_lenght = strlen($v_lenght);
  207. if ($v_lenght == 13) {
  208. $value = $value . " ";
  209. }
  210. $head .= $value . ' ' . $price . ' ' . $num . ' ' . $prices;
  211. } else {
  212. $head .= $value . '<BR>';
  213. }
  214. }
  215. $orderInfo .= $head . $tail;
  216. }
  217. $orderInfo .= '--------------------------------<BR>';
  218. return $orderInfo;
  219. }
  220. /**
  221. * 飞鹅技术支持-2020-03-25
  222. * #########################################################################################################
  223. * 一,纯数字条件下:
  224. * 58mm打印机最大支持28位纯数字,80mm打印机最大支持46位纯数字,超出无效
  225. *
  226. * 26-28位数字条形码,在数字中不可以出现2个及以上连续的0存在
  227. * 23-25位数字条形码,在数字中不可以出现3个及以上连续的0存在
  228. * 21-22位数字条形码,在数字中不可以出现4个及以上连续的0存在
  229. * 19-20位数字条形码,在数字中不可以出现6个及以上连续的0存在
  230. * 17-18位数字条形码,在数字中不可以出现8个及以上连续的0存在
  231. * 15-16位数字条形码,在数字中不可以出现10个及以上连续的0存在
  232. * 少于或等于14位数字的条形码,0的数量没有影响
  233. * #########################################################################################################
  234. * 二,非纯数字混合条件下:
  235. * 58mm打印机最大支持14位字符,80mm打印机最大支持23位字符,超出无效
  236. *
  237. * 支持数字,大小写字母,特殊字符例如: !@#$%^&*()-=+_
  238. * #########################################################################################################
  239. */
  240. protected function barCode($strnum)
  241. {
  242. $chr = '';
  243. $codeB = ["\x30", "\x31", "\x32", "\x33", "\x34", "\x35", "\x36", "\x37", "\x38", "\x39"];//匹配字符集B
  244. $codeC = [
  245. "\x00",
  246. "\x01",
  247. "\x02",
  248. "\x03",
  249. "\x04",
  250. "\x05",
  251. "\x06",
  252. "\x07",
  253. "\x08",
  254. "\x09",
  255. "\x0A",
  256. "\x0B",
  257. "\x0C",
  258. "\x0D",
  259. "\x0E",
  260. "\x0F",
  261. "\x10",
  262. "\x11",
  263. "\x12",
  264. "\x13",
  265. "\x14",
  266. "\x15",
  267. "\x16",
  268. "\x17",
  269. "\x18",
  270. "\x19",
  271. "\x1A",
  272. "\x1B",
  273. "\x1C",
  274. "\x1D",
  275. "\x1E",
  276. "\x1F",
  277. "\x20",
  278. "\x21",
  279. "\x22",
  280. "\x23",
  281. "\x24",
  282. "\x25",
  283. "\x26",
  284. "\x27",
  285. "\x28",
  286. "\x29",
  287. "\x2A",
  288. "\x2B",
  289. "\x2C",
  290. "\x2D",
  291. "\x2E",
  292. "\x2F",
  293. "\x30",
  294. "\x31",
  295. "\x32",
  296. "\x33",
  297. "\x34",
  298. "\x35",
  299. "\x36",
  300. "\x37",
  301. "\x38",
  302. "\x39",
  303. "\x3A",
  304. "\x3B",
  305. "\x3C",
  306. "\x3D",
  307. "\x3E",
  308. "\x3F",
  309. "\x40",
  310. "\x41",
  311. "\x42",
  312. "\x43",
  313. "\x44",
  314. "\x45",
  315. "\x46",
  316. "\x47",
  317. "\x48",
  318. "\x49",
  319. "\x4A",
  320. "\x4B",
  321. "\x4C",
  322. "\x4D",
  323. "\x4E",
  324. "\x4F",
  325. "\x50",
  326. "\x51",
  327. "\x52",
  328. "\x53",
  329. "\x54",
  330. "\x55",
  331. "\x56",
  332. "\x57",
  333. "\x58",
  334. "\x59",
  335. "\x5A",
  336. "\x5B",
  337. "\x5C",
  338. "\x5D",
  339. "\x5E",
  340. "\x5F",
  341. "\x60",
  342. "\x61",
  343. "\x62",
  344. "\x63"
  345. ];//匹配字符集C
  346. $length = strlen($strnum);
  347. $b = [];
  348. $b[0] = "\x1b";
  349. $b[1] = "\x64";
  350. $b[2] = "\x02";
  351. $b[3] = "\x1d";
  352. $b[4] = "\x48";
  353. $b[5] = "\x32";//条形码显示控制,\x32上图下字,\x31上字下图,\x30只显示条形码
  354. $b[6] = "\x1d";
  355. $b[7] = "\x68";
  356. $b[8] = "\x50";// \x30 设置条形码高度,7F是最大的高度
  357. $b[9] = "\x1d";
  358. $b[10] = "\x77";
  359. $b[11] = "\x02";// \x01 设置条形码宽度,1-6
  360. $b[12] = "\x1d";
  361. $b[13] = "\x6b";
  362. $b[14] = "\x49";//选择条形码类型code128,code39,codabar等等
  363. $b[15] = chr($length + 2);
  364. $b[16] = "\x7b";
  365. $b[17] = "\x42";//选择字符集
  366. if ($length > 14 && is_numeric($strnum)) {//大于14个字符,且为纯数字的进来这个区间
  367. $b[17] = "\x43";
  368. $j = 0;
  369. $key = 18;
  370. $ss = $length / 2;//初始化数组长度
  371. if ($length % 2 == 1) {//判断条形码为单数
  372. $ss = $ss - 0.5;
  373. }
  374. for ($i = 0; $i < $ss; $i++) {
  375. $temp = substr($strnum, $j, 2);
  376. $iindex = intval($temp);
  377. $j = $j + 2;
  378. if ($iindex == 0) {
  379. $chr = '';
  380. if ($b[$key + $i - 1] == '0' && $b[$key + $i - 2] == '0') {//判断前面的为字符集B,此时不需要转换字符集
  381. $b[$key + $i] = $codeB[0];
  382. $b[$key + $i + 1] = $codeB[0];
  383. $key += 1;
  384. } else {
  385. if ($b[$key + $i - 1] == 'C' && $b[$key + $i - 2] == '{') {//判断前面的为字符集C时转换字符集B
  386. $b[$key + $i - 2] = "\x7b";
  387. $b[$key + $i - 1] = "\x42";
  388. $b[$key + $i] = $codeB[0];
  389. $b[$key + $i + 1] = $codeB[0];
  390. $key += 1;
  391. } else {
  392. $b[$key + $i] = "\x7b";
  393. $b[$key + $i + 1] = "\x42";
  394. $b[$key + $i + 2] = $codeB[0];
  395. $b[$key + $i + 3] = $codeB[0];
  396. $key += 3;
  397. }
  398. }
  399. } else {
  400. if ($b[$key + $i - 1] == '0' && $b[$key + $i - 2] == '0' && $chr != 'chr') {//判断前面的为字符集B,此时要转换字符集C
  401. $b[$key + $i] = "\x7b";
  402. $b[$key + $i + 1] = "\x43";
  403. $b[$key + $i + 2] = $codeC[$iindex];
  404. $key += 2;
  405. } else {
  406. $chr = '';
  407. $b[$key + $i] = $codeC[$iindex];
  408. if ($iindex == 48) {
  409. $chr = 'chr';
  410. }//判断chr(48)等于0的情况
  411. }
  412. }
  413. }
  414. @$lastkey = end(array_keys($b));//取得数组的最后一个元素的键
  415. if ($length % 2 > 0) {
  416. $lastnum = substr($strnum, -1);//取得字符串的最后一个数字
  417. if ($b[$lastkey] == '0' && $b[$lastkey - 1] == '0') {//判断前面的为字符集B,此时不需要转换字符集
  418. $b[$lastkey + 1] = $codeB[$lastnum];
  419. } else {
  420. $b[$lastkey + 1] = "\x7b";
  421. $b[$lastkey + 2] = "\x42";
  422. $b[$lastkey + 3] = $codeB[$lastnum];
  423. }
  424. }
  425. @$b[15] = chr(end(array_keys($b)) - 15);//得出条形码长度
  426. $str = implode("", $b);
  427. } else {//1-14个字符的纯数字和非纯数字的条形码进来这个区间,支持数字,大小写字母,特殊字符例如: !@#$%^&*()-=+_
  428. $str = "\x1b\x64\x02\x1d\x48\x32\x1d\x68\x50\x1d\x77\x02\x1d\x6b\x49" . chr($length + 2) . "\x7b\x42" . $strnum;
  429. }
  430. return $str;
  431. }
  432. /**
  433. * @param $time
  434. * @return string
  435. */
  436. protected function signature($time)
  437. {
  438. // 公共参数,请求公钥
  439. return sha1($this->user . $this->uKey . $time);
  440. }
  441. }
粤ICP备19079148号