2], null, '*', null, true); if (!empty($list)) { foreach ($list as $clear) { $payTime = $clear->payTime ?? ''; $saleIds = $clear->saleIds ?? ''; $ids = explode(',', trim($saleIds)); print_r($ids); if (!empty($ids)) { OrderClass::updateByIds($ids, ['clearTime' => $payTime]); } $purchaseIds = $clear->purchaseIds ?? ''; $purchaseIds = explode(',', $purchaseIds); print_r($purchaseIds); if (!empty($purchaseIds)) { PurchaseClass::updateByIds($purchaseIds, ['clearTime' => $payTime]); } echo "-----------------\n"; } } } /** * 回填 xhOrderCgClear:clearStyle 1(hd2Gys) + 2(gys2Hd) * * 用法: * php yii clear/backfill-order-cg-clear-hd-gys --dryRun=1 --limit=100 * php yii clear/backfill-order-cg-clear-hd-gys --reset=1 --batchSize=50 --sleepMs=200 */ public function actionBackfillOrderCgClearHdGys() { $clearStyles = [ intval(dict::getDict('clearStyle', 'hd2Gys')), intval(dict::getDict('clearStyle', 'gys2Hd')), ]; return $this->runBackfillOrderCgClear( $clearStyles, 'hd_gys', 'hd2Gys(1)+gys2Hd(2)', false ); } /** * 回填 xhOrderCgClear:clearStyle 3(gys2KmGys) 供货商向昆明供货商结账 * * 用法: * php yii clear/rebuild-order-cg-clear-index * php yii clear/backfill-order-cg-clear-km-gys --dryRun=1 --limit=100 * php yii clear/backfill-order-cg-clear-km-gys --reset=1 --batchSize=50 --sleepMs=200 */ public function actionBackfillOrderCgClearKmGys() { $clearStyles = [ intval(dict::getDict('clearStyle', 'gys2KmGys')), ]; return $this->runBackfillOrderCgClear( $clearStyles, 'km_gys', 'gys2KmGys(3)', true ); } /** * @deprecated 请改用 backfill-order-cg-clear-hd-gys 与 backfill-order-cg-clear-km-gys */ public function actionBackfillOrderCgClear() { $this->stderr("请使用独立脚本:\n"); $this->stderr(" php yii clear/backfill-order-cg-clear-hd-gys --reset=1\n"); $this->stderr(" php yii clear/backfill-order-cg-clear-km-gys --reset=1\n"); return ExitCode::UNSPECIFIED_ERROR; } /** * 迁移 gys2KmGys 关系行:orderId/cgId 旧格式 -> cgId 存 xhGhsCgOrder、orderId=0 * * 用法: * php yii clear/migrate-gys2-km-gys-order-cg-clear --dryRun=1 * php yii clear/migrate-gys2-km-gys-order-cg-clear * * 注意:若仍存在唯一索引 order_id_clear_id(clearId,orderId),请先执行 rebuild-order-cg-clear-index */ public function actionMigrateGys2KmGysOrderCgClear() { set_time_limit(0); $this->stdout("迁移 gys2KmGys 关系行 | dryRun={$this->dryRun} batchSize={$this->batchSize}\n"); $count = OrderCgClearClass::migrateLegacyGys2KmGysRows((bool)$this->dryRun, $this->batchSize, $this->sleepMs); $this->stdout(($this->dryRun ? '预览' : '完成') . ":处理 {$count} 行\n"); return ExitCode::OK; } /** * 重建 xhOrderCgClear 唯一索引为 (clearId, cgId, orderId) * * 用法: * php yii clear/rebuild-order-cg-clear-index --dryRun=1 * php yii clear/rebuild-order-cg-clear-index */ public function actionRebuildOrderCgClearIndex() { set_time_limit(0); if ($this->dryRun) { $info = OrderCgClearClass::rebuildRelationUniqueIndex(true); $this->stdout(sprintf( "预览:旧索引=%s 新索引=%s 待迁移旧格式行=%d\n", !empty($info['hasLegacyIndex']) ? '有' : '无', !empty($info['hasNewIndex']) ? '有' : '无', (int)($info['legacyRows'] ?? 0) )); return ExitCode::OK; } $this->stdout("开始重建唯一索引 uk_clear_cg_order(clearId,cgId,orderId)\n"); $result = OrderCgClearClass::rebuildRelationUniqueIndex(false); $this->stdout(sprintf( "完成:删旧索引=%s 建新索引=%s 迁移旧格式行=%d\n", !empty($result['droppedLegacyIndex']) ? '是' : '否', !empty($result['addedNewIndex']) ? '是' : '否', (int)($result['migratedLegacyRows'] ?? 0) )); return ExitCode::OK; } /** * @param int[] $clearStyles * @param string $progressKey 进度文件后缀 * @param string $label 日志标签 * @param bool $checkLegacyGys2KmGys 是否检测 gys2KmGys 旧格式行 */ private function runBackfillOrderCgClear(array $clearStyles, $progressKey, $label, $checkLegacyGys2KmGys) { ini_set('memory_limit', '512M'); set_time_limit(0); if ($this->batchSize < 1) { $this->stderr("batchSize 必须大于 0\n"); return ExitCode::UNSPECIFIED_ERROR; } if ($this->sleepMs < 0) { $this->stderr("sleepMs 不能小于 0\n"); return ExitCode::UNSPECIFIED_ERROR; } $this->backfillProgressKey = $progressKey; if ($this->reset && $this->clearId <= 0) { $this->clearBackfillProgress(); $this->stdout("已重置回填进度 [{$progressKey}]\n"); } $cursorId = $this->clearId > 0 ? null : $this->loadBackfillProgress(); $total = 0; $inserted = 0; $skipped = 0; $batchNo = 0; $this->stdout(sprintf( "开始回填 xhOrderCgClear [%s] | id DESC 分批 | dryRun=%d batchSize=%d sleepMs=%d limit=%d | 续跑 id<%s | 进度=%s\n", $label, (int)$this->dryRun, $this->batchSize, $this->sleepMs, (int)$this->limit, $cursorId === null ? 'max' : (string)$cursorId, $this->getBackfillProgressFile() )); while (true) { if ($this->limit > 0 && $total >= $this->limit) { break; } $fetchLimit = $this->batchSize; if ($this->limit > 0) { $fetchLimit = min($fetchLimit, $this->limit - $total); if ($fetchLimit < 1) { break; } } $list = $this->fetchClearBatch($clearStyles, $cursorId, $fetchLimit); if (empty($list)) { break; } $batchNo++; $batchMinId = null; $clearIds = array_map('intval', array_column($list, 'id')); $emptyAmountClearIds = OrderCgClearClass::getClearIdsWithEmptyAmount($clearIds); $legacyGys2KmGysClearIds = $checkLegacyGys2KmGys ? OrderCgClearClass::getClearIdsWithLegacyGys2KmGysRows($clearIds) : []; $relationCountMap = OrderCgClearClass::getRelationCountMap($clearIds); foreach ($list as $clear) { $total++; $currentClearId = intval($clear['id'] ?? 0); $batchMinId = $currentClearId; $cursorId = $currentClearId; $saleIds = trim($clear['saleIds'] ?? ''); $purchaseIds = trim($clear['purchaseIds'] ?? ''); if ($saleIds === '' && $purchaseIds === '') { $skipped++; continue; } $relationCount = $relationCountMap[$currentClearId] ?? 0; if (OrderCgClearClass::shouldSkipBackfill( $clear, $relationCount, $emptyAmountClearIds, $legacyGys2KmGysClearIds )) { $skipped++; continue; } if ($this->dryRun) { if ($this->logEvery > 0 && $total % $this->logEvery === 0) { $this->stdout("dryRun 已扫描 {$total} 条,当前 clearId={$currentClearId}\n"); } continue; } $count = OrderCgClearClass::backfillFromClear($clear, true); if ($count > 0) { $inserted += $count; } else { $skipped++; } if ($this->logEvery > 0 && $total % $this->logEvery === 0) { $this->stdout("进度:扫描 {$total},写入关系 {$inserted} 行,跳过 {$skipped},当前 clearId={$currentClearId}\n"); } } if (!$this->dryRun && $this->clearId <= 0 && $batchMinId !== null) { $this->saveBackfillProgress($batchMinId); } $this->stdout(sprintf( "第 %d 批完成:本批 %d 条,累计扫描 %d,写入关系 %d 行,跳过 %d,checkpoint id=%d\n", $batchNo, count($list), $total, $inserted, $skipped, (int)$batchMinId )); if ($this->clearId > 0 || count($list) < $fetchLimit) { break; } $this->sleepBetweenBatch(); } if (!$this->dryRun && $this->clearId <= 0 && $cursorId !== null) { $this->stdout("进度已保存,下次从 id < {$cursorId} 继续\n"); } $this->stdout("全部完成 [{$label}]:扫描 {$total} 条 xhClear,写入 {$inserted} 行关系,跳过 {$skipped} 条\n"); return ExitCode::OK; } /** * 按主键倒序轻量拉取一批 xhClear(只查回填所需字段,LIMIT 走主键索引) */ private function fetchClearBatch(array $clearStyles, $cursorId, $limit) { $clearStyles = array_values(array_unique(array_filter(array_map('intval', $clearStyles)))); if (empty($clearStyles)) { return []; } $inParts = []; $params = []; foreach ($clearStyles as $idx => $style) { $key = ':s' . $idx; $inParts[] = $key; $params[$key] = $style; } $inSql = implode(',', $inParts); if ($this->clearId > 0) { $params[':id'] = intval($this->clearId); $row = Yii::$app->db->createCommand( 'SELECT id, orderSn, saleIds, purchaseIds, status, clearStyle FROM ' . Clear::tableName() . ' WHERE id = :id AND clearStyle IN (' . $inSql . ') LIMIT 1' )->bindValues($params)->queryOne(); return $row ? [$row] : []; } $sql = 'SELECT id, orderSn, saleIds, purchaseIds, status, clearStyle FROM ' . Clear::tableName() . ' WHERE clearStyle IN (' . $inSql . ')'; if ($cursorId !== null) { $sql .= ' AND id < :cursorId'; $params[':cursorId'] = intval($cursorId); } $sql .= ' ORDER BY id DESC LIMIT ' . intval($limit); return Yii::$app->db->createCommand($sql)->bindValues($params)->queryAll(); } private function sleepBetweenBatch() { if ($this->sleepMs > 0) { usleep($this->sleepMs * 1000); } } private function getBackfillProgressFile() { $key = $this->backfillProgressKey !== '' ? $this->backfillProgressKey : 'all'; return Yii::getAlias('@console/runtime/order_cg_clear_backfill_' . $key . '.last_id'); } private function loadBackfillProgress() { $file = $this->getBackfillProgressFile(); if (!is_file($file)) { return null; } $id = intval(trim((string)file_get_contents($file))); return $id > 0 ? $id : null; } private function saveBackfillProgress($clearId) { if ($this->dryRun || $this->clearId > 0) { return; } $clearId = intval($clearId); if ($clearId <= 0) { return; } $file = $this->getBackfillProgressFile(); $dir = dirname($file); if (!is_dir($dir)) { mkdir($dir, 0777, true); } file_put_contents($file, (string)$clearId); } private function clearBackfillProgress() { $file = $this->getBackfillProgressFile(); if (is_file($file)) { unlink($file); } } }