Files
lgp-admin-plus-api/app/Service/DepartmentService.php
LQ bcf54c2727 初始化
缺陷:主题配色需要优化整体的同风格
2026-08-14 23:21:21 +08:00

204 lines
6.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Service;
use App\BaseApp\BaseService;
use App\Models\AdminModel;
use App\Models\business\DepartmentModel;
use Exception;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* 部门服务
*
* 老后台的 department/option 返回的是扁平列表,前端 BasicTree 拿到后其实渲染不出层级,
* pid 白存了。这里 option 直接返回真正的树,账号列表左侧的部门树才能用。
*/
class DepartmentService extends BaseService
{
public function __construct()
{
parent::__construct();
$this->model = DepartmentModel::class;
$this->selectField = ['id', 'name', 'desc', 'status', 'pid', 'color', 'created_at', 'updated_at'];
$this->queryField = ['name' => 'like', 'status' => '=', 'pid' => '='];
$this->orderBy = ['name' => 'id', 'sort' => 'asc'];
}
/**
* 分页列表,附带每个部门的账号数
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function list(): array
{
$result = $this->getPageList();
$ids = array_column($result['items'] ?? [], 'id');
$counts = $this->countAdminByDepartment($ids);
foreach ($result['items'] as &$item) {
$item['admin_count'] = $counts[$item['id']] ?? 0;
}
unset($item);
return $result;
}
/**
* 部门树(账号列表左侧筛选树、部门表单的上级选择都用这个)
*/
public function option(): array
{
$rows = DepartmentModel::where('deleted_at', 0)
->orderBy('id')
->get(['id', 'name', 'pid', 'status', 'color'])
->toArray();
return $this->utils->tree($rows);
}
/**
* 部门树下拉(部门表单选上级用)。带 $isSelect 时首位补「顶级部门」
*/
public function getTreeOption(bool $isSelect = false): array
{
$rows = DepartmentModel::where('deleted_at', 0)
->orderBy('id')
->get(['id', 'name', 'pid'])
->toArray();
$result = $this->utils->tree($rows);
if ($isSelect) {
array_unshift($result, ['id' => 0, 'name' => '顶级部门', 'pid' => 0]);
}
return $result;
}
/**
* @throws Exception
*/
public function detail($id): mixed
{
return $this->getDetail($id);
}
/**
* 新增部门
*/
public function create($params): mixed
{
$this->assertNameUnique((string) ($params['name'] ?? ''), (int) ($params['pid'] ?? 0));
return $this->insert($params);
}
/**
* 编辑部门
* @throws Exception
*/
public function update($id, $params): mixed
{
$id = (int) $id;
if (array_key_exists('name', $params)) {
$this->assertNameUnique((string) $params['name'], (int) ($params['pid'] ?? 0), $id);
}
// 上级不能指向自己或自己的子孙,否则树会成环、递归建树直接栈溢出
if (array_key_exists('pid', $params)) {
$pid = (int) $params['pid'];
if ($pid === $id) {
$this->utils->errorThrow('上级部门不能是自己');
}
if ($pid > 0 && in_array($pid, $this->descendantIds($id), true)) {
$this->utils->errorThrow('上级部门不能是自己的下级');
}
}
return $this->save($id, $params);
}
/**
* 删除部门:有子部门或仍有账号在用时拒绝,避免留下悬空引用
* @throws Exception
*/
public function delete($id): mixed
{
$ids = array_values(array_filter(array_map('intval', is_array($id) ? $id : [$id])));
if (empty($ids)) {
$this->utils->errorThrow('参数错误');
}
$hasChild = DepartmentModel::whereIn('pid', $ids)->where('deleted_at', 0)->exists();
if ($hasChild) {
$this->utils->errorThrow('存在下级部门,请先删除下级');
}
$inUse = AdminModel::whereIn('department_id', $ids)->where('deleted_at', 0)->exists();
if ($inUse) {
$this->utils->errorThrow('仍有账号属于该部门,请先调整账号所属部门');
}
return $this->del($ids);
}
/**
* 停用/启用部门
* @throws Exception
*/
public function status($id, $status): mixed
{
return $this->save((int) $id, ['status' => (int) $status]);
}
/**
* 同一父级下部门名不允许重复,否则树上出现两个同名节点没法分辨
*/
private function assertNameUnique(string $name, int $pid, int $exceptId = 0): void
{
$name = trim($name);
if ($name === '') {
$this->utils->errorThrow('部门名称不能为空');
}
$exists = DepartmentModel::where('name', $name)
->where('pid', $pid)
->where('deleted_at', 0)
->when($exceptId > 0, fn ($q) => $q->where('id', '<>', $exceptId))
->exists();
if ($exists) {
$this->utils->errorThrow('同级下已存在同名部门');
}
}
/**
* 取某部门的全部子孙 id用于成环校验
*/
private function descendantIds(int $id): array
{
$all = DepartmentModel::where('deleted_at', 0)->get(['id', 'pid']);
$childMap = [];
foreach ($all as $row) {
$childMap[(int) $row->pid][] = (int) $row->id;
}
$result = [];
$stack = $childMap[$id] ?? [];
while (!empty($stack)) {
$current = array_pop($stack);
if (in_array($current, $result, true)) {
continue;
}
$result[] = $current;
foreach ($childMap[$current] ?? [] as $child) {
$stack[] = $child;
}
}
return $result;
}
/**
* 部门账号数admin 在 mysql 连接、department 在 business 连接,跨连接不能 join分两次查
*/
private function countAdminByDepartment(array $departmentIds): array
{
if (empty($departmentIds)) {
return [];
}
return AdminModel::whereIn('department_id', $departmentIds)
->where('deleted_at', 0)
->groupBy('department_id')
->selectRaw('department_id, COUNT(*) AS c')
->pluck('c', 'department_id')
->all();
}
}