Browse Source

成品入仓同步优化

unknown 1 week ago
parent
commit
c59dc7d1f6

+ 92 - 49
application/api/controller/FinishedProductWarehousing.php

@@ -470,14 +470,10 @@ public function getList()
             }
         }
 
-        // 2. 中间表同步(与超节损独立,失败不阻断)
-        if ($this->shouldSyncFinishStockMiddle($data['jjcp_gdbh'])) {
-            try {
-                $synchronization = new \app\api\controller\Synchronization();
-                $synchronization->syncFinishStockMiddle($data['UniqId'], '新增');
-            } catch (\Exception $e) {
-                $warnings[] = $e->getMessage();
-            }
+        // 2. 中间表同步(与超节损独立;失败/未同步均写入日志 status_msg)
+        $syncMsg = $this->syncFinishStockOrLogSkip($data, $data['UniqId'], '新增');
+        if ($syncMsg !== null) {
+            $warnings[] = $syncMsg;
         }
 
         $result = ['UniqId' => $data['UniqId']];
@@ -612,21 +608,22 @@ public function getList()
             }
         }
 
-        // 2. 同步中间表(与超节损独立)
+        // 2. 同步中间表(与超节损独立;失败/未同步均写入日志)
         // - 数量变更:插入差值为新同步数据(增加为正、减少为负)
         // - 仅修改 smb / 时间:正常同步完整数量
         // - 数量与其他同时改:只同步差值
-        if ($this->shouldSyncFinishStockMiddle($old['jjcp_gdbh']) && ($qtyChanged || $smbChanged || $sjChanged)) {
-            try {
-                $synchronization = new \app\api\controller\Synchronization();
-                if ($qtyChanged) {
-                    $qtyDiff = $newQty - $oldQty; // 增加为正,减少为负
-                    $synchronization->syncFinishStockMiddle($uniqId, '修改', $qtyDiff, $sysId);
-                } else {
-                    $synchronization->syncFinishStockMiddle($uniqId, '修改', null, $sysId);
-                }
-            } catch (\Exception $e) {
-                $warnings[] = $e->getMessage();
+        if ($qtyChanged || $smbChanged || $sjChanged) {
+            $recordForSync = array_merge($old, $data);
+            $iQtyOverride = $qtyChanged ? ($newQty - $oldQty) : null;
+            $syncMsg = $this->syncFinishStockOrLogSkip(
+                $recordForSync,
+                $uniqId,
+                '修改',
+                $iQtyOverride,
+                $sysId
+            );
+            if ($syncMsg !== null) {
+                $warnings[] = $syncMsg;
             }
         }
 
@@ -665,20 +662,14 @@ public function getList()
             $this->error('未找到成品入仓记录');
         }
 
-        // 满足同步条件才回冲:MES插入类型=新增,数量取负;不满足则正常删除
-        if ($this->shouldSyncFinishStockMiddle($old['jjcp_gdbh'])) {
-            try {
-                $synchronization = new \app\api\controller\Synchronization();
-                $synchronization->syncFinishStockMiddle(
-                    $uniqId,
-                    '新增',
-                    -1 * (float)$old['jjcp_sl'],
-                    $sysId
-                );
-            } catch (\Exception $e) {
-                $this->error('同步中间表失败,未删除:' . $e->getMessage(), ['UniqId' => $uniqId]);
-            }
-        }
+        // 满足同步条件才回冲;不满足或失败都写日志。同步失败不再阻断删除
+        $syncMsg = $this->syncFinishStockOrLogSkip(
+            $old,
+            $uniqId,
+            '新增',
+            -1 * (float)$old['jjcp_sl'],
+            $sysId
+        );
 
         $mongodb = Db::connect(config('database.mongodb'));
         db()->startTrans();
@@ -698,6 +689,12 @@ public function getList()
             $this->error('失败');
         }
 
+        if ($syncMsg !== null) {
+            $this->success('删除成功,但同步处理异常', [
+                'UniqId'   => $uniqId,
+                'warnings' => [$syncMsg],
+            ]);
+        }
         $this->success('成功');
     }
 
@@ -1549,36 +1546,82 @@ public function getList()
 
     /**
      * 是否需要同步成品入仓中间表
-     * 规则:
-     * 1. 工单编号以 Y 开头,且 >= Y2608001
-     * 2. 工单_基本资料.接单日期 >= 2026-08-01
-     * 3. 中间库 U8_06工单资料 中存在该工单编号
      * @param string $gdbh
      * @return bool
      */
     protected function shouldSyncFinishStockMiddle($gdbh)
+    {
+        return $this->explainFinishStockSyncGate($gdbh)['ok'];
+    }
+
+    /**
+     * 同步门槛判定及跳过原因(便于写入同步日志 status_msg)
+     * @param string $gdbh
+     * @return array{ok:bool,reason:string}
+     */
+    protected function explainFinishStockSyncGate($gdbh)
     {
         $gdbh = strtoupper(trim((string)$gdbh));
-        if ($gdbh === '' || $gdbh[0] !== 'Y') {
-            return false;
+        if ($gdbh === '' || !isset($gdbh[0]) || $gdbh[0] !== 'Y') {
+            return ['ok' => false, 'reason' => '不满足同步条件:工单编号不以Y开头或为空[' . $gdbh . ']'];
         }
         if ((int)substr($gdbh, 1) < 2608001) {
-            return false;
+            return ['ok' => false, 'reason' => '不满足同步条件:工单编号小于Y2608001[' . $gdbh . ']'];
         }
 
-        $orderDate = db('工单_基本资料')->where('Gd_gdbh', $gdbh)->value('接单日期');
+        try {
+            $orderDate = db('工单_基本资料')->where('Gd_gdbh', $gdbh)->value('接单日期');
+        } catch (\Exception $e) {
+            return ['ok' => false, 'reason' => '查询工单_基本资料接单日期失败:' . $e->getMessage()];
+        }
         if (empty($orderDate)) {
-            return false;
+            return ['ok' => false, 'reason' => '不满足同步条件:工单_基本资料无接单日期[' . $gdbh . ']'];
         }
         if (strtotime(trim((string)$orderDate)) < strtotime('2026-08-01')) {
-            return false;
+            return ['ok' => false, 'reason' => '不满足同步条件:接单日期[' . trim((string)$orderDate) . ']早于2026-08-01'];
+        }
+
+        try {
+            // db3:正式中间库,仅查询 U8_06工单资料是否存在该工单
+            $middleDb = Db::connect(config('database.db3'));
+            $exists = $middleDb->name('U8_06工单资料')->where('工单编号', $gdbh)->count();
+        } catch (\Exception $e) {
+            return ['ok' => false, 'reason' => '查询正式库U8_06工单资料失败:' . $e->getMessage()];
+        }
+        if ($exists <= 0) {
+            return ['ok' => false, 'reason' => '不满足同步条件:正式库U8_06工单资料不存在工单[' . $gdbh . ']'];
         }
 
-        // db3:正式中间库,仅查询 U8_06工单资料是否存在该工单
-        // 实际写入走 syncFinishStockMiddle 中的 database.db8(本地 MySQL 测试库)
-        $middleDb = Db::connect(config('database.db3'));
-        $exists = $middleDb->name('U8_06工单资料')->where('工单编号', $gdbh)->count();
-        return $exists > 0;
+        return ['ok' => true, 'reason' => ''];
+    }
+
+    /**
+     * 按判定结果执行同步,或写入「未同步」原因到日志表
+     * @param array $record 成品入仓记录(至少含业务字段)
+     * @param int|string $uniqId
+     * @param string $insertType
+     * @param float|int|null $iQtyOverride
+     * @param string|null $usernameOverride
+     * @return string|null 失败或未同步时的提示文案;成功返回 null
+     */
+    protected function syncFinishStockOrLogSkip(array $record, $uniqId, $insertType, $iQtyOverride = null, $usernameOverride = null)
+    {
+        $gdbh = isset($record['jjcp_gdbh']) ? $record['jjcp_gdbh'] : '';
+        $gate = $this->explainFinishStockSyncGate($gdbh);
+        $synchronization = new \app\api\controller\Synchronization();
+
+        if (!$gate['ok']) {
+            $synchronization->logFinishStockSync($record, $uniqId, $insertType, '未同步', $gate['reason']);
+            // 条件不满足仅记日志,不作为接口告警
+            return null;
+        }
+
+        try {
+            $synchronization->syncFinishStockMiddle($uniqId, $insertType, $iQtyOverride, $usernameOverride);
+            return null;
+        } catch (\Exception $e) {
+            return $e->getMessage();
+        }
     }
 
     /**

+ 105 - 54
application/api/controller/Synchronization.php

@@ -7,6 +7,7 @@ use app\common\library\token\driver\Redis;
 use think\Db;
 use Overtrue\Pinyin;
 use think\Exception;
+use think\Log;
 
 /**
  * 中间表数据同步
@@ -2104,70 +2105,97 @@ class Synchronization extends Api
      */
     public function syncFinishStockMiddle($uniqId, $insertType = '新增', $iQtyOverride = null, $usernameOverride = null)
     {
-        if (empty($uniqId)) {
-            throw new \Exception('同步失败:UniqId 为空');
-        }
+        $syncTime = date('Y-m-d H:i:s');
+        $record = [
+            'UniqId'     => $uniqId,
+            'jjcp_gdbh'  => '',
+            'jjcp_cpdh'  => '',
+            'jjcp_cpmc'  => '',
+            'jjcp_yjno'  => 0,
+            'Sys_id'     => '',
+        ];
 
-        $record = db('成品入仓')->where('UniqId', $uniqId)->find();
-     
-        if (empty($record)) {
-            throw new \Exception('同步失败:未找到成品入仓记录 UniqId=' . $uniqId);
-        }
+        try {
+            if (empty($uniqId)) {
+                throw new \Exception('同步失败:UniqId 为空');
+            }
 
-        if ($usernameOverride !== null && trim((string)$usernameOverride) !== '') {
-            $record['Sys_id'] = $usernameOverride;
-        }
+            $found = db('成品入仓')->where('UniqId', $uniqId)->find();
+            if (empty($found)) {
+                throw new \Exception('同步失败:未找到成品入仓记录 UniqId=' . $uniqId);
+            }
+            $record = $found;
+
+            if ($usernameOverride !== null && trim((string)$usernameOverride) !== '') {
+                $record['Sys_id'] = $usernameOverride;
+            }
 
-        // 写入目标:本地测试库 database.db8(MySQL)
-        // 是否同步的判定在 shouldSyncFinishStockMiddle:用正式库 database.db3 查 U8_06工单资料是否存在
-        $targetDb = Db::connect(config('database.db8'));
-        $targetTable = 'U8_成品入仓';
+            // 写入目标:本地测试库 database.db8(MySQL)
+            // 是否同步的判定在 shouldSyncFinishStockMiddle:用正式库 database.db3 查 U8_06工单资料是否存在
+            $targetDb = Db::connect(config('database.db8'));
+            $targetTable = 'U8_08成品入库';
 
-        $syncTime = date('Y-m-d H:i:s');
-        $smb = preg_replace('/^[\s\x{3000}]+|[\s\x{3000}]+$/u', '', (string)$record['jjcp_smb']);
-        $isEndBoard = ($smb !== '' && mb_substr($smb, 0, 1, 'UTF-8') === '末') ? '末板' : '';
-        $id = 0;
-        $errorMsg = '';
-        $iQty = $iQtyOverride !== null ? $iQtyOverride : $record['jjcp_sl'];
+            $smb = preg_replace('/^[\s\x{3000}]+|[\s\x{3000}]+$/u', '', (string)$record['jjcp_smb']);
+            $isEndBoard = ($smb !== '' && mb_substr($smb, 0, 1, 'UTF-8') === '末') ? '末板' : '';
+            $iQty = $iQtyOverride !== null ? $iQtyOverride : $record['jjcp_sl'];
 
-        try {
             $maxId = (int)$targetDb->name($targetTable)->max('id');
             $id = $maxId + 1;
 
             $data = [
-                'id'          => $id,                                  // 主键ID(目标表不自增,按条数递增)
-                'MESCode'     => '000000',                             // 默认编码
-                'dDate'       => $record['jjcp_sj'],                   // 入仓日期
-                'cWhCode'     => $record['仓库编号'],                   // 仓库编码
-                'cMoCode'     => $record['jjcp_gdbh'],                 // 工单编号
-                'cMemo'       => $record['jjcp_desc'],                 // 备注
-                'cInvCode'    => $record['jjcp_cpdh'],                 // 物料编码
-                'cInvName'    => $record['jjcp_cpmc'],                 // 物料名称
-                'iQty'        => $iQty,                                // 数量(修改数量时可传差值)
-                'cBatch'      => $record['订单编号'],                   // 批次号
-                'username'    => $record['Sys_id'],                    // 用户名
-                '是否末版'    => $isEndBoard,                          // 是否末版
-                'MES插入类型' => $insertType,                          // 插入类型
-                'MES传递时间' => $syncTime,                            // 传递时间
-                'U8接收时间'  => null,                                 // 接收时间为空
-                'U8接收状态'  => '0',                                  // 状态默认 0
-                '印件编码'    => $record['jjcp_cpdh'],                 // 印件编码
-                '印件名称'    => $record['jjcp_cpmc'],                 // 印件名称
-                '成品编码'    => $record['成品编码'],                   // 成品编码
-                '成品名称'    => $record['成品名称'],                   // 成品名称
-                'UniqId'      => $record['UniqId'],                    // 唯一ID
+                'id'          => $id,
+                'MESCode'     => '000000',
+                'dDate'       => $record['jjcp_sj'],
+                'cWhCode'     => $record['仓库编号'],
+                'cMoCode'     => $record['jjcp_gdbh'],
+                'cMemo'       => $record['jjcp_desc'],
+                'cInvCode'    => $record['jjcp_cpdh'],
+                'cInvName'    => $record['jjcp_cpmc'],
+                'iQty'        => $iQty,
+                'cBatch'      => $record['订单编号'],
+                'username'    => $record['Sys_id'],
+                '是否末版'    => $isEndBoard,
+                'MES插入类型' => $insertType,
+                'MES传递时间' => $syncTime,
+                'U8接收时间'  => null,
+                'U8接收状态'  => '0',
+                '印件编码'    => $record['jjcp_cpdh'],
+                '印件名称'    => $record['jjcp_cpmc'],
+                '成品编码'    => $record['成品编码'],
+                '成品名称'    => $record['成品名称'],
+                'UniqId'      => $record['UniqId'],
             ];
-            // 中文字段名不能走 ThinkPHP 命名绑定(PDO 会截断 :data__MES插入类型),改用位置绑定
-            $this->insertFinishStockMiddle($targetDb, $targetTable, $data);
 
+            $this->insertFinishStockMiddle($targetDb, $targetTable, $data);
             $this->writeFinishStockSyncLog($record, $uniqId, $insertType, $syncTime, true, '同步成功');
+            return true;
         } catch (\Exception $e) {
             $errorMsg = $e->getMessage();
             $this->writeFinishStockSyncLog($record, $uniqId, $insertType, $syncTime, false, $errorMsg);
             throw new \Exception('同步中间表失败:' . $errorMsg);
         }
+    }
 
-        return true;
+    /**
+     * 对外写入成品入仓同步日志(成功/失败/未同步均可)
+     * @param array $record
+     * @param int|string $uniqId
+     * @param string $insertType
+     * @param string $status 同步成功/同步失败/未同步
+     * @param string $statusMsg 原因说明
+     */
+    public function logFinishStockSync($record, $uniqId, $insertType, $status, $statusMsg = '')
+    {
+        $success = ($status === '同步成功');
+        $this->writeFinishStockSyncLog(
+            $record,
+            $uniqId,
+            $insertType,
+            date('Y-m-d H:i:s'),
+            $success,
+            $statusMsg,
+            $status
+        );
     }
 
     /**
@@ -2178,24 +2206,35 @@ class Synchronization extends Api
      * @param string $insertType 新增/修改/删除
      * @param string $syncTime
      * @param bool $success
-     * @param string $statusMsg
+     * @param string $statusMsg 失败或跳过原因(写入 status_msg)
+     * @param string|null $statusOverride 覆盖 status 文案
      */
-    protected function writeFinishStockSyncLog($record, $uniqId, $insertType, $syncTime, $success, $statusMsg = '')
+    protected function writeFinishStockSyncLog($record, $uniqId, $insertType, $syncTime, $success, $statusMsg = '', $statusOverride = null)
     {
-        // 操作人取成品入仓 Sys_id(通常为字符串,如 [272/超级用户])
         $sysId = $record['Sys_id'] ?? ($record['sys_id'] ?? '');
+        $status = $statusOverride !== null
+            ? (string)$statusOverride
+            : ($success ? '同步成功' : '同步失败');
+
+        // 保留失败原因,避免字段过长导致整行插入失败
+        $msg = (string)$statusMsg;
+        if (function_exists('mb_substr')) {
+            $msg = mb_substr($msg, 0, 1000, 'UTF-8');
+        } else {
+            $msg = substr($msg, 0, 1000);
+        }
 
         $log = [
             'Gdbh'       => $record['jjcp_gdbh'] ?? '',
             'wlbm'       => $record['jjcp_cpdh'] ?? '',
             'yjmc'       => $record['jjcp_cpmc'] ?? '',
-            'Yjdh'       => $record['jjcp_cpdh'] ?? '',                 // 印件代号
+            'Yjdh'       => $record['jjcp_cpdh'] ?? '',
             'Yjno'       => (int)($record['jjcp_yjno'] ?? 0),
-            'sys_id'     => (string)$sysId,                              // 操作人
+            'sys_id'     => (string)$sysId,
             'sys_rq'     => $syncTime,
             'type'       => $insertType,
-            'status'     => $success ? '同步成功' : '同步失败',
-            'status_msg' => $success ? '' : (string)$statusMsg,
+            'status'     => $status,
+            'status_msg' => $msg,
             'UniqId'     => (int)$uniqId,
         ];
 
@@ -2208,7 +2247,19 @@ class Synchronization extends Api
                 throw new \Exception('日志 insert 返回 false');
             }
         } catch (\Exception $e) {
-            // 不阻断主流程
+            // 降级再试一次;仍失败则写运行日志,避免完全无痕迹
+            try {
+                unset($log['id']);
+                db('工单_成品入仓同步操作日志')->insert($log);
+            } catch (\Exception $e2) {
+                Log::write(
+                    '成品入仓同步日志写入失败: ' . $e2->getMessage()
+                    . ' | status=' . $status
+                    . ' | UniqId=' . $uniqId
+                    . ' | msg=' . $msg,
+                    'error'
+                );
+            }
         }
     }