Przeglądaj źródła

Merge branch 'redesign‌-260706' of git.huaml.com:zhh/huahuibao into redesign‌-260706

shizhongqi 1 tydzień temu
rodzic
commit
76bea02861

+ 46 - 0
app-ghs/controllers/FeieyunController.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace ghs\controllers;
+
+use common\components\feiePrintLogUtil;
+use common\components\feieyunCallbackUtil;
+use common\components\util;
+use Yii;
+
+/**
+ * 飞鹅云打印回调
+ */
+class FeieyunController extends BaseController
+{
+    /** 飞鹅回调无需登录 */
+    public $guestAccess = ['call-back'];
+
+    /**
+     * 飞鹅打印状态回调
+     * POST application/x-www-form-urlencoded
+     */
+    public function actionCallBack()
+    {
+        $post = Yii::$app->request->post();
+        if (empty($post)) {
+            $raw = file_get_contents('php://input');
+            if (!empty($raw)) {
+                parse_str($raw, $post);
+            }
+        }
+        Yii::info('飞鹅打印回调: ' . json_encode($post, JSON_UNESCAPED_UNICODE), __METHOD__);
+        if (!feieyunCallbackUtil::verify($post)) {
+            Yii::warning('飞鹅打印回调验签失败: ' . json_encode($post, JSON_UNESCAPED_UNICODE), __METHOD__);
+            util::stop('飞鹅打印回调验签失败');
+        }
+
+        $feieOrderId = trim((string)($post['orderId'] ?? ''));
+        $status = intval($post['status'] ?? 0);
+        $stime = intval($post['stime'] ?? 0);
+        if ($feieOrderId !== '') {
+            feiePrintLogUtil::updateByCallback($feieOrderId, $stime, $status, $post);
+        }
+        // 飞鹅要求 5 秒内返回 SUCCESS
+        util::stop('SUCCESS');
+    }
+}

+ 24 - 1
app-ghs/controllers/OrderController.php

@@ -23,7 +23,7 @@ use bizGhs\order\services\OrderService;
 use bizHd\saas\services\RegionService;
 use bizGhs\product\classes\ProductClass;
 use common\components\baiduAip;
-use common\components\dict;
+use common\components\feiePrintLogUtil;
 use common\components\dateUtil;
 use common\components\dirUtil;
 use common\components\freight;
@@ -38,6 +38,7 @@ use Yii;
 use common\components\util;
 use biz\shop\classes\ShopAdminClass;
 use biz\shop\classes\ShopClass;
+use common\components\dict;
 use common\components\lakala\Lakala;
 use ghs\models\order\CreateOrderForm;
 
@@ -2683,6 +2684,28 @@ class OrderController extends BaseController
         util::complete();
     }
 
+    /**
+     * 飞鹅云打印日志(Redis 缓存)
+     */
+    public function actionPrintLog()
+    {
+        $id = intval(Yii::$app->request->get('id', 0));
+        if ($id <= 0) {
+            util::fail('订单id不对');
+        }
+        $order = OrderClass::getById($id, true);
+        OrderClass::valid($order, $this->shopId);
+
+        $printSn = $this->shopExt->printSn ?? '';
+        $list = feiePrintLogUtil::getList($order->mainId, $order->id);
+
+        util::success([
+            'printSn' => (string)$printSn,
+            'orderSn' => (string)($order->orderSn ?? ''),
+            'list' => $list,
+        ]);
+    }
+
     //订单确认完成 ssh 20210809
     public function actionConfirmFinish()
     {

+ 1 - 0
app-ghs/web/feieyun/feieyun_verify_Rd1vH59DmcEl4Bpe.txt

@@ -0,0 +1 @@
+Rd1vH59DmcEl4Bpe

+ 9 - 0
app-ghs/web/feieyun/public.key

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2LDlvyuClqrnKW01FqYg
+valPy1/e09ZWlvjb5Nu+0T1PsGhKjF4WBb+D7x3Dy/Db5IHMcpG/Eps6ew6n/6rw
+v8Ctu+uZI33YNv9sqAMPjG2EN+WcqCrVrMGUmjITVpIkQEjTdkuismf+VL3x+eJo
+W1y/TaLb9vchReBc6IZowRu2yItC+tFbock5Nupsl5uOCKltm3s0VqtiHUrpgVeV
+8dVJHLhmLENnLgcTqrkZeKogFDT+fTOhzQPZVEqQgdat/6kcmD44lN4UI7EvVNfe
+amwRLgy4e/CpInD9cql+t5eiRLem0+rgPq9RLivM1pRt67crH0WGY1xXtAtzWO0M
+MwIDAQAB
+-----END PUBLIC KEY-----

+ 20 - 18
biz-ghs/order/classes/OrderClass.php

@@ -1960,6 +1960,14 @@ class OrderClass extends BaseClass
                     $showPrice = 0;
                 }
             }
+
+            //清流美朵花业线上下单不显示价格
+            if (isset($orderInfo->mainId) && $orderInfo->mainId == 99641) {
+                if (!empty($orderInfo->customShopAdminId)) {
+                    $showPrice = 0;
+                }
+            }
+
             //北京花篮子,线上下单,不显示价格
             if (isset($orderInfo->mainId) && $orderInfo->mainId == 10735) {
                 if (!empty($orderInfo->customShopAdminId)) {
@@ -2015,7 +2023,7 @@ class OrderClass extends BaseClass
         $orderMainId = $orderInfo['mainId'] ?? 0;
         if (getenv('YII_ENV') == 'production') {
             //东莞我要花不要自动打印标准,mainId=2644,已经去掉了
-            $map = [65726, 58, 25119, 28500, 1294, 12925];
+            $map = [65726, 58, 25119, 28500, 1294, 12925, 83694];
         } else {
             $map = [828];
         }
@@ -2142,7 +2150,7 @@ class OrderClass extends BaseClass
                 $name = $current['name'] ?? '';
                 $count = $current['count'] ?? '';
                 if ($fontSizeType == 1) {
-                    $content .= '<L>'.$name . ' ' . $count . '</L><BR>';
+                    $content .= '<L>' . $name . ' ' . $count . '</L><BR>';
                 } else {
                     $content .= '<B>' . $name . ' ' . $count . '</B><BR>';
                 }
@@ -2213,26 +2221,20 @@ class OrderClass extends BaseClass
         }
 
         $content .= "<BR>";
-
-        if (getenv('YII_ENV') == 'production') {
-            //丽子鲜花、花样年华、镜中花卉、满天星、世纪花、盛丰、福清辉煌、美阁都不显示扫码买花
-            if (!in_array($orderInfo->shopId, [1527, 1585, 1596, 16454, 33668, 38492, 36596, 35389, 38322, 43274, 40312, 44556, 42946, 51758, 8391, 65591, 8596, 81437, 14248])) {
-                //勇记、国恋、花悠星零售开单不需要打印扫码买花
-//                if ($customId != 12145 || $customId != 12487 || $customId != 12281) {
-//                    $content .= "<BR>";
-//                    $content .= "<C>扫码买花结账</C>";
-//                    $content .= "<BR>";
-//                    $host = Yii::$app->params['hdHost'];
-//                    $content .= "<QR>" . $host . "/#/admin/home/workbench</QR>";
-//                }
-            }
-        }
         $content .= "<BR>";
         $content .= "<BR>";
 
         if (!empty($printSn)) {
             $p = new printUtil($printSn);
-            $p->printMsg($content, $shop, $orderSn);
+            // onlinePrint 每次打印均写入飞鹅打印日志缓存
+            $logMeta = [
+                'writeLog' => true,
+                'mainId' => intval($orderInfo['mainId'] ?? 0),
+                'orderId' => intval($orderInfo['id'] ?? 0),
+                'orderSn' => (string)$orderSn,
+                'printSn' => (string)$printSn,
+            ];
+            $p->printMsg($content, $shop, $orderSn, $logMeta);
         }
 
     }
@@ -3079,7 +3081,7 @@ XL;
         /**************************如果是好多花的云仓还要再打一下标签纸,多处要同步修改,关键词 hdh_yc *****************************/
         $orderMainId = $order->mainId;
         if (getenv('YII_ENV') == 'production') {
-            $map = [65726, 58, 25119, 28500, 1294, 12925, 2644, 10652, 2084, 89473,83694];
+            $map = [65726, 58, 25119, 28500, 1294, 12925, 2644, 10652, 2084, 89473, 83694];
         } else {
             $map = [828];
         }

+ 155 - 0
common/components/feiePrintLogUtil.php

@@ -0,0 +1,155 @@
+<?php
+
+namespace common\components;
+
+use Yii;
+
+/**
+ * 飞鹅云打印日志(Redis 缓存,按 mainId + orderId 聚合)
+ */
+class feiePrintLogUtil
+{
+    /** 日志缓存 TTL:30 天 */
+    const CACHE_TTL = 2592000;
+
+    /** 待回调映射 TTL:30 天 */
+    const MAP_TTL = 2592000;
+
+    /**
+     * 订单打印日志 Redis Key
+     */
+    public static function logCacheKey($mainId, $orderId)
+    {
+        return 'feie_print_log:' . intval($mainId) . ':' . intval($orderId);
+    }
+
+    /**
+     * 飞鹅订单 ID 反查 Redis Key
+     */
+    public static function mapCacheKey($feieOrderId)
+    {
+        return 'feie_print_map:' . trim((string)$feieOrderId);
+    }
+
+    /**
+     * 打印提交成功后写入 pending 记录,并建立 feieOrderId 反查索引
+     */
+    public static function appendPending($mainId, $orderId, $orderSn, $printSn, $feieOrderId)
+    {
+        $mainId = intval($mainId);
+        $orderId = intval($orderId);
+        $feieOrderId = trim((string)$feieOrderId);
+        if ($mainId <= 0 || $orderId <= 0 || $feieOrderId === '') {
+            return false;
+        }
+
+        $mapKey = self::mapCacheKey($feieOrderId);
+        $mapData = json_encode([
+            'mainId' => $mainId,
+            'orderId' => $orderId,
+            'orderSn' => (string)$orderSn,
+            'printSn' => (string)$printSn,
+        ], JSON_UNESCAPED_UNICODE);
+        Yii::$app->redis->executeCommand('SETEX', [$mapKey, self::MAP_TTL, $mapData]);
+
+        $logKey = self::logCacheKey($mainId, $orderId);
+        $list = self::decodeList(Yii::$app->redis->executeCommand('GET', [$logKey]));
+        $list[] = [
+            'orderSn' => (string)$orderSn,
+            'printSn' => (string)$printSn,
+            'feieOrderId' => $feieOrderId,
+            'printTime' => 0,
+            'printTimeText' => '',
+            'status' => 0,
+        ];
+        Yii::$app->redis->executeCommand('SETEX', [$logKey, self::CACHE_TTL, json_encode($list, JSON_UNESCAPED_UNICODE)]);
+
+        return true;
+    }
+
+    /**
+     * 飞鹅回调更新打印时间与状态
+     */
+    public static function updateByCallback($feieOrderId, $stime, $status, $rawCallback = [])
+    {
+        $feieOrderId = trim((string)$feieOrderId);
+        if ($feieOrderId === '') {
+            return false;
+        }
+
+        $mapRaw = Yii::$app->redis->executeCommand('GET', [self::mapCacheKey($feieOrderId)]);
+        if (empty($mapRaw)) {
+            Yii::warning('飞鹅打印回调未找到映射: ' . $feieOrderId, __METHOD__);
+            return false;
+        }
+
+        $map = json_decode($mapRaw, true);
+        if (empty($map['mainId']) || empty($map['orderId'])) {
+            return false;
+        }
+
+        $logKey = self::logCacheKey($map['mainId'], $map['orderId']);
+        $list = self::decodeList(Yii::$app->redis->executeCommand('GET', [$logKey]));
+        $stime = intval($stime);
+        $updated = false;
+
+        foreach ($list as &$item) {
+            if (($item['feieOrderId'] ?? '') === $feieOrderId) {
+                $item['printTime'] = $stime;
+                $item['printTimeText'] = $stime > 0 ? date('Y-m-d H:i:s', $stime) : '';
+                $item['status'] = intval($status);
+                if (!empty($rawCallback)) {
+                    $item['callbackInfo'] = $rawCallback;
+                }
+                $updated = true;
+                break;
+            }
+        }
+        unset($item);
+
+        if (!$updated) {
+            $list[] = [
+                'orderSn' => (string)($map['orderSn'] ?? ''),
+                'printSn' => (string)($map['printSn'] ?? ''),
+                'feieOrderId' => $feieOrderId,
+                'printTime' => $stime,
+                'printTimeText' => $stime > 0 ? date('Y-m-d H:i:s', $stime) : '',
+                'status' => intval($status),
+                'callbackInfo' => $rawCallback,
+            ];
+        }
+
+        Yii::$app->redis->executeCommand('SETEX', [$logKey, self::CACHE_TTL, json_encode($list, JSON_UNESCAPED_UNICODE)]);
+        return true;
+    }
+
+    /**
+     * 读取订单打印日志,按 printTime 降序
+     */
+    public static function getList($mainId, $orderId)
+    {
+        $logKey = self::logCacheKey($mainId, $orderId);
+        $list = self::decodeList(Yii::$app->redis->executeCommand('GET', [$logKey]));
+        usort($list, function ($a, $b) {
+            $timeA = intval($a['printTime'] ?? 0);
+            $timeB = intval($b['printTime'] ?? 0);
+            if ($timeA === $timeB) {
+                return strcmp($b['feieOrderId'] ?? '', $a['feieOrderId'] ?? '');
+            }
+            return $timeB <=> $timeA;
+        });
+        return $list;
+    }
+
+    /**
+     * 解析 Redis 中的 JSON 数组
+     */
+    protected static function decodeList($raw)
+    {
+        if (empty($raw)) {
+            return [];
+        }
+        $list = json_decode($raw, true);
+        return is_array($list) ? $list : [];
+    }
+}

+ 85 - 0
common/components/feieyunCallbackUtil.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace common\components;
+
+use Yii;
+
+/**
+ * 飞鹅云打印回调验签(SHA256WithRSA)
+ * 文档:http://help.feieyun.com/#/home/doc/zh;nav=1-8
+ */
+class feieyunCallbackUtil
+{
+    /**
+     * 飞鹅公钥文件路径
+     */
+    public static function getPublicKeyPath()
+    {
+        return Yii::getAlias('@app/web/feieyun/public.key');
+    }
+
+    /**
+     * 组装待验签字符串:剔除 sign 与空值,按参数名 ASCII 升序拼接
+     */
+    public static function buildSignContent(array $params)
+    {
+        unset($params['sign']);
+        $filtered = [];
+        foreach ($params as $key => $value) {
+            if ($value === null || $value === '') {
+                continue;
+            }
+            $filtered[(string)$key] = (string)$value;
+        }
+        ksort($filtered, SORT_STRING);
+
+        $parts = [];
+        foreach ($filtered as $key => $value) {
+            $parts[] = $key . '=' . $value;
+        }
+        return implode('&', $parts);
+    }
+
+    /**
+     * 校验飞鹅回调签名
+     */
+    public static function verify(array $params)
+    {
+        $sign = trim((string)($params['sign'] ?? ''));
+        if ($sign === '') {
+            return false;
+        }
+
+        $publicKeyPath = self::getPublicKeyPath();
+        if (!is_file($publicKeyPath)) {
+            Yii::error('飞鹅公钥文件不存在: ' . $publicKeyPath, __METHOD__);
+            return false;
+        }
+
+        $publicKey = file_get_contents($publicKeyPath);
+        if ($publicKey === false || trim($publicKey) === '') {
+            Yii::error('飞鹅公钥文件读取失败: ' . $publicKeyPath, __METHOD__);
+            return false;
+        }
+
+        $signContent = self::buildSignContent($params);
+        $signature = base64_decode($sign, true);
+        if ($signature === false) {
+            Yii::warning('飞鹅回调 sign Base64 解码失败', __METHOD__);
+            return false;
+        }
+
+        $key = openssl_pkey_get_public($publicKey);
+        if ($key === false) {
+            Yii::error('飞鹅公钥解析失败: ' . openssl_error_string(), __METHOD__);
+            return false;
+        }
+
+        $result = openssl_verify($signContent, $signature, $key, OPENSSL_ALGO_SHA256);
+        if (PHP_VERSION_ID < 80000) {
+            openssl_free_key($key);
+        }
+
+        return $result === 1;
+    }
+}

+ 29 - 5
common/components/printUtil.php

@@ -60,8 +60,10 @@ class printUtil
 
     /**
      * 打印订单
+     *
+     * @param array|null $logMeta 打印日志上下文:mainId、orderId、orderSn、printSn、writeLog
      */
-    public function printMsg($content, $shop = null, $orderSn = '')
+    public function printMsg($content, $shop = null, $orderSn = '', $logMeta = null)
     {
         $contentLength = strlen($content);
         if ($contentLength > 5000) {
@@ -69,21 +71,30 @@ class printUtil
             $newContent = '';
             foreach ($contents as $line) {
                 if (strlen($newContent . $line . "<BR>") > 5000) {
-                    $this->doPrint($newContent, $shop, $orderSn);
+                    $this->doPrint($newContent, $shop, $orderSn, $logMeta);
                     $newContent = $line . "<BR>";
                 } else {
                     $newContent .= $line . "<BR>";
                 }
             }
             if (!empty($newContent)) {
-                $this->doPrint($newContent, $shop, $orderSn);
+                $this->doPrint($newContent, $shop, $orderSn, $logMeta);
             }
         } else {
-            $this->doPrint($content, $shop, $orderSn);
+            $this->doPrint($content, $shop, $orderSn, $logMeta);
         }
     }
 
-    private function doPrint($content, $shop = null, $orderSn = '')
+    /**
+     * 飞鹅打印回调地址
+     */
+    protected function getCallbackUrl()
+    {
+        $host = Yii::$app->params['ghsHost'] ?? '';
+        return rtrim($host, '/') . '/feieyun/call-back';
+    }
+
+    private function doPrint($content, $shop = null, $orderSn = '', $logMeta = null)
     {
         $time = time();         //请求时间
         $msgInfo = array(
@@ -95,6 +106,10 @@ class printUtil
             'content' => $content,
             'times' => $this->times//打印次数
         );
+        // 写入打印日志时需要带回调地址
+        if (!empty($logMeta['writeLog'])) {
+            $msgInfo['backurl'] = $this->getCallbackUrl();
+        }
         try {
             $client = new \HttpClient(self::IP, self::PORT);
             $client->useGzip(false); // 禁用 Gzip
@@ -111,6 +126,15 @@ class printUtil
             $result = $client->getContent();
             Yii::info('打印日志吧:' . $result);
             $arr = json_decode($result, true);
+            if (isset($arr['ret']) && intval($arr['ret']) === 0 && !empty($arr['data']) && !empty($logMeta['writeLog'])) {
+                feiePrintLogUtil::appendPending(
+                    $logMeta['mainId'] ?? 0,
+                    $logMeta['orderId'] ?? 0,
+                    $logMeta['orderSn'] ?? $orderSn,
+                    $logMeta['printSn'] ?? $this->sn,
+                    $arr['data']
+                );
+            }
             if (isset($arr['ret']) && $arr['ret'] == 1001) {
                 $msg = $arr['msg'] ?? '';
                 $sjName = $shop->merchantName ?? '';