json, true); if (json_last_error() !== JSON_ERROR_NONE) { $this->logError('JSON解析失败: ' . json_last_error_msg(), $output); return false; } // 修复:直接处理解析后的顶层数据,而不是依赖键名 $topLevelAreas = is_array($areaData) ? $areaData : []; foreach ($topLevelAreas as $item) { if (is_array($item) && isset($item['adcode'])) { if ($item['parent']['adcode'] == 0){ continue; } $this->logInfo(0, "开始处理顶层数据: {$item['name']}", $output); $this->processArea($item, $output); } } $output->writeln("[" . date('Y-m-d H:i:s') . "] INFO: 所有层级数据处理完成"); return true; } /** * 递归处理所有层级(核心修复:增强子级处理逻辑) * @param array $area 地区数据 * @param int $parentId 父级ID * @param object $output 输出对象 * @param int $currentDepth 当前层级深度(用于日志追踪) * @return int 地区ID */ private function processArea($area, $output, $currentDepth = 1) { // 1. 提取基础信息(增加容错处理) $adcode = isset($area['adcode']) ? $area['adcode'] : 0; $name = isset($area['name']) ? $area['name'] : '未知地区'; $center = isset($area['center']) && is_array($area['center']) ? $area['center'] : []; $longitude = $center[0] ?? ''; $latitude = $center[1] ?? ''; $levelText = isset($area['level']) ? $area['level'] : ''; $level = $this->mapLevel($levelText); // 2. 数据校验 if (empty($adcode)) { $this->logError("第{$currentDepth}层数据缺少adcode: " . json_encode($area), $output); return 0; } // 3. 数据库操作 $sysAreaModel = new SysArea(); $existing = $sysAreaModel->where('id', $adcode)->find(); $currentId = 0; $parentId = $area['parent']['adcode'] == 100000 ? 0 : $area['parent']['adcode']; if ($existing) { // 更新操作 $updateData = [ 'pid' => $parentId, 'longitude' => $longitude, 'latitude' => $latitude, 'level' => $level ]; $sysAreaModel->where('id', $adcode)->update($updateData); $currentId = $adcode; $output->writeln("[" . date('Y-m-d H:i:s') . "] INFO: 第{$currentDepth}层 - 更新地区 [{$name}] (ID:{$currentId}, 父级:{$parentId})"); $oldData = $existing ? $existing->toArray() : []; if (!empty($oldData)) { $rollbackSql = "UPDATE ns_sys_area SET "; $fields = []; foreach ($oldData as $field => $value) { // 排除无需回滚的字段(如主键、自动更新的时间戳等,需根据表结构调整) if (in_array($field, ['id', 'create_time', 'update_time'])) { continue; } // 字符串类型字段加引号,防止SQL注入 if (is_string($value)) { $escapedValue = addslashes($value); $fields[] = "`{$field}` = '{$escapedValue}'"; } else { $fields[] = "`{$field}` = {$value}"; } } $rollbackSql .= implode(', ', $fields) . " WHERE id = {$adcode};\n"; // 追加写入回滚SQL到文件 file_put_contents('rollback_update.sql', $rollbackSql, FILE_APPEND); $output->writeln($rollbackSql); } } else { // 新增操作 $insertData = [ 'id' => $adcode, 'pid' => $parentId, 'name' => $name, 'shortname' => $name, 'longitude' => $longitude, 'latitude' => $latitude, 'level' => $level, 'sort' => 0, 'status' => 1 ]; $result = $sysAreaModel->create($insertData); $currentId = $result ? $result->id : 0; $this->logInfo($currentId, "第{$currentDepth}层 - 新增地区 [{$name}] (ID:{$currentId}, 父级:{$parentId})", $output); } // 4. 修复:增强子级处理逻辑,确保所有层级都能被递归处理 $children = []; // 兼容不同的children数据结构 if (isset($area['children']) && is_array($area['children'])) { $children = $area['children']; } // 特殊处理可能存在的子级字段名差异 elseif (isset($area['child']) && is_array($area['child'])) { $children = $area['child']; } if (!empty($children)) { $output->writeln("[" . date('Y-m-d H:i:s') . "] INFO: 开始处理 [{$name}] 的子级(共" . count($children) . "个,当前层级:{$currentDepth})"); foreach ($children as $child) { if (is_array($child) && isset($child['adcode'])) { // 递归处理子级,层级深度+1 $this->processArea($child, $output, $currentDepth + 1); } else { $this->logError("第{$currentDepth}层 - 无效的子级数据: " . json_encode($child), $output); } } } return $currentId; } /** * 层级映射(覆盖所有可能的层级) */ private function mapLevel($levelText) { $levelMap = [ 'country' => 0, // 国家 'province' => 1, // 省/直辖市 'city' => 2, // 市 'district' => 3, // 区/县 'street' => 4 // 街道(支持到第四级) ]; return $levelMap[$levelText] ?? 99; // 未知层级用99标记 } /** * 日志与回滚处理 */ private function logInfo($id, $message, $output) { $output->writeln("[" . date('Y-m-d H:i:s') . "] INFO: {$message}"); if ($id > 0) { file_put_contents('callback.sql', "DELETE FROM ns_sys_area WHERE id = {$id};" . PHP_EOL, FILE_APPEND); } } private function logError($message, $output) { $errorMsg = "[" . date('Y-m-d H:i:s') . "] ERROR: {$message}"; $output->writeln($errorMsg); Log::error($errorMsg); } }