OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
283
app/Domain/Bom/Controllers/Api.php
Normal file
283
app/Domain/Bom/Controllers/Api.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Bom\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Leantime\Domain\Bom\Services\Bom as BomService;
|
||||
use Leantime\Domain\Bom\Services\MasterRef as MasterRefService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* BOM JSON API(原生 Laravel 控制器)。每个动作都在 Service 层按 BOM 所属项目自鉴权。
|
||||
*/
|
||||
class Api
|
||||
{
|
||||
public function __construct(
|
||||
private BomService $bomService,
|
||||
private MasterRefService $masterRefService,
|
||||
) {}
|
||||
|
||||
public function detail(int $id): Response
|
||||
{
|
||||
$detail = $this->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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user