bomService->getBomDetail($id); return $detail === false ? response()->json(['status' => 'error', 'message' => 'BOM not found'], 404) : response()->json(['status' => 'success', 'data' => $detail]); } public function store(Request $request): Response { $projectId = (int) $request->input('projectId', session('currentProject')); $id = $this->bomService->createBom($request->all() + ['projectId' => $projectId]); return $id > 0 ? response()->json(['status' => 'success', 'id' => $id]) : response()->json(['status' => 'error', 'message' => 'Create failed'], 500); } public function update(int $id, Request $request): Response { return $this->bomService->updateBom($id, $request->all()) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Update failed'], 500); } public function destroy(int $id): Response { return $this->bomService->deleteBom($id) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Delete failed'], 500); } public function saveItem(int $id, Request $request): Response { $itemId = $request->input('itemId'); $itemId = $itemId !== null && $itemId !== '' ? (int) $itemId : null; $newId = $this->bomService->saveItem($id, $request->all(), $itemId); return $newId > 0 ? response()->json(['status' => 'success', 'itemId' => $newId]) : response()->json(['status' => 'error', 'message' => 'Save item failed'], 500); } public function deleteItem(int $itemId): Response { return $this->bomService->deleteItem($itemId) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Delete item failed'], 500); } public function uploadAttachment(int $id, Request $request): Response { if (! $request->hasFile('file')) { return response()->json(['status' => 'error', 'message' => 'Missing file'], 400); } $result = $this->bomService->uploadAttachment($id, $_FILES); if (is_string($result)) { return response()->json(['status' => 'error', 'message' => $result], 500); } return response()->json(['status' => 'success', 'data' => $result]); } public function addColumn(int $id, Request $request): Response { $key = (string) $request->input('key', ''); $label = (string) $request->input('label', $key); $columnId = $this->bomService->addColumn($id, $key, $label); return $columnId > 0 ? response()->json(['status' => 'success', 'columnId' => $columnId]) : response()->json(['status' => 'error', 'message' => 'Add column failed'], 500); } public function deleteColumn(int $columnId): Response { return $this->bomService->deleteColumn($columnId) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Delete column failed'], 500); } public function setHiddenColumns(int $id, Request $request): Response { $keys = $request->input('keys', []); $keys = is_array($keys) ? $keys : []; return $this->bomService->setHiddenColumns($id, $keys) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Save column visibility failed'], 500); } public function export(int $id, Request $request): Response { $format = strtolower((string) $request->input('format', 'xlsx')); $result = $this->bomService->export($id, $format); if ($result === false) { return response()->json(['status' => 'error', 'message' => 'Export failed'], 500); } $content = file_get_contents($result['path']); @unlink($result['path']); return response($content, 200, [ 'Content-Type' => $result['contentType'], 'Content-Disposition' => 'attachment; filename="'.$result['filename'].'"', ]); } public function exportTemplate(int $id): Response { $result = $this->bomService->exportTemplate($id); if ($result === false) { return response()->json(['status' => 'error', 'message' => 'Export failed'], 500); } $content = file_get_contents($result['path']); @unlink($result['path']); return response($content, 200, [ 'Content-Type' => $result['contentType'], 'Content-Disposition' => 'attachment; filename="'.$result['filename'].'"', ]); } public function addSource(int $id, Request $request): Response { $sourceId = $this->bomService->addSource($id, $request->all()); return $sourceId > 0 ? response()->json(['status' => 'success', 'sourceId' => $sourceId]) : response()->json(['status' => 'error', 'message' => 'Add source failed'], 500); } public function deleteSource(int $sourceId): Response { return $this->bomService->deleteSource($sourceId) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Delete source failed'], 500); } public function parseTeable(Request $request): Response { $text = (string) $request->input('text', ''); $result = $this->bomService->parseTeableText($text); return $result['success'] ? response()->json(['status' => 'success', 'data' => $result]) : response()->json(['status' => 'error', 'message' => $result['message'] ?? '解析失败'], 400); } public function importTeablePaste(int $id, Request $request): Response { $text = (string) $request->input('text', ''); $result = $this->bomService->importFromTeableText($id, $text); return $result['success'] ? response()->json(['status' => 'success', 'imported' => $result['imported'] ?? 0, 'errors' => $result['errors'] ?? [], 'source' => $result['source'] ?? null]) : response()->json(['status' => 'error', 'message' => $result['message'] ?? '导入失败'], 500); } public function importTeable(int $id): Response { $result = $this->bomService->importFromTeable($id); return $result['success'] ? response()->json(['status' => 'success', 'imported' => $result['imported'] ?? 0, 'errors' => $result['errors'] ?? []]) : response()->json(['status' => 'error', 'message' => $result['message'] ?? 'Import failed'], 500); } public function importExcel(int $id, Request $request): Response { if (! $request->hasFile('file')) { return response()->json(['status' => 'error', 'message' => 'Missing file'], 400); } $file = $request->file('file'); $ext = strtolower($file->getClientOriginalExtension() ?: 'xlsx'); // tempnam 会创建一个无扩展名的空文件;我们拼接扩展名得到目标路径, // 目标文件本身不存在,直接写入内容即可(避免 move 到已存在路径的兼容问题)。 $tmp = tempnam(sys_get_temp_dir(), 'bom.'); $target = $tmp.'.'.$ext; try { file_put_contents($target, $file->getContent()); $result = $this->bomService->importFromExcel($id, $target); } catch (\Throwable $e) { @unlink($tmp); @unlink($target); return response()->json(['status' => 'error', 'message' => $e->getMessage()], 500); } // 清理临时文件(tempnam 的空文件 + 带扩展名的数据文件) @unlink($tmp); @unlink($target); return $result['success'] ? response()->json(['status' => 'success', 'imported' => $result['imported'] ?? 0]) : response()->json(['status' => 'error', 'message' => $result['message'] ?? 'Import failed'], 500); } // ---- 全局主数据:项目引用层(单向,不写回全局) ---- public function linkMaster(Request $request): Response { $projectId = (int) $request->input('projectId', session('currentProject')); $masterId = (int) $request->input('masterId', 0); $refId = $this->masterRefService->link($projectId, $masterId); return $refId > 0 ? response()->json(['status' => 'success', 'refId' => $refId]) : response()->json(['status' => 'error', 'message' => 'Link failed'], 500); } public function unlinkMaster(int $refId): Response { return $this->masterRefService->unlink($refId) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Unlink failed'], 500); } public function refDetail(int $refId): Response { $detail = $this->masterRefService->getRefDetail($refId); return $detail === false ? response()->json(['status' => 'error', 'message' => 'Ref not found'], 404) : response()->json(['status' => 'success', 'data' => $detail]); } public function addRefColumn(int $refId, Request $request): Response { $key = (string) $request->input('key', ''); $label = (string) $request->input('label', $key); $columnId = $this->masterRefService->addColumn($refId, $key, $label); return $columnId > 0 ? response()->json(['status' => 'success', 'columnId' => $columnId]) : response()->json(['status' => 'error', 'message' => 'Add column failed'], 500); } public function deleteRefColumn(int $columnId): Response { return $this->masterRefService->deleteColumn($columnId) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Delete column failed'], 500); } public function saveRefValue(int $refId, Request $request): Response { $columnId = (int) $request->input('columnId', 0); $itemId = (int) $request->input('itemId', 0); $value = (string) $request->input('value', ''); return $this->masterRefService->saveValue($refId, $columnId, $itemId, $value) ? response()->json(['status' => 'success']) : response()->json(['status' => 'error', 'message' => 'Save value failed'], 500); } }