unknown vor 1 Monat
Ursprung
Commit
d71e1961c1
1 geänderte Dateien mit 165 neuen und 31 gelöschten Zeilen
  1. 165 31
      application/service/UnifiedCostCalculationService.php

+ 165 - 31
application/service/UnifiedCostCalculationService.php

@@ -56,6 +56,35 @@ class UnifiedCostCalculationService
         '成本合计' => 0,
     ];
 
+    // 添加科目名称到数据库字段的映射
+    const SUBJECT_TO_FIELD_MAP = [
+        // 真空鼓风机相关
+        '真空鼓风机' => '真空鼓风机',
+        '真空鼓风' => '真空鼓风机',
+        '6#楼真空鼓风' => '真空鼓风机',
+        '真空风机' => '真空鼓风机',
+
+        // 废气处理相关
+        '废气处理' => '废气处理',
+        '废气' => '废气处理',
+
+        // 锅炉相关
+        '锅炉' => '锅炉',
+        '热水锅炉' => '热水锅炉',
+
+        // 空压机相关
+        '空压机' => '空压机',
+        '空压' => '空压机',
+
+        // 中央空调相关
+        '中央空调' => '中央空调',
+        '空调' => '中央空调',
+
+        // 待分摊总额
+        '待分摊总额' => '分摊水电',
+        '分摊总额' => '分摊水电',
+    ];
+
     // 批次大小
     const BATCH_SIZE = 100;
 
@@ -825,6 +854,10 @@ class UnifiedCostCalculationService
                 return;
             }
 
+            // 记录原始科目名称用于调试
+            $originalSubjects = array_unique(array_column($utilityData, '科目名称'));
+            Log::info("原始科目名称: " . implode(', ', $originalSubjects));
+
             $machineAllocations = $this->calculateMachineAllocations($utilityData);
 
             if (!empty($machineAllocations)) {
@@ -1114,13 +1147,26 @@ class UnifiedCostCalculationService
      */
     protected function simplifySubjectName(string $subject): string
     {
-        foreach (self::SUBJECT_MAPPING as $keyword => $simple) {
+        // 先尝试完全匹配
+        $subject = trim($subject);
+
+        foreach (self::SUBJECT_TO_FIELD_MAP as $keyword => $mappedField) {
+            if ($subject === $keyword) {
+                return $mappedField;
+            }
+        }
+
+        // 尝试部分匹配
+        foreach (self::SUBJECT_TO_FIELD_MAP as $keyword => $mappedField) {
             if (strpos($subject, $keyword) !== false) {
-                return $simple;
+                Log::debug("科目名称部分匹配: {$subject} => {$mappedField} (关键词: {$keyword})");
+                return $mappedField;
             }
         }
 
-        return $subject;
+        // 如果都不匹配,使用默认映射
+        Log::warning("未识别的科目名称: {$subject}, 将映射到 '分摊水电'");
+        return '分摊水电';
     }
 
     /**
@@ -1152,9 +1198,13 @@ class UnifiedCostCalculationService
                     continue;
                 }
 
+                // 使用简化后的科目名称
+                $simplifiedSubject = $this->simplifySubjectName($subject);
+
                 $this->allocationFactors[] = [
                     'Sys_ny' => $month,
-                    '科目名称' => $subject,
+                    '科目名称' => $subject,  // 原始科目名称
+                    '简化科目名称' => $simplifiedSubject,  // 简化后的科目名称
                     '设备编号' => (string)$machine,
                     '分摊系数' => 1,
                     '分摊金额' => floatval($amount),
@@ -1164,7 +1214,6 @@ class UnifiedCostCalculationService
             }
         }
     }
-
     /**
      * 分配分摊水电
      */
@@ -1180,15 +1229,20 @@ class UnifiedCostCalculationService
                 continue;
             }
 
+            // 直接水电费(保持不变)
             $detail['直接水电'] = round($hours * 0.69, 2);
 
+            // 分摊水电费 - 使用映射后的字段名
             foreach ($machineRates[$machine] as $subject => $rate) {
                 $field = $this->getUtilityFieldName($subject);
+                if (!isset($detail[$field])) {
+                    Log::warning("数据库字段不存在: {$field},跳过该分摊");
+                    continue;
+                }
                 $detail[$field] = round($hours * $rate, 2);
             }
         }
     }
-
     /**
      * 计算机台每机时费用
      */
@@ -1218,17 +1272,21 @@ class UnifiedCostCalculationService
      */
     protected function getUtilityFieldName(string $subject): string
     {
-        $map = [
-            '待分摊总额' => '分摊水电',
-            '废气处理' => '废气处理',
-            '锅炉' => '锅炉',
-            '空压机' => '空压机',
-            '热水锅炉' => '热水锅炉',
-            '真空鼓风机' => '真空鼓风机',
-            '中央空调' => '中央空调',
-        ];
+        // 使用映射表
+        if (isset(self::SUBJECT_TO_FIELD_MAP[$subject])) {
+            return self::SUBJECT_TO_FIELD_MAP[$subject];
+        }
 
-        return $map[$subject] ?? $subject;
+        // 尝试部分匹配
+        foreach (self::SUBJECT_TO_FIELD_MAP as $keyword => $mappedField) {
+            if (strpos($subject, $keyword) !== false) {
+                return $mappedField;
+            }
+        }
+
+        // 默认映射到 '分摊水电'
+        Log::warning("未知的科目字段: {$subject}, 映射到 '分摊水电'");
+        return '分摊水电';
     }
 
     /**
@@ -1268,31 +1326,59 @@ class UnifiedCostCalculationService
         $tableName = '成本v23_月度成本明细';
 
         try {
+            // 获取数据库表结构
             $columns = Db::query("DESCRIBE `{$tableName}`");
             $columnNames = array_column($columns, 'Field');
 
             Log::info("表{$tableName}结构字段数: " . count($columnNames));
+            Log::debug("表字段列表: " . implode(', ', $columnNames));
 
+            // 检查并修正每行数据
             foreach ($this->monthlyCostDetails as $index => &$row) {
                 // 确保行是数组
                 $rowArray = is_object($row) ? (array)$row : $row;
 
-                if (count($rowArray) !== count($columnNames)) {
-                    Log::warning("第" . ($index + 1) . "行字段数不匹配: 数据" . count($rowArray) . "个,表" . count($columnNames) . "个");
-                    $this->fixRowData($rowArray, $columnNames, $index);
-                    $row = $rowArray;
-                }
-
-                // 检查所有数组键名是否为字符串
+                // 1. 移除表中不存在的字段
                 foreach ($rowArray as $key => $value) {
-                    if (!is_string($key) && !is_int($key)) {
-                        Log::error("发现非法键名类型 (行 {$index}, 键类型: " . gettype($key) . ")");
-                        // 转换为字符串
+                    if (!in_array($key, $columnNames)) {
+                        Log::warning("移除无效字段 (行 {$index}): {$key}");
                         unset($rowArray[$key]);
-                        $rowArray[(string)$key] = $value;
                     }
                 }
+
+                // 2. 确保所有表字段都存在
+                foreach ($columnNames as $column) {
+                    if (!array_key_exists($column, $rowArray)) {
+                        // 设置默认值
+                        if (in_array($column, ['直接水电', '分摊材料', '车间人工', '部门人工附加', '分摊水电',
+                            '废气处理', '锅炉', '空压机', '热水锅炉', '真空鼓风机', '中央空调', '分摊其他'])) {
+                            $rowArray[$column] = 0;
+                        } else {
+                            $rowArray[$column] = null;
+                        }
+                        Log::debug("添加缺失字段 (行 {$index}): {$column}");
+                    }
+                }
+
+                // 3. 确保字段顺序与表一致
+                $orderedRow = [];
+                foreach ($columnNames as $column) {
+                    $orderedRow[$column] = $rowArray[$column] ?? null;
+                }
+
+                // 4. 修复字段名中的特殊字符
+                $this->sanitizeFieldNames($orderedRow);
+
+                $row = $orderedRow;
+
+                // 记录第一行的字段信息用于调试
+                if ($index === 0) {
+                    Log::debug("第一行字段数: " . count($orderedRow) . ", 字段: " . implode(', ', array_keys($orderedRow)));
+                }
             }
+
+            Log::info("数据结构验证完成,总记录数: " . count($this->monthlyCostDetails));
+
         } catch (\Exception $e) {
             Log::error("验证数据结构时出错: " . $e->getMessage());
         }
@@ -1344,31 +1430,62 @@ class UnifiedCostCalculationService
      */
     protected function insertBatch(array $batch, string $tableName, int $startIndex): void
     {
+        if (empty($batch)) {
+            Log::warning("批次数据为空,跳过插入");
+            return;
+        }
+
         $firstRow = reset($batch);
         $fields = array_keys($firstRow);
+
+        // 验证字段名不包含特殊字符
+        foreach ($fields as $field) {
+            if (preg_match('/[#@$%^&*()+\-=\[\]{}|;:"<>,.?\/]/', $field)) {
+                Log::error("字段名包含特殊字符: {$field}");
+                throw new Exception("字段名 '{$field}' 包含非法字符");
+            }
+        }
+
         $fieldStr = '`' . implode('`,`', $fields) . '`';
 
         $values = [];
-        foreach ($batch as $row) {
+        foreach ($batch as $rowIndex => $row) {
             $rowValues = [];
             foreach ($fields as $field) {
                 $value = $row[$field] ?? null;
-                $rowValues[] = is_numeric($value) ? $value : "'" . addslashes($value) . "'";
+                if (is_numeric($value)) {
+                    $rowValues[] = $value;
+                } elseif (is_null($value)) {
+                    $rowValues[] = 'NULL';
+                } else {
+                    $rowValues[] = "'" . addslashes((string)$value) . "'";
+                }
             }
             $values[] = '(' . implode(',', $rowValues) . ')';
+
+            // 记录第一行数据用于调试
+            if ($rowIndex === 0 && $startIndex === 0) {
+                Log::debug("第一行数据字段: " . implode(', ', $fields));
+                Log::debug("第一行数据值: " . implode(', ', $rowValues));
+            }
         }
 
         $sql = "INSERT INTO `{$tableName}` ({$fieldStr}) VALUES " . implode(',', $values);
 
+        // 记录SQL语句(前200个字符)
+        Log::debug("SQL语句: " . substr($sql, 0, 200) . "...");
+
         try {
-            Db::execute($sql);
-            Log::info("成功插入批次 " . (($startIndex / self::BATCH_SIZE) + 1));
+            $result = Db::execute($sql);
+            Log::info("成功插入批次 " . (($startIndex / self::BATCH_SIZE) + 1) . ", 影响行数: " . $result);
         } catch (\Exception $e) {
             Log::error("插入批次失败: " . $e->getMessage());
+            Log::error("失败SQL: " . $sql);
             throw $e;
         }
     }
 
+
     /**
      * 插入分摊系数
      */
@@ -1444,4 +1561,21 @@ class UnifiedCostCalculationService
             'error' => $e->getMessage()
         ];
     }
+
+    /**
+     * 清理字段名中的特殊字符
+     */
+    protected function sanitizeFieldNames(array &$row): void
+    {
+        $sanitized = [];
+        foreach ($row as $key => $value) {
+            // 移除字段名中的特殊字符,只保留字母、数字、下划线和中文字符
+            $cleanKey = preg_replace('/[^\w\x{4e00}-\x{9fa5}]/u', '', $key);
+            if ($cleanKey !== $key) {
+                Log::debug("清理字段名: {$key} => {$cleanKey}");
+            }
+            $sanitized[$cleanKey] = $value;
+        }
+        $row = $sanitized;
+    }
 }