normalizeMonth($params['month'] ?? null); $days = (int) date('t', strtotime($month . '-01')); $start = strtotime($month . '-01 00:00:00'); $end = strtotime(date('Y-m-t 23:59:59', $start)); $rows = $this->collectRows($params, $start, $end, static fn (string $column): string => "DATE_FORMAT(FROM_UNIXTIME({$column}), '%Y-%m-%d')"); return $this->formatRows($rows, $days, static fn (int $index): string => (string) $index); } public function daily(array $params): array { $date = $this->normalizeDate($params['date'] ?? null); $start = strtotime($date . ' 00:00:00'); $end = strtotime($date . ' 23:59:59'); $rows = $this->collectRows($params, $start, $end, static fn (string $column): string => "HOUR(FROM_UNIXTIME({$column}))"); return $this->formatRows($rows, 24, static fn (int $index): string => str_pad((string) $index, 2, '0', STR_PAD_LEFT) . ':00'); } private function collectRows(array $params, int $start, int $end, callable $periodExpression): array { $dataType = (string) ($params['data_type'] ?? 'all'); $rows = []; if ($dataType === 'all' || $dataType === 'order') { $rows = array_merge($rows, $this->queryScanData($params, $start, $end, $periodExpression)->get()->all()); } if ($dataType === 'all' || $dataType === 'machine') { $rows = array_merge($rows, $this->queryProductRecord($params, $start, $end, $periodExpression)->get()->all()); } if ($dataType === 'all' || $dataType === 'exception') { $rows = array_merge($rows, $this->queryExceptionRecord($params, $start, $end, $periodExpression)->get()->all()); } if ($dataType === 'all' || $dataType === 'repair') { $rows = array_merge($rows, $this->queryRepairRecord($params, $start, $end, $periodExpression)->get()->all()); } return $rows; } private function queryExceptionRecord(array $params, int $start, int $end, callable $periodExpression): Builder { $model = new ExceptionRecord(); $connection = $model->getConnection(); $table = $model->getTable(); $processTable = 'produce_process'; $stepTable = 'produce_process_step'; $query = $connection->table($table) ->leftJoin($processTable, $processTable . '.id', '=', $table . '.process_id') ->leftJoin($stepTable, $stepTable . '.id', '=', $table . '.step_id') ->whereBetween($table . '.reported_at', [$start, $end]) ->whereNull($table . '.deleted_at'); $this->applyStatisticsFilters($query, $params, $table, $processTable, $stepTable, 'reporter'); return $query->select([$table . '.user_code', $table . '.reporter as operator']) ->selectRaw($periodExpression($connection->getTablePrefix() . $table . '.reported_at') . ' as period') ->selectRaw($connection->getTablePrefix() . $processTable . '.process_name as process_name') ->selectRaw($connection->getTablePrefix() . $stepTable . '.step_name as step_name') ->selectRaw('COUNT(*) as amount') ->groupBy('user_code', 'operator', 'process_name', 'step_name', 'period'); } private function queryRepairRecord(array $params, int $start, int $end, callable $periodExpression): Builder { $model = new RepairRecord(); $exceptionModel = new ExceptionRecord(); $connection = $model->getConnection(); $table = $model->getTable(); $exceptionTable = $exceptionModel->getTable(); $query = $connection->table($table) ->leftJoin($exceptionTable, $exceptionTable . '.id', '=', $table . '.exception_id') ->whereBetween($table . '.repaired_at', [$start, $end]) ->whereNull($table . '.deleted_at'); foreach (['process_id', 'step_id'] as $field) { if (($params[$field] ?? '') !== '') { $query->where($table . '.' . $field, $params[$field]); } } foreach (['process_name', 'step_name'] as $field) { if (($params[$field] ?? '') !== '') { $query->where($table . '.' . $field, 'like', '%' . $params[$field] . '%'); } } if (($params['user_code'] ?? '') !== '') { $query->where($exceptionTable . '.user_code', 'like', '%' . $params['user_code'] . '%'); } if (($params['operator'] ?? '') !== '') { $query->where($table . '.operator', 'like', '%' . $params['operator'] . '%'); } return $query->select([$exceptionTable . '.user_code', $table . '.operator']) ->selectRaw($periodExpression($connection->getTablePrefix() . $table . '.repaired_at') . ' as period') ->selectRaw("COALESCE(NULLIF({$connection->getTablePrefix()}{$table}.process_name, ''), '历史数据未记录') as process_name") ->selectRaw("COALESCE(NULLIF({$connection->getTablePrefix()}{$table}.step_name, ''), '历史数据未记录') as step_name") ->selectRaw('COUNT(*) as amount') ->groupBy('user_code', 'operator', 'process_name', 'step_name', 'period'); } private function queryScanData(array $params, int $start, int $end, callable $periodExpression): Builder { $scanDataModel = new ScanData(); $connection = $scanDataModel->getConnection(); $scanDataTable = $scanDataModel->getTable(); $processTable = 'produce_process'; $stepTable = 'produce_process_step'; $query = $connection->table($scanDataTable) ->leftJoin($processTable, $processTable . '.id', '=', $scanDataTable . '.process_id') ->leftJoin($stepTable, $stepTable . '.id', '=', $scanDataTable . '.step_id') ->whereBetween($scanDataTable . '.scan_time', [$start, $end]) ->whereNull($scanDataTable . '.deleted_at'); foreach (['process_id', 'step_id'] as $field) { if (($params[$field] ?? '') !== '') { $query->where($scanDataTable . '.' . $field, $params[$field]); } } foreach (['user_code', 'operator'] as $field) { if (($params[$field] ?? '') !== '') { $query->where($scanDataTable . '.' . $field, 'like', '%' . $params[$field] . '%'); } } foreach (['process_name' => $processTable . '.process_name', 'step_name' => $stepTable . '.step_name'] as $field => $column) { if (($params[$field] ?? '') !== '') { $query->where($column, 'like', '%' . $params[$field] . '%'); } } return $query->select([ $scanDataTable . '.user_code', $scanDataTable . '.operator', $connection->raw($connection->getTablePrefix() . $processTable . '.process_name as process_name'), $connection->raw($connection->getTablePrefix() . $stepTable . '.step_name as step_name'), ]) ->selectRaw($periodExpression($connection->getTablePrefix() . $scanDataTable . '.scan_time') . ' as period') ->selectRaw('SUM(' . $connection->getTablePrefix() . $scanDataTable . '.quantity) as amount') ->groupBy('user_code', 'operator', 'process_name', 'step_name', 'period') ->orderBy('user_code') ->orderBy('process_name') ->orderBy('step_name'); } private function queryProductRecord(array $params, int $start, int $end, callable $periodExpression): Builder { $productRecordModel = new ProductRecord(); $connection = $productRecordModel->getConnection(); $productRecordTable = $productRecordModel->getTable(); $processTable = 'produce_process'; $stepTable = 'produce_process_step'; $query = $connection->table($productRecordTable) ->leftJoin($processTable, $processTable . '.id', '=', $productRecordTable . '.process_id') ->leftJoin($stepTable, $stepTable . '.id', '=', $productRecordTable . '.step_id') ->whereBetween($productRecordTable . '.created_at', [$start, $end]) ->whereNull($productRecordTable . '.deleted_at'); foreach (['process_id', 'step_id', 'scan_result'] as $field) { if (($params[$field] ?? '') !== '') { $query->where($productRecordTable . '.' . $field, $params[$field]); } } foreach (['user_code', 'operator'] as $field) { if (($params[$field] ?? '') !== '') { $query->where($productRecordTable . '.' . $field, 'like', '%' . $params[$field] . '%'); } } foreach (['process_name' => $processTable . '.process_name', 'step_name' => $stepTable . '.step_name'] as $field => $column) { if (($params[$field] ?? '') !== '') { $query->where($column, 'like', '%' . $params[$field] . '%'); } } return $query->select([ $productRecordTable . '.user_code', $productRecordTable . '.operator', $connection->raw($connection->getTablePrefix() . $processTable . '.process_name as process_name'), $connection->raw($connection->getTablePrefix() . $stepTable . '.step_name as step_name'), ]) ->selectRaw($periodExpression($connection->getTablePrefix() . $productRecordTable . '.created_at') . ' as period') ->selectRaw('SUM(' . $connection->getTablePrefix() . $productRecordTable . '.quantity) as amount') ->groupBy('user_code', 'operator', 'process_name', 'step_name', 'period') ->orderBy('user_code') ->orderBy('process_name') ->orderBy('step_name'); } private function applyStatisticsFilters(Builder $query, array $params, string $table, string $processTable, string $stepTable, string $operatorField): void { foreach (['process_id', 'step_id', 'scan_result'] as $field) { if (($params[$field] ?? '') !== '') { $query->where($table . '.' . $field, $params[$field]); } } if (($params['user_code'] ?? '') !== '') { $query->where($table . '.user_code', 'like', '%' . $params['user_code'] . '%'); } if (($params['operator'] ?? '') !== '') { $query->where($table . '.' . $operatorField, 'like', '%' . $params['operator'] . '%'); } foreach (['process_name' => $processTable . '.process_name', 'step_name' => $stepTable . '.step_name'] as $field => $column) { if (($params[$field] ?? '') !== '') { $query->where($column, 'like', '%' . $params[$field] . '%'); } } } private function matchesRepairFlow(array $params, array $flow): bool { if (($params['process_id'] ?? '') !== '' && (string) $params['process_id'] !== (string) $flow['process_id']) { return false; } if (($params['step_id'] ?? '') !== '' && (string) $params['step_id'] !== (string) $flow['step_id']) { return false; } if (($params['process_name'] ?? '') !== '' && !str_contains($flow['process_name'], (string) $params['process_name'])) { return false; } return ($params['step_name'] ?? '') === '' || str_contains($flow['step_name'], (string) $params['step_name']); } private function getRepairFlow(): array { if ($this->repairFlow !== null) { return $this->repairFlow; } $configs = ProduceConfig::query() ->whereIn('group_code', ['basic_setting', 'repair_flow']) ->whereIn('code', ['repair_process_id', 'repair_step_id']) ->get(['group_code', 'code', 'content']) ->groupBy('group_code'); $basic = $configs->get('basic_setting', collect())->pluck('content', 'code'); $flow = $configs->get('repair_flow', collect())->pluck('content', 'code'); $processId = (int) (($basic['repair_process_id'] ?? '') ?: ($flow['repair_process_id'] ?? 0)); $stepId = (int) (($basic['repair_step_id'] ?? '') ?: ($flow['repair_step_id'] ?? 0)); $process = $processId > 0 ? Process::query()->withTrashed()->find($processId) : null; $step = $stepId > 0 ? ProcessStep::query()->withTrashed()->find($stepId) : null; return $this->repairFlow = [ 'process_id' => $processId, 'step_id' => $stepId, 'process_name' => (string) ($process?->process_name ?? '维修'), 'step_name' => (string) ($step?->step_name ?? '维修'), ]; } private function formatRows(iterable $rows, int $periodCount, callable $periodLabel): array { $grouped = []; $summaryRows = []; foreach ($rows as $row) { $productionProcess = $this->formatProductionProcess((string) $row->process_name, (string) $row->step_name); $key = implode('|', [(string) $row->user_code, (string) $row->operator, (string) $row->process_name, (string) $row->step_name]); if (!isset($grouped[$key])) { $grouped[$key] = [ 'user_code' => $row->user_code, 'operator' => $row->operator, 'production_process' => $productionProcess, 'process_name' => $row->process_name, 'step_name' => $row->step_name, 'total' => 0, ]; for ($index = 1; $index <= $periodCount; $index++) { $grouped[$key][$periodLabel($index)] = 0; } } if (!isset($summaryRows[$productionProcess])) { $summaryRows[$productionProcess] = [ 'user_code' => '总计', 'operator' => '', 'production_process' => $productionProcess, 'process_name' => $row->process_name, 'step_name' => $row->step_name, 'total' => 0, ]; for ($index = 1; $index <= $periodCount; $index++) { $summaryRows[$productionProcess][$periodLabel($index)] = 0; } } $period = (string) $row->period; $period = $periodCount === 24 ? str_pad($period, 2, '0', STR_PAD_LEFT) . ':00' : (string) ((int) substr($period, -2)); if (array_key_exists($period, $grouped[$key])) { $amount = (int) $row->amount; $grouped[$key][$period] += $amount; $grouped[$key]['total'] += $amount; $summaryRows[$productionProcess][$period] += $amount; $summaryRows[$productionProcess]['total'] += $amount; } } $items = array_values($grouped); if ($items !== []) { $items = array_merge($items, array_values($summaryRows)); } return $items; } private function formatProductionProcess(string $processName, string $stepName): string { if ($processName !== '' && $stepName !== '') { return $processName . '-' . $stepName; } return $processName !== '' ? $processName : $stepName; } private function normalizeMonth(mixed $month): string { $month = is_string($month) && preg_match('/^\d{4}-\d{2}$/', $month) ? $month : date('Y-m'); return date('Y-m', strtotime($month . '-01')); } private function normalizeDate(mixed $date): string { $date = is_string($date) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) ? $date : date('Y-m-d'); return date('Y-m-d', strtotime($date)); } }