初始化
缺陷:主题配色需要优化整体的同风格
This commit is contained in:
203
app/Service/DepartmentService.php
Normal file
203
app/Service/DepartmentService.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user