153 lines
4.3 KiB
PHP
153 lines
4.3 KiB
PHP
<?php
|
||
|
||
namespace Leantime\Domain\Bom\Services;
|
||
|
||
use Leantime\Core\Domains\BaseService;
|
||
use Leantime\Domain\Bom\Permissions\BomPermissions;
|
||
use Leantime\Domain\Bom\Repositories\Bom as BomRepository;
|
||
use Leantime\Domain\Bom\Repositories\MasterRef as MasterRefRepository;
|
||
|
||
/**
|
||
* 项目 ↔ 全局主数据(BOM/工艺文件/工具清单)引用服务。
|
||
*
|
||
* 单向引用:项目引用主数据后追加的项目级列/值只存引用层,绝不写回全局主数据。
|
||
*/
|
||
class MasterRef extends BaseService
|
||
{
|
||
public function __construct(
|
||
protected MasterRefRepository $repo,
|
||
protected BomRepository $bomRepo,
|
||
) {}
|
||
|
||
/**
|
||
* 项目已引用的主数据列表。
|
||
*/
|
||
public function getRefs(int $projectId): array
|
||
{
|
||
return $this->repo->getRefsByProject($projectId);
|
||
}
|
||
|
||
/**
|
||
* 项目引用一个主数据(幂等:已引用则返回已有 refId)。
|
||
*/
|
||
public function link(int $projectId, int $masterId): int
|
||
{
|
||
$this->authorize(BomPermissions::EDIT, $projectId);
|
||
|
||
$master = $this->bomRepo->getBom($masterId);
|
||
if ($master === false) {
|
||
return 0;
|
||
}
|
||
|
||
$existing = $this->repo->findRef($projectId, $masterId);
|
||
if ($existing !== false) {
|
||
return (int) $existing['id'];
|
||
}
|
||
|
||
return $this->repo->createRef([
|
||
'projectId' => $projectId,
|
||
'masterId' => $masterId,
|
||
'createdOn' => now(),
|
||
]);
|
||
}
|
||
|
||
public function unlink(int $refId): bool
|
||
{
|
||
$ref = $this->repo->getRef($refId);
|
||
if ($ref === false) {
|
||
return false;
|
||
}
|
||
$this->authorize(BomPermissions::EDIT, (int) $ref['projectId']);
|
||
|
||
return $this->repo->deleteRef($refId);
|
||
}
|
||
|
||
/**
|
||
* 项目级引用详情:主数据明细 + 项目级追加列 + 项目级追加值。
|
||
*/
|
||
public function getRefDetail(int $refId): array|false
|
||
{
|
||
$ref = $this->repo->getRef($refId);
|
||
if ($ref === false) {
|
||
return false;
|
||
}
|
||
$this->authorize(BomPermissions::VIEW, (int) $ref['projectId']);
|
||
|
||
$columns = $this->repo->getColumns($refId);
|
||
$values = $this->repo->getValues($refId);
|
||
|
||
// 值按 itemId -> [columnId -> value] 组织
|
||
$valueMap = [];
|
||
foreach ($values as $v) {
|
||
$valueMap[(int) $v['itemId']][(int) $v['columnId']] = (string) $v['value'];
|
||
}
|
||
|
||
return [
|
||
'ref' => $ref,
|
||
'columns' => $columns,
|
||
'values' => $valueMap,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 项目级追加列。
|
||
*/
|
||
public function addColumn(int $refId, string $key, string $label): int
|
||
{
|
||
$ref = $this->repo->getRef($refId);
|
||
if ($ref === false) {
|
||
return 0;
|
||
}
|
||
$this->authorize(BomPermissions::EDIT, (int) $ref['projectId']);
|
||
|
||
$key = trim(str_replace([' ', '/', '\\'], '_', $key));
|
||
if ($key === '') {
|
||
return 0;
|
||
}
|
||
|
||
foreach ($this->repo->getColumns($refId) as $col) {
|
||
if ($col['key'] === $key) {
|
||
return (int) $col['id'];
|
||
}
|
||
}
|
||
|
||
$maxSort = 0;
|
||
foreach ($this->repo->getColumns($refId) as $col) {
|
||
$maxSort = max($maxSort, (int) $col['sortOrder']);
|
||
}
|
||
|
||
return $this->repo->addColumn([
|
||
'refId' => $refId,
|
||
'key' => $key,
|
||
'label' => trim($label) ?: $key,
|
||
'sortOrder' => $maxSort + 1,
|
||
]);
|
||
}
|
||
|
||
public function deleteColumn(int $columnId): bool
|
||
{
|
||
// 按 columnId 找 refId 以完成鉴权
|
||
$ref = $this->repo->getRefByColumnId($columnId);
|
||
if ($ref === false) {
|
||
return false;
|
||
}
|
||
$this->authorize(BomPermissions::EDIT, (int) $ref['projectId']);
|
||
|
||
return $this->repo->deleteColumn($columnId);
|
||
}
|
||
|
||
/**
|
||
* 保存项目级追加值(不影响全局主数据)。
|
||
*/
|
||
public function saveValue(int $refId, int $columnId, int $itemId, string $value): bool
|
||
{
|
||
$ref = $this->repo->getRef($refId);
|
||
if ($ref === false) {
|
||
return false;
|
||
}
|
||
$this->authorize(BomPermissions::EDIT, (int) $ref['projectId']);
|
||
|
||
return $this->repo->setValue($refId, $columnId, $itemId, $value);
|
||
}
|
||
}
|