dootask/app/Module/ContactsDepartment.php
kuaifan ca1a1b5415 feat(contacts): 通讯录支持按部门显示,成员按需加载
- 新增 contacts/departments 接口,返回部门树及成员数
- 通讯录搜索框右侧新增字母/部门视图切换,部门视图可折叠,展开时才加载成员
- users/search 的 keys.departments 改为参数绑定(修复 SQL 注入),0 表示未加入任何部门
- 同步翻译与 ai-kb
2026-10-10 04:47:34 +00:00

91 lines
3.1 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\Module;
use App\Models\User;
use App\Models\UserDepartment;
/**
* 通讯录部门树
*/
class ContactsDepartment
{
/**
* 构建通讯录部门树(深度优先展开的扁平列表)
*
* 成员口径与 users/search 默认一致:未禁用、非机器人。
* - member_count: 直属成员数
* - total_count: 含全部下级部门的成员数(去重)
*
* @return array{total: int, no_department_count: int, departments: array}
*/
public static function tree(): array
{
$departments = UserDepartment::select(['id', 'name', 'parent_id', 'owner_userid'])->orderBy('id')->get()->keyBy('id');
// 部门 => 直属成员
$members = [];
$noDepartmentCount = 0;
$total = 0;
User::whereNull('disable_at')->where('bot', 0)->pluck('department', 'userid')->each(function ($department, $userid) use ($departments, &$members, &$noDepartmentCount, &$total) {
$total++;
// pluck 会经过 User::getDepartmentAttribute,得到的已是 int 数组
$ids = array_filter((array)$department, fn($id) => $departments->has($id));
if (empty($ids)) {
$noDepartmentCount++;
return;
}
foreach ($ids as $id) {
$members[$id][$userid] = true;
}
});
// 上级部门不存在时视为顶级
$children = [];
foreach ($departments as $department) {
$parentId = $departments->has($department->parent_id) && $department->parent_id != $department->id ? $department->parent_id : 0;
$children[$parentId][] = $department->id;
}
$list = [];
$visited = [];
$walk = function ($id, $level) use (&$walk, &$list, &$visited, &$children, &$members, $departments) {
$visited[$id] = true;
$department = $departments[$id];
$index = count($list);
$list[] = [
'id' => $department->id,
'name' => $department->name,
'parent_id' => (int)$department->parent_id,
'owner_userid' => (int)$department->owner_userid,
'level' => $level,
'member_count' => count($members[$id] ?? []),
'total_count' => 0,
];
$all = $members[$id] ?? [];
foreach ($children[$id] ?? [] as $childId) {
if (isset($visited[$childId])) {
continue; // 防止异常数据形成环
}
$all += $walk($childId, $level + 1);
}
$list[$index]['total_count'] = count($all);
return $all;
};
foreach ($children[0] ?? [] as $rootId) {
$walk($rootId, 0);
}
foreach ($departments as $id => $department) {
if (!isset($visited[$id])) {
$walk($id, 0); // 异常数据形成环时兜底,避免部门丢失
}
}
return [
'total' => $total,
'no_department_count' => $noDepartmentCount,
'departments' => $list,
];
}
}