shish před 2 měsíci
rodič
revize
d0feae9807

+ 84 - 15
biz-ghs/clear/classes/OrderCgClearClass.php

@@ -734,34 +734,57 @@ class OrderCgClearClass extends BaseClass
         return array_values(array_unique(array_filter(array_map('intval', $arr))));
     }
 
-    protected static function zeroAmountWhereSql($alias = '')
+    /**
+     * xhOrderCgClear.amount 是否视为 0(含 NULL、0.00)
+     */
+    protected static function isZeroAmountSql($alias = '')
     {
         $prefix = $alias !== '' ? $alias . '.' : '';
         return '(' . $prefix . 'amount IS NULL OR ' . $prefix . 'amount = 0 OR ' . $prefix . 'amount = \'0\' OR ' . $prefix . 'amount = \'0.00\')';
     }
 
     /**
-     * 只查 amount=0 的关系(带 clearStyle,避免再查整单 xhClear)
-     * @return array[] id, clearId, orderId, cgId, clearStyle
+     * 步骤1:从 xhOrderCgClear 查 amount=0 的行
+     * @return array[] id, clearId, orderId, cgId
      */
     public static function fetchZeroAmountRelationBatch($cursorId, $limit)
     {
         $relTable = self::$baseFile::tableName();
-        $clearTable = Clear::tableName();
-        $sql = 'SELECT r.id, r.clearId, r.orderId, r.cgId, c.clearStyle FROM ' . $relTable . ' r'
-            . ' INNER JOIN ' . $clearTable . ' c ON c.id = r.clearId'
-            . ' WHERE ' . self::zeroAmountWhereSql('r');
+        $sql = 'SELECT id, clearId, orderId, cgId FROM ' . $relTable
+            . ' WHERE ' . self::isZeroAmountSql();
         $params = [];
         if ($cursorId !== null) {
-            $sql .= ' AND r.id < :cursorId';
+            $sql .= ' AND id < :cursorId';
             $params[':cursorId'] = intval($cursorId);
         }
-        $sql .= ' ORDER BY r.id DESC LIMIT ' . intval($limit);
+        $sql .= ' ORDER BY id DESC LIMIT ' . intval($limit);
         return Yii::$app->db->createCommand($sql)->bindValues($params)->queryAll();
     }
 
     /**
-     * 按关系行 orderId/cgId + clearStyle 反查应对应的 actPrice(不展开整单 saleIds/purchaseIds)
+     * 步骤2 辅助:批量取 clearStyle(仅处理步骤1命中的 clearId)
+     * @param int[] $clearIds
+     * @return array<int,int> clearId => clearStyle
+     */
+    public static function getClearStyleMap(array $clearIds)
+    {
+        $clearIds = array_values(array_unique(array_filter(array_map('intval', $clearIds))));
+        if (empty($clearIds)) {
+            return [];
+        }
+        $rows = Yii::$app->db->createCommand(
+            'SELECT id, clearStyle FROM ' . Clear::tableName()
+            . ' WHERE id IN (' . implode(',', $clearIds) . ')'
+        )->queryAll();
+        $map = [];
+        foreach ($rows as $row) {
+            $map[intval($row['id'])] = intval($row['clearStyle'] ?? 0);
+        }
+        return $map;
+    }
+
+    /**
+     * 步骤2:反查本行 amount 应写入的值(源单 actPrice)
      */
     public static function resolveAmountForRelationRowDirect(array $row)
     {
@@ -847,45 +870,91 @@ class OrderCgClearClass extends BaseClass
     }
 
     /**
+     * 步骤1~3:xhOrderCgClear amount=0 → 反查应填金额 → 按 id 更新
      * @param array[] $rows fetchZeroAmountRelationBatch 的结果
-     * @return array{updated:int,skipped:int}
+     * @return array{updated:int,skipped:int,details:array[]}
      */
-    public static function patchZeroAmountRows(array $rows, $dryRun = false)
+    public static function patchZeroAmountRows(array $rows, $dryRun = false, $logDetails = false)
     {
         if (empty($rows)) {
-            return ['updated' => 0, 'skipped' => 0];
+            return ['updated' => 0, 'skipped' => 0, 'details' => []];
         }
+
+        $clearIds = array_values(array_unique(array_filter(array_map('intval', array_column($rows, 'clearId')))));
+        $clearStyleMap = self::getClearStyleMap($clearIds);
+
         $table = self::$baseFile::tableName();
         $updated = 0;
         $skipped = 0;
+        $details = [];
+
         foreach ($rows as $row) {
             $relId = intval($row['id'] ?? 0);
+            $clearId = intval($row['clearId'] ?? 0);
             if ($relId <= 0) {
                 $skipped++;
                 continue;
             }
+
+            $row['clearStyle'] = $clearStyleMap[$clearId] ?? 0;
             $amount = self::resolveAmountForRelationRowDirect($row);
             if ($amount === null) {
                 $skipped++;
+                if ($logDetails) {
+                    $details[] = [
+                        'id' => $relId,
+                        'clearId' => $clearId,
+                        'orderId' => intval($row['orderId'] ?? 0),
+                        'cgId' => intval($row['cgId'] ?? 0),
+                        'status' => 'skip',
+                        'reason' => '反查不到有效 actPrice',
+                    ];
+                }
                 continue;
             }
+
             if (!$dryRun) {
                 $affected = Yii::$app->db->createCommand(
-                    'UPDATE ' . $table . ' SET amount = :amount WHERE id = :id AND ' . self::zeroAmountWhereSql()
+                    'UPDATE ' . $table . ' SET amount = :amount WHERE id = :id AND ' . self::isZeroAmountSql()
                 )->bindValues([
                     ':amount' => $amount,
                     ':id' => $relId,
                 ])->execute();
                 if ($affected > 0) {
                     $updated++;
+                    if ($logDetails) {
+                        $details[] = [
+                            'id' => $relId,
+                            'clearId' => $clearId,
+                            'amount' => $amount,
+                            'status' => 'updated',
+                        ];
+                    }
                 } else {
                     $skipped++;
+                    if ($logDetails) {
+                        $details[] = [
+                            'id' => $relId,
+                            'status' => 'skip',
+                            'reason' => '更新时 amount 已非 0',
+                        ];
+                    }
                 }
             } else {
                 $updated++;
+                if ($logDetails) {
+                    $details[] = [
+                        'id' => $relId,
+                        'clearId' => $clearId,
+                        'orderId' => intval($row['orderId'] ?? 0),
+                        'cgId' => intval($row['cgId'] ?? 0),
+                        'amount' => $amount,
+                        'status' => 'would_update',
+                    ];
+                }
             }
         }
-        return ['updated' => $updated, 'skipped' => $skipped];
+        return ['updated' => $updated, 'skipped' => $skipped, 'details' => $details];
     }
 
 }

+ 22 - 5
console/controllers/ClearController.php

@@ -108,7 +108,11 @@ class ClearController extends Controller
     }
 
     /**
-     * 补写 xhOrderCgClear 中 amount=0 的行:只查零金额行 → 按 orderId/cgId 反查 actPrice → 按 id 更新
+     * 补写 xhOrderCgClear.amount
+     *
+     * 1. 从 xhOrderCgClear 查 amount=0
+     * 2. 按 orderId/cgId + clearStyle 反查源单 actPrice
+     * 3. 按关系行 id 更新(且仍满足 amount=0 才写,避免覆盖已有值)
      *
      * 用法:
      *   php yii clear/patch-order-cg-clear-amount --dryRun=1 --limit=1000
@@ -138,13 +142,12 @@ class ClearController extends Controller
         $batchNo = 0;
 
         $this->stdout(sprintf(
-            "补写 amount(仅查 amount=0 行)| dryRun=%d batchSize=%d sleepMs=%d limit=%d | 续跑 id<%s | 进度=%s\n",
+            "补 amount | 1查xhOrderCgClear.amount=0 → 2反查actPrice → 3更新 | dryRun=%d batchSize=%d sleepMs=%d limit=%d | id<%s\n",
             (int)$this->dryRun,
             $this->batchSize,
             $this->sleepMs,
             (int)$this->limit,
-            $cursorId === null ? 'max' : (string)$cursorId,
-            $this->getBackfillProgressFile()
+            $cursorId === null ? 'max' : (string)$cursorId
         ));
 
         while (true) {
@@ -166,9 +169,23 @@ class ClearController extends Controller
             $batchNo++;
             $batchMinId = null;
 
-            $result = OrderCgClearClass::patchZeroAmountRows($rows, (bool)$this->dryRun);
+            $result = OrderCgClearClass::patchZeroAmountRows($rows, (bool)$this->dryRun, (bool)$this->dryRun);
             $batchUpdated = intval($result['updated'] ?? 0);
             $batchSkipped = intval($result['skipped'] ?? 0);
+            if ($this->dryRun && !empty($result['details'])) {
+                foreach ($result['details'] as $detail) {
+                    if (($detail['status'] ?? '') === 'would_update') {
+                        $this->stdout(sprintf(
+                            "  预览 id=%d clearId=%d orderId=%d cgId=%d => amount=%s\n",
+                            (int)$detail['id'],
+                            (int)$detail['clearId'],
+                            (int)$detail['orderId'],
+                            (int)$detail['cgId'],
+                            $detail['amount']
+                        ));
+                    }
+                }
+            }
             $updated += $batchUpdated;
             $skipped += $batchSkipped;
             $total += count($rows);