['02、胶印机组', '03、卷凹机组', '06、单凹机组', '05、圆切机组', '04、圆烫机组', '10、模切机组', '09、烫金机组'], '2' => ['01、切纸机组', '11、检品机组', '07、丝印机组', '12、覆膜机组', '08、喷码机组'], ]; // 科目名称映射 const SUBJECT_MAPPING = [ '废气处理' => '废气处理', '锅炉' => '锅炉', '空压机' => '空压机', '热水锅炉' => '热水锅炉', '真空鼓风机' => '真空鼓风机', '中央空调' => '中央空调', '待分摊总额' => '待分摊总额', ]; // 字段默认值 const DEFAULT_FIELD_VALUES = [ '直接水电' => 0, '分摊材料' => 0, '车间人工' => 0, '部门人工附加' => 0, '分摊水电' => 0, '废气处理' => 0, '锅炉' => 0, '空压机' => 0, '热水锅炉' => 0, '真空鼓风机' => 0, '中央空调' => 0, '分摊其他' => 0, ]; // 批次大小 const BATCH_SIZE = 100; /** * 主入口:执行所有成本计算并统一入库 */ public function calculateAndSaveAll(array $param): array { Db::startTrans(); try { $month = $param['month']; $sysId = $param['sys_id'] ?? ''; // 1. 清空旧数据 $this->clearOldData($month); // 2. 执行五项计算 $this->calculateDirectLabor($param); $this->calculateDirectUtilities($param); $this->calculateIndirectMaterials($month); $this->calculateIndirectLabor($month); $this->calculateApportionedUtilities($param); // 3. 统一插入数据 $this->saveAllData($month, $sysId); Db::commit(); $this->logSuccess($month); return $this->buildSuccessResponse($month); } catch (Exception $e) { Db::rollback(); $this->logError($e); return $this->buildErrorResponse($e); } } /** * 清空旧数据 */ protected function clearOldData(string $month): void { $this->monthlyCostDetails = []; $this->allocationFactors = []; } /** * 1. 计算直接人工 */ protected function calculateDirectLabor(array $param): void { $month = $param['month']; $sysId = $param['sys_id'] ?? ''; $now = date('Y-m-d H:i:s'); $list = Db::name('绩效工资汇总') ->alias('a') ->join('工单_工艺资料 b', 'a.sczl_gdbh = b.Gy0_gdbh and a.sczl_yjno = b.Gy0_yjno and a.sczl_gxh = b.Gy0_gxh') ->join('设备_基本资料 c', 'a.sczl_jtbh = c.设备编号', 'LEFT') ->join('工单_印件资料 d', 'a.sczl_gdbh = d.Yj_Gdbh and a.sczl_yjno = d.yj_Yjno') ->field($this->getDirectLaborFields()) ->where('a.sys_ny', $month) ->group('a.sczl_gdbh,a.sczl_yjno,a.sczl_gxh,a.sczl_jtbh') ->select(); foreach ($list as $item) { $this->monthlyCostDetails[] = $this->buildMonthlyCostDetail($item, $month, $sysId, $now); } Log::info("直接人工计算完成,记录数: " . count($list)); } /** * 获取直接人工查询字段 */ protected function getDirectLaborFields(): string { return 'a.sczl_gdbh as 工单编号,a.sczl_yjno as 印件号,a.sczl_gxh as 工序号, sum(a.班组车头产量) as 班组车头产量,b.Gy0_gxmc as 工序名称, a.sczl_ms as 墨色数,c.使用部门,b.印刷方式,b.版距,b.工价系数, a.sczl_jtbh,d.yj_yjmc as 印件名称,sum(a.车头产量占用机时) as 占用机时, a.sys_rq as 年月,a.工序难度系数,sum(a.班组换算产量) as 班组换算产量, a.千件工价'; } /** * 构建月度成本明细记录 */ protected function buildMonthlyCostDetail(array $data, string $month, string $sysId, string $now): array { $banju = $data['版距'] === '0.0' ? 1000 : $data['版距']; $moshushu = $this->calculateMoshushu($data); $chanliang = $this->calculateChanliang($data); $renGongFenTan = $this->calculateRenGongFenTan($chanliang, $data); return array_merge([ '车间名称' => $data['使用部门'] ?? '', 'sys_ny' => $month, 'sczl_gdbh' => $data['工单编号'], '印件名称' => $data['印件名称'] ?? '', 'sczl_yjno' => $data['印件号'], 'sczl_gxh' => $data['工序号'], '工序名称' => $data['工序名称'] ?? '', 'sczl_jtbh' => $data['sczl_jtbh'] ?? '', '卷张换算系数' => floatval($banju) / 1000, '占用机时' => floatval($data['占用机时']) ?? 0, '班组车头产量' => floatval($data['班组车头产量']) ?? 0, 'sczl_ms' => floatval($moshushu), '工序难度系数' => floatval($data['工序难度系数']) ?? 1, '班组换算产量' => floatval($data['班组换算产量']) ?? 0, '千件工价' => floatval($data['千件工价']) ?? 0, '计件产量' => floatval($chanliang), '水电分摊因子' => floatval($data['占用机时']) ?? 0, '材料分摊因子' => floatval($chanliang), '人工分摊因子' => floatval($renGongFenTan), 'Sys_id' => $sysId, 'Sys_rq' => $now ], self::DEFAULT_FIELD_VALUES); } /** * 计算墨色数 */ protected function calculateMoshushu(array $data): float { if (strpos($data['工序名称'] ?? '', '切废') !== false) { return 0.2; } return $data['墨色数'] === '0.00' ? 1.0 : floatval($data['墨色数']); } /** * 计算产量 */ protected function calculateChanliang(array $data): float { return ($data['班组车头产量'] * $data['工序难度系数']) + $data['班组换算产量']; } /** * 计算人工分摊 */ protected function calculateRenGongFenTan(float $chanliang, array $data): float { return ($chanliang / 1000) * $data['千件工价']; } /** * 2. 计算直接水电 */ protected function calculateDirectUtilities(array $param): void { $month = $param['month']; if (empty($this->monthlyCostDetails)) { throw new Exception("请先执行直接人工计算"); } $utilityData = $this->fetchDirectUtilities($month); if (empty($utilityData)) { Log::info("{$month}月份未找到直接水电费用数据"); return; } $machineUtilities = $this->calculateMachineUtilities($utilityData); $workOrderHours = $this->getMachineWorkHours(); $machineTotalHours = $this->calculateMachineTotalHours($workOrderHours); $allocationResults = $this->allocateUtilitiesToWorkOrders( $machineUtilities, $workOrderHours, $machineTotalHours ); $this->updateDirectUtilitiesToCostDetails($allocationResults); } /** * 获取直接水电数据 */ protected function fetchDirectUtilities(string $month): array { return Db::name('成本_各月水电气') ->where('Sys_ny', $month) ->where('费用类型', '直接') ->field('设备编号, 部门名称, 科目名称, 耗电量, 单位电价, 耗气量, 单位气价') ->select(); } /** * 计算机台水电费用 */ protected function calculateMachineUtilities(array $utilityData): array { $machineUtilities = []; foreach ($utilityData as $item) { $machineCode = $item['设备编号'] ?? ''; if (empty($machineCode)) { continue; } $electricityCost = $this->calculateElectricityCost($item); $gasCost = $this->calculateGasCost($item); $totalCost = $electricityCost + $gasCost; if (!isset($machineUtilities[$machineCode])) { $machineUtilities[$machineCode] = [ '机器编号' => $machineCode, '部门名称' => $item['部门名称'] ?? '', '总费用' => 0, '电费' => 0, '气费' => 0, ]; } $machineUtilities[$machineCode]['总费用'] += $totalCost; $machineUtilities[$machineCode]['电费'] += $electricityCost; $machineUtilities[$machineCode]['气费'] += $gasCost; } return $machineUtilities; } /** * 计算电费 */ protected function calculateElectricityCost(array $item): float { return round(floatval($item['耗电量'] ?? 0) * floatval($item['单位电价'] ?? 0), 2); } /** * 计算气费 */ protected function calculateGasCost(array $item): float { return round(floatval($item['耗气量'] ?? 0) * floatval($item['单位气价'] ?? 0), 2); } /** * 获取机台工作小时数 */ protected function getMachineWorkHours(): array { $workHours = []; foreach ($this->monthlyCostDetails as $detail) { $machineCode = $detail['sczl_jtbh'] ?? ''; $hours = floatval($detail['占用机时'] ?? 0); if (!empty($machineCode) && $hours > 0) { $workHours[] = [ 'sczl_gdbh' => $detail['sczl_gdbh'] ?? '', 'sczl_yjno' => $detail['sczl_yjno'] ?? '', 'sczl_gxh' => $detail['sczl_gxh'] ?? '', 'sczl_jtbh' => $machineCode, '占用机时' => $hours, '车间名称' => $detail['车间名称'] ?? '', ]; } } return $workHours; } /** * 计算机台总小时数 */ protected function calculateMachineTotalHours(array $workOrderHours): array { $machineTotalHours = []; foreach ($workOrderHours as $workOrder) { $machineCode = $workOrder['sczl_jtbh'] ?? ''; $hours = floatval($workOrder['占用机时'] ?? 0); if (!empty($machineCode) && $hours > 0) { $machineTotalHours[$machineCode] = ($machineTotalHours[$machineCode] ?? 0) + $hours; } } return $machineTotalHours; } /** * 分摊水电到工单 */ protected function allocateUtilitiesToWorkOrders( array $machineUtilities, array $workOrderHours, array $machineTotalHours ): array { $allocationResults = []; foreach ($workOrderHours as $workOrder) { $machineCode = $workOrder['sczl_jtbh'] ?? ''; $hours = floatval($workOrder['占用机时'] ?? 0); if (empty($machineCode) || !isset($machineUtilities[$machineCode]) || $machineUtilities[$machineCode]['总费用'] <= 0 || !isset($machineTotalHours[$machineCode]) || $machineTotalHours[$machineCode] <= 0) { continue; } $allocationRatio = $hours / $machineTotalHours[$machineCode]; $allocatedAmount = round($machineUtilities[$machineCode]['总费用'] * $allocationRatio, 2); $uniqueKey = $this->getWorkOrderUniqueKey($workOrder); $allocationResults[$uniqueKey] = [ 'unique_key' => $uniqueKey, 'sczl_gdbh' => $workOrder['sczl_gdbh'] ?? '', 'sczl_yjno' => $workOrder['sczl_yjno'] ?? '', 'sczl_gxh' => $workOrder['sczl_gxh'] ?? '', 'sczl_jtbh' => $machineCode, '占用机时' => $hours, '分摊比例' => $allocationRatio, '分摊金额' => $allocatedAmount, '机台总费用' => $machineUtilities[$machineCode]['总费用'], '机台总工时' => $machineTotalHours[$machineCode], ]; } return $allocationResults; } /** * 获取工单唯一键 */ protected function getWorkOrderUniqueKey(array $workOrder): string { return implode('-', [ $workOrder['sczl_gdbh'] ?? '', $workOrder['sczl_yjno'] ?? '', $workOrder['sczl_gxh'] ?? '', $workOrder['sczl_jtbh'] ?? '' ]); } /** * 更新直接水电到成本明细 */ protected function updateDirectUtilitiesToCostDetails(array $allocationResults): void { if (empty($allocationResults)) { return; } $updatedCount = 0; $totalAllocatedAmount = 0; foreach ($this->monthlyCostDetails as &$costDetail) { $uniqueKey = $this->getWorkOrderUniqueKey($costDetail); if (isset($allocationResults[$uniqueKey])) { $allocatedAmount = $allocationResults[$uniqueKey]['分摊金额']; $costDetail['直接水电'] = $allocatedAmount; $totalAllocatedAmount += $allocatedAmount; $updatedCount++; } } Log::info("已更新直接水电费的工单数量: {$updatedCount}, 分配总金额: {$totalAllocatedAmount}"); } /** * 3. 计算间接材料分摊 */ protected function calculateIndirectMaterials(string $month): void { if (empty($this->monthlyCostDetails)) { return; } $date = substr($month, 0, 4) . '-' . substr($month, 4, 2); $totalMoney = $this->getIndirectMaterialTotal($date); if (!$totalMoney || $totalMoney <= 0) { return; } $totalChroma = $this->calculateTotalChroma(); if ($totalChroma <= 0) { return; } $this->allocateIndirectMaterials($totalMoney, $totalChroma); } /** * 获取间接材料总额 */ protected function getIndirectMaterialTotal(string $date): float { $result = Db::name('材料出库单列表') ->where([ '出库日期' => ['like', $date . '%'], '部门' => '印刷成本中心' ]) ->whereNull('表体生产订单号') ->field('SUM(金额) as money') ->find(); return $result ? floatval($result['money']) : 0; } /** * 计算总色度数 */ protected function calculateTotalChroma(): float { $totalChroma = 0; foreach ($this->monthlyCostDetails as $detail) { $totalChroma += floatval($detail['班组车头产量']) * floatval($detail['sczl_ms']); } return $totalChroma; } /** * 分摊间接材料 */ protected function allocateIndirectMaterials(float $totalMoney, float $totalChroma): void { foreach ($this->monthlyCostDetails as &$detail) { $chroma = floatval($detail['班组车头产量']) * floatval($detail['sczl_ms']); $money = round($totalMoney * ($chroma / $totalChroma), 2); $detail['分摊材料'] = $money; } } /** * 4. 计算间接人工分摊 */ protected function calculateIndirectLabor(string $month): void { $wageRatio = $this->getWageRatio(); if (empty($wageRatio)) { return; } $monthWage = $this->getMonthlyWage($month); if (empty($monthWage)) { return; } $this->allocateIndirectLabor($wageRatio, $monthWage); } /** * 获取工资比例 */ protected function getWageRatio(): array { $workshopTotals = []; foreach ($this->monthlyCostDetails as $detail) { $workshop = $detail['车间名称']; $amount = floatval($detail['人工分摊因子']); $workshopTotals[$workshop] = ($workshopTotals[$workshop] ?? 0) + $amount; } $total = array_sum($workshopTotals); if ($total <= 0) { return []; } $ratios = []; foreach ($workshopTotals as $workshop => $workshopTotal) { $ratios[$workshop] = round($workshopTotal / $total, 4); } return $ratios; } /** * 获取月度工资数据 */ protected function getMonthlyWage(string $month): array { return Db::name('成本_各月其他费用') ->where('sys_ny', $month) ->field('部门人员工资,管理人员工资') ->find() ?: []; } /** * 分摊间接人工 */ protected function allocateIndirectLabor(array $wageRatio, array $monthWage): void { foreach ($wageRatio as $workshopName => $ratio) { $chromaData = $this->getChromaDataForWorkshop($workshopName); if (empty($chromaData['list']) || $chromaData['total'] == 0) { continue; } $this->allocateWageToWorkshop($workshopName, $ratio, $monthWage, $chromaData); } } /** * 获取车间色度数数据 */ protected function getChromaDataForWorkshop(string $workshop): array { $data = ['total' => 0, 'list' => []]; foreach ($this->monthlyCostDetails as $detail) { if ($detail['车间名称'] === $workshop) { $chroma = floatval($detail['班组车头产量']) * floatval($detail['sczl_ms']); $data['total'] += $chroma; $data['list'][] = [ 'sczl_gdbh' => $detail['sczl_gdbh'], 'sczl_yjno' => $detail['sczl_yjno'], 'sczl_gxh' => $detail['sczl_gxh'], 'sczl_jtbh' => $detail['sczl_jtbh'], 'chroma' => $chroma ]; } } return $data; } /** * 分配工资到车间 */ protected function allocateWageToWorkshop( string $workshop, float $ratio, array $monthWage, array $chromaData ): void { $wageTypes = [ '部门人员工资' => '车间人工', '管理人员工资' => '部门人工附加' ]; foreach ($wageTypes as $wageType => $fieldName) { if (empty($monthWage[$wageType]) || $monthWage[$wageType] <= 0) { continue; } $workshopAmount = $ratio * $monthWage[$wageType]; if ($chromaData['total'] <= 0) { continue; } $this->allocateWageToDetails($workshop, $workshopAmount, $chromaData, $fieldName); } } /** * 分配工资到明细记录 */ protected function allocateWageToDetails( string $workshop, float $workshopAmount, array $chromaData, string $fieldName ): void { foreach ($this->monthlyCostDetails as &$detail) { if ($detail['车间名称'] === $workshop) { $chroma = floatval($detail['班组车头产量']) * floatval($detail['sczl_ms']); $amount = round($chroma / $chromaData['total'] * $workshopAmount, 2); $detail[$fieldName] += $amount; } } } /** * 5. 计算分摊水电 */ protected function calculateApportionedUtilities(array $param): void { $month = $param['month']; $sysId = $param['sys_id'] ?? ''; $utilityData = $this->fetchApportionedUtilities($month); if (empty($utilityData)) { return; } $machineAllocations = $this->calculateMachineAllocations($utilityData); $this->generateAllocationFactors($machineAllocations, $month, $sysId); $this->allocateApportionedUtilities($machineAllocations); } /** * 获取分摊水电数据 */ protected function fetchApportionedUtilities(string $month): array { return Db::name('成本_各月水电气') ->where('Sys_ny', $month) ->whereLike('费用类型', '%分摊%') ->select(); } /** * 计算机台分摊金额 */ protected function calculateMachineAllocations(array $utilityData): array { $allocations = []; $machineHours = $this->getMachineHours(); foreach ($utilityData as $item) { $subject = $this->simplifySubjectName($item['科目名称']); $amount = $this->calculateUtilityAmount($item); if ($amount <= 0) { continue; } $this->allocateBySubject($allocations, $subject, $amount, $machineHours); } return $allocations; } /** * 获取机台运行时间 */ protected function getMachineHours(): array { $hours = []; foreach ($this->monthlyCostDetails as $detail) { $machine = $detail['sczl_jtbh']; $hour = floatval($detail['占用机时']); if (!empty($machine)) { $hours[$machine] = ($hours[$machine] ?? 0) + $hour; } } return $hours; } /** * 按科目分摊 */ protected function allocateBySubject( array &$allocations, string $subject, float $amount, array $machineHours ): void { if ($subject === '待分摊总额') { $this->allocateByFloor($allocations, $amount, $machineHours); } elseif (in_array($subject, ['锅炉', '热水锅炉'])) { $this->allocateToRollCoater($allocations, $subject, $amount, $machineHours); } else { $this->allocateGlobally($allocations, $subject, $amount, $machineHours); } } /** * 按楼层分摊 */ protected function allocateByFloor(array &$allocations, float $amount, array $machineHours): void { $floorData = $this->groupMachinesByFloor($machineHours); if ($floorData['totalHours'] <= 0) { return; } foreach ($floorData['floors'] as $floor => $data) { if ($data['hours'] <= 0) { continue; } $floorAmount = $amount * ($data['hours'] / $floorData['totalHours']); $this->allocateToFloorMachines($allocations, $floorAmount, $data['machines'], $machineHours); } } /** * 按楼层分组机台 */ protected function groupMachinesByFloor(array $machineHours): array { $floors = ['1' => ['hours' => 0, 'machines' => []], '2' => ['hours' => 0, 'machines' => []]]; $totalHours = 0; foreach ($machineHours as $machine => $hours) { $floor = $this->getFloorByMachine($machine); if ($floor && isset($floors[$floor])) { $floors[$floor]['hours'] += $hours; $floors[$floor]['machines'][] = $machine; $totalHours += $hours; } } return ['floors' => $floors, 'totalHours' => $totalHours]; } /** * 分摊到楼层机台 */ protected function allocateToFloorMachines( array &$allocations, float $floorAmount, array $machines, array $machineHours ): void { $floorHours = 0; foreach ($machines as $machine) { $floorHours += $machineHours[$machine]; } if ($floorHours <= 0) { return; } foreach ($machines as $machine) { $machineHoursAmount = $machineHours[$machine]; if ($machineHoursAmount <= 0) { continue; } $allocations[$machine]['待分摊总额'] = ($allocations[$machine]['待分摊总额'] ?? 0) + round($floorAmount * ($machineHoursAmount / $floorHours), 2); } } /** * 只分摊到卷凹机组 */ protected function allocateToRollCoater( array &$allocations, string $subject, float $amount, array $machineHours ): void { $rollCoaterMachines = $this->filterRollCoaterMachines(array_keys($machineHours)); $totalHours = 0; foreach ($rollCoaterMachines as $machine) { $totalHours += $machineHours[$machine]; } if ($totalHours <= 0) { return; } foreach ($rollCoaterMachines as $machine) { $machineHoursAmount = $machineHours[$machine]; if ($machineHoursAmount <= 0) { continue; } $allocations[$machine][$subject] = ($allocations[$machine][$subject] ?? 0) + round($amount * ($machineHoursAmount / $totalHours), 2); } } /** * 全局分摊 */ protected function allocateGlobally( array &$allocations, string $subject, float $amount, array $machineHours ): void { $totalHours = array_sum($machineHours); if ($totalHours <= 0) { return; } foreach ($machineHours as $machine => $hours) { if ($hours <= 0) { continue; } $allocations[$machine][$subject] = ($allocations[$machine][$subject] ?? 0) + round($amount * ($hours / $totalHours), 2); } } /** * 根据机台获取楼层 */ protected function getFloorByMachine(string $machine): ?string { $group = Db::name('设备_基本资料') ->where('设备编号', $machine) ->value('设备编组'); if (!$group) { return null; } foreach (self::FLOOR_GROUP_MAP as $floor => $groupNames) { foreach ($groupNames as $groupName) { if (strpos($group, $groupName) !== false) { return $floor; } } } return null; } /** * 筛选卷凹机组的机台 */ protected function filterRollCoaterMachines(array $machines): array { $rollCoater = []; foreach ($machines as $machine) { $group = Db::name('设备_基本资料') ->where('设备编号', $machine) ->value('设备编组'); if ($group && strpos($group, '03、卷凹机组') !== false) { $rollCoater[] = $machine; } } return $rollCoater; } /** * 简化科目名称 */ protected function simplifySubjectName(string $subject): string { foreach (self::SUBJECT_MAPPING as $keyword => $simple) { if (strpos($subject, $keyword) !== false) { return $simple; } } return $subject; } /** * 计算水电金额 */ protected function calculateUtilityAmount(array $item): float { $electricity = $this->calculateElectricityCost($item); $gas = $this->calculateGasCost($item); return $electricity + $gas; } /** * 生成分摊系数记录 */ protected function generateAllocationFactors(array $allocations, string $month, string $sysId): void { $now = date('Y-m-d H:i:s'); foreach ($allocations as $machine => $subjects) { foreach ($subjects as $subject => $amount) { $this->allocationFactors[] = [ 'Sys_ny' => $month, '科目名称' => $subject, '设备编号' => $machine, '分摊系数' => 1, '分摊金额' => $amount, 'Sys_id' => $sysId, 'Sys_rq' => $now, ]; } } } /** * 分配分摊水电 */ protected function allocateApportionedUtilities(array $machineAllocations): void { $machineRates = $this->calculateMachineRates($machineAllocations); foreach ($this->monthlyCostDetails as &$detail) { $machine = $detail['sczl_jtbh']; $hours = floatval($detail['占用机时']); if ($hours <= 0 || !isset($machineRates[$machine])) { continue; } $detail['直接水电'] = round($hours * 0.69, 2); foreach ($machineRates[$machine] as $subject => $rate) { $field = $this->getUtilityFieldName($subject); $detail[$field] = round($hours * $rate, 2); } } } /** * 计算机台每机时费用 */ protected function calculateMachineRates(array $allocations): array { $rates = []; $machineHours = $this->getMachineHours(); foreach ($allocations as $machine => $subjects) { $totalHours = $machineHours[$machine] ?? 0; if ($totalHours <= 0) { continue; } $rates[$machine] = []; foreach ($subjects as $subject => $amount) { $rates[$machine][$subject] = round($amount / $totalHours, 4); } } return $rates; } /** * 获取水电字段名 */ protected function getUtilityFieldName(string $subject): string { $map = [ '待分摊总额' => '分摊水电', '废气处理' => '废气处理', '锅炉' => '锅炉', '空压机' => '空压机', '热水锅炉' => '热水锅炉', '真空鼓风机' => '真空鼓风机', '中央空调' => '中央空调', ]; return $map[$subject] ?? $subject; } /** * 统一保存所有数据 */ protected function saveAllData(string $month, string $sysId): void { // 1. 删除旧数据 $this->deleteOldData($month); // 2. 插入前检查数据结构 $this->validateDataStructure(); // 3. 插入新数据 $this->insertAllData(); } /** * 删除旧数据 */ protected function deleteOldData(string $month): void { Db::name('成本v23_月度成本明细')->where('sys_ny', $month)->delete(); Db::name('成本_各月分摊系数')->where('Sys_ny', $month)->delete(); } /** * 验证数据结构 */ protected function validateDataStructure(): void { if (empty($this->monthlyCostDetails)) { return; } $tableName = '成本v23_月度成本明细'; $columns = Db::query("DESCRIBE `{$tableName}`"); $columnNames = array_column($columns, 'Field'); foreach ($this->monthlyCostDetails as $index => &$row) { if (count($row) !== count($columnNames)) { $this->fixRowData($row, $columnNames, $index); } } } /** * 修复行数据 */ protected function fixRowData(array &$row, array $columnNames, int $index): void { $fixedRow = []; foreach ($columnNames as $column) { $fixedRow[$column] = $row[$column] ?? null; } $this->monthlyCostDetails[$index] = $fixedRow; Log::info("已修复第" . ($index + 1) . "行数据"); } /** * 插入所有数据 */ protected function insertAllData(): void { $this->insertMonthlyCostDetails(); $this->insertAllocationFactors(); } /** * 插入月度成本明细 */ protected function insertMonthlyCostDetails(): void { if (empty($this->monthlyCostDetails)) { return; } $total = count($this->monthlyCostDetails); for ($i = 0; $i < $total; $i += self::BATCH_SIZE) { $batch = array_slice($this->monthlyCostDetails, $i, self::BATCH_SIZE); $this->insertBatch($batch, '成本v23_月度成本明细', $i); } } /** * 插入批次数据 */ protected function insertBatch(array $batch, string $tableName, int $startIndex): void { $firstRow = reset($batch); $fields = array_keys($firstRow); $fieldStr = '`' . implode('`,`', $fields) . '`'; $values = []; foreach ($batch as $row) { $rowValues = []; foreach ($fields as $field) { $value = $row[$field] ?? null; $rowValues[] = is_numeric($value) ? $value : "'" . addslashes($value) . "'"; } $values[] = '(' . implode(',', $rowValues) . ')'; } $sql = "INSERT INTO `{$tableName}` ({$fieldStr}) VALUES " . implode(',', $values); try { Db::execute($sql); Log::info("成功插入批次 " . (($startIndex / self::BATCH_SIZE) + 1)); } catch (\Exception $e) { Log::error("插入批次失败: " . $e->getMessage()); throw $e; } } /** * 插入分摊系数 */ protected function insertAllocationFactors(): void { if (!empty($this->allocationFactors)) { $total = count($this->allocationFactors); for ($i = 0; $i < $total; $i += self::BATCH_SIZE) { $batch = array_slice($this->allocationFactors, $i, self::BATCH_SIZE); $this->insertBatch($batch, '成本_各月分摊系数', $i); } } } /** * 记录成功日志 */ protected function logSuccess(string $month): void { Log::info("成本核算完成", [ 'month' => $month, '月度成本明细记录数' => count($this->monthlyCostDetails), '分摊系数记录数' => count($this->allocationFactors) ]); } /** * 构建成功响应 */ protected function buildSuccessResponse(string $month): array { return [ 'success' => true, 'message' => '成本核算完成', 'month' => $month, 'stats' => [ 'monthly_cost_details' => count($this->monthlyCostDetails), 'allocation_factors' => count($this->allocationFactors) ] ]; } /** * 记录错误日志 */ protected function logError(Exception $e): void { Log::error("统一成本核算失败", [ 'message' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); } /** * 构建错误响应 */ protected function buildErrorResponse(Exception $e): array { return [ 'success' => false, 'message' => '成本核算失败: ' . $e->getMessage(), 'error' => $e->getMessage() ]; } }