Files

776 lines
23 KiB
Go

// package system
// @Link https://github.com/xiujiecn/xiu-admin
// @Copyright Copyright (c) 2025 LiXiujie
// @Author Lxj <xxx@example.com>
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
package system
import (
"context"
"fmt"
"sort"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/event"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/request"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)
type sSysDept struct {
}
func NewSysDept() *sSysDept {
return &sSysDept{}
}
func init() {
service.RegisterSysDept(NewSysDept())
}
func (l *sSysDept) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
if len(option) == 0 {
option = append(option, &handler.Option{
FilterTenant: true,
FilterAuth: true,
})
}
return handler.Model(dao.SysDept.Ctx(ctx), option...)
}
func (l *sSysDept) ModelQuery(ctx context.Context, option ...*handler.Option) *gdb.Model {
if len(option) == 0 {
option = append(option, &handler.Option{
FilterTenant: true,
FilterAuth: true,
})
}
return handler.Model(service.MemoryDB().DB(ctx).Ctx(ctx).Model(dao.SysDept.Table()), option...)
}
// GetDeptList 获取部门列表
func (l *sSysDept) GetDeptList(ctx context.Context, query model.SysDeptListParam) (items []*model.SysDeptListModel, total int, err error) {
m := l.ModelQuery(ctx)
if query.DeptName != "" {
m = m.WhereLike(dao.SysDept.Columns().DeptName, "%"+query.DeptName+"%")
}
if query.Status != "" {
m = m.Where(dao.SysDept.Columns().Status, query.Status)
}
if query.DeptType != nil {
m = m.Where(dao.SysDept.Columns().DeptType, *query.DeptType)
}
// 排序:按照显示顺序OrderNum从小到大排序,如果相同则按照部门ID从小到大排序
err = m.Page(1, 5000).Order(dao.SysDept.Columns().OrderNum, "ASC").Order(dao.SysDept.Columns().DeptId, "ASC").Scan(&items)
if err != nil {
return nil, 0, err
}
total = len(items)
return
}
// GetDeptById 获取部门详情
func (l *sSysDept) GetDeptById(ctx context.Context, id int64) (dept *model.SysDeptViewModel, err error) {
user := contexts.GetUser(ctx)
if user == nil {
return nil, gerror.NewCode(consts.CodeLoginExpired, "登录已过期")
}
mod := l.ModelQuery(ctx).Where(dao.SysDept.Columns().DeptId, id)
mod = mod.FieldsPrefix(dao.SysDept.Table(), model.SysDeptViewModel{})
err = mod.Scan(&dept)
if err != nil {
return nil, err
}
return dept, nil
}
// 构建树结构
func (l *sSysDept) DeptTree(ctx context.Context, parentDept *model.SysDeptTreeModel, deptList []*model.SysDeptListModel, ancestors string) (data []*model.SysDeptTreeModel, err error) {
data = make([]*model.SysDeptTreeModel, 0)
for _, dept := range deptList {
pId := int64(0)
if parentDept != nil {
pId = parentDept.DeptId
}
if dept.ParentId == pId {
item := &model.SysDeptTreeModel{
DeptId: dept.DeptId,
ParentId: dept.ParentId,
DeptName: dept.DeptName,
Key: fmt.Sprintf("%d", dept.DeptId),
Children: nil,
Ancestors: ancestors,
DeptType: dept.DeptType,
}
subDept, err := l.DeptTree(ctx, item, deptList, ancestors+","+fmt.Sprintf("%d", dept.DeptId))
if err != nil {
return nil, err
}
item.Children = subDept
data = append(data, item)
}
}
return
}
// GetDeptFullTree 组织管理页专用的完整部门树。
//
// 与 GetDeptTree 的区别:
// 1. 支持按组织名称模糊搜索、按状态/类型筛选(复用 GetDeptList 的过滤能力),
// 而 GetDeptTree 只支持 deptType 一个维度;
// 2. 节点携带 SysDeptListModel 的全部字段(含 deptCategory/status/createdAt),
// 供表格展示。
//
// 同样由后端一次建好嵌套 children 返回,前端无需再开 treeConfig.transform。
func (l *sSysDept) GetDeptFullTree(ctx context.Context, query model.SysDeptFullTreeParam) (items []*model.SysDeptFullTreeModel, err error) {
depts, _, err := l.GetDeptList(ctx, query.SysDeptListParam)
if err != nil {
return nil, err
}
// 带过滤条件时,命中的节点其父级可能被 WhereLike/Where 滤掉。
// 补齐这些祖先节点,使搜索结果仍呈现完整的层级链路(与前端
// filterDeptTree 的「命中节点保留整棵子树」语义一致),
// 否则用户看到的是一个parentId 悬空的孤立行,看不出它归属谁。
depts, err = l.withDeptAncestors(ctx, depts)
if err != nil {
return nil, err
}
if err = validateDeptHierarchy(depts); err != nil {
return nil, err
}
return buildSysDeptFullTree(depts), nil
}
// withDeptAncestors 为列表中的每个节点补齐缺失的祖先,返回补齐后的完整列表。
//
// 输入通常是过滤后的结果集,节点可能缺失父级。做法是逐轮收集
// 「父级 id 不在当前集合中」的节点,按 id 批量补查,直到没有新节点补入为止。
// 补入的祖先沿用原有排序(order_num、dept_id),与 GetDeptList 一致。
func (l *sSysDept) withDeptAncestors(ctx context.Context, depts []*model.SysDeptListModel) ([]*model.SysDeptListModel, error) {
if len(depts) == 0 {
return depts, nil
}
present := make(map[int64]struct{}, len(depts))
for _, d := range depts {
if d != nil {
present[d.DeptId] = struct{}{}
}
}
merged := make([]*model.SysDeptListModel, 0, len(depts))
merged = append(merged, depts...)
// 每轮收集缺失父级 → 批量补查;轮数受部门层级深度约束(通常 2~3 轮)。
for {
missingIds := make([]int64, 0)
for _, d := range depts {
if d == nil || d.ParentId == 0 {
continue
}
if _, ok := present[d.ParentId]; !ok {
missingIds = append(missingIds, d.ParentId)
}
}
if len(missingIds) == 0 {
break
}
ancestors, err := l.GetDeptListByIds(ctx, missingIds)
if err != nil {
return nil, err
}
if len(ancestors) == 0 {
// 父级已不存在(脏数据或跨租户不可见),到此为止,避免死循环。
break
}
// 去重后并入;下一轮继续向上补。
fresh := make([]*model.SysDeptListModel, 0, len(ancestors))
for _, a := range ancestors {
if a == nil {
continue
}
if _, ok := present[a.DeptId]; ok {
continue
}
present[a.DeptId] = struct{}{}
fresh = append(fresh, a)
}
if len(fresh) == 0 {
break
}
merged = append(merged, fresh...)
depts = merged
}
// 补入的祖先是按「缺失父级 id 批次」查回来的,追加在尾部会打乱原有
// order_num / dept_id 排序,而 buildSysDeptFullTree 依赖入参顺序来保证
// 同层节点的展示次序,因此这里统一重排一次。
sort.SliceStable(merged, func(i, j int) bool {
if merged[i].OrderNum != merged[j].OrderNum {
return merged[i].OrderNum < merged[j].OrderNum
}
return merged[i].DeptId < merged[j].DeptId
})
return merged, nil
}
// buildSysDeptFullTree 把部门列表组装成嵌套树。
//
// 处理「可见根」:ParentId == 0 的顶层部门,以及 ParentId 指向本批次
// 中不存在的部门(例如按状态/名称过滤后父级被滤掉)—— 后者若丢弃会
// 导致整棵子树不可见,因此提升为根节点。逻辑与 buildSysMenuFullTree 一致。
func buildSysDeptFullTree(depts []*model.SysDeptListModel) []*model.SysDeptFullTreeModel {
if len(depts) == 0 {
return make([]*model.SysDeptFullTreeModel, 0)
}
// 保持入参顺序(查询已按 order_num、dept_id 排序),同时建立 id 索引。
nodes := make([]*model.SysDeptFullTreeModel, 0, len(depts))
nodeMaps := make(map[int64]*model.SysDeptFullTreeModel, len(depts))
for _, item := range depts {
if item == nil {
continue
}
node := &model.SysDeptFullTreeModel{
SysDeptListModel: *item,
Children: make([]*model.SysDeptFullTreeModel, 0),
}
nodes = append(nodes, node)
nodeMaps[item.DeptId] = node
}
roots := make([]*model.SysDeptFullTreeModel, 0, len(depts))
for _, node := range nodes {
if node.ParentId == 0 {
roots = append(roots, node)
continue
}
if parent, ok := nodeMaps[node.ParentId]; ok {
parent.Children = append(parent.Children, node)
continue
}
// 父级被过滤掉:提升为可见根,保证其子树仍可访问。
roots = append(roots, node)
}
return roots
}
// GetDeptTree 获取部门树
func (l *sSysDept) GetDeptTree(ctx context.Context, query model.SysDeptTreeParam) (items []*model.SysDeptTreeModel, err error) {
depts, _, err := l.GetDeptList(ctx, model.SysDeptListParam{})
if err != nil {
return nil, err
}
if err = validateDeptHierarchy(depts); err != nil {
return nil, err
}
if len(depts) == 0 {
return make([]*model.SysDeptTreeModel, 0), nil
}
// 可见根:ParentId=0 或 ParentId 指向不在列表中的部门(孤立节点)
deptIdSet := make(map[int64]struct{}, len(depts))
for _, d := range depts {
deptIdSet[d.DeptId] = struct{}{}
}
rootDepts := make([]*model.SysDeptListModel, 0, len(depts))
for _, d := range depts {
if d.ParentId == 0 {
rootDepts = append(rootDepts, d)
continue
}
if _, ok := deptIdSet[d.ParentId]; !ok {
rootDepts = append(rootDepts, d)
}
}
items = make([]*model.SysDeptTreeModel, 0)
// 不带过滤:完整树
if query.DeptType == nil || *query.DeptType == "" {
for _, root := range rootDepts {
rootItem := l.deptTreeItem(root, fmt.Sprintf("%d", root.DeptId))
rootItem.Children, err = l.DeptTree(ctx, rootItem, depts, rootItem.Ancestors)
if err != nil {
return nil, err
}
items = append(items, rootItem)
}
return items, nil
}
// DeptType=0:根部门的直接子(按类型过滤)
if *query.DeptType == "0" {
for _, root := range rootDepts {
for _, dept := range depts {
if dept.ParentId == root.DeptId && dept.DeptType == 0 {
items = append(items, l.deptTreeItem(dept, fmt.Sprintf("%d,%d", root.DeptId, dept.DeptId)))
}
}
}
return items, nil
}
// DeptType=1:公司类型树
if *query.DeptType == "1" {
companyDepts := make([]*model.SysDeptListModel, 0)
for _, dept := range depts {
if dept.DeptType == 1 {
companyDepts = append(companyDepts, dept)
}
}
// 公司类型的可见根:ParentId=0 或 ParentId 不在 companyDepts 中
companyIdSet := make(map[int64]struct{}, len(companyDepts))
for _, d := range companyDepts {
companyIdSet[d.DeptId] = struct{}{}
}
companyRoots := make([]*model.SysDeptListModel, 0, len(companyDepts))
for _, d := range companyDepts {
if d.ParentId == 0 {
companyRoots = append(companyRoots, d)
continue
}
if _, ok := companyIdSet[d.ParentId]; !ok {
companyRoots = append(companyRoots, d)
}
}
// rootMap:原 rootDepts 的索引,用于补 ancestors
rootMap := make(map[int64]*model.SysDeptListModel, len(rootDepts))
for _, root := range rootDepts {
rootMap[root.DeptId] = root
}
// 公司子树递归
var buildCompanySubTree func(parentDept *model.SysDeptTreeModel, ancestors string) ([]*model.SysDeptTreeModel, error)
buildCompanySubTree = func(parentDept *model.SysDeptTreeModel, ancestors string) ([]*model.SysDeptTreeModel, error) {
data := make([]*model.SysDeptTreeModel, 0)
for _, dept := range companyDepts {
if dept.ParentId == parentDept.DeptId {
item := l.deptTreeItem(dept, ancestors+","+fmt.Sprintf("%d", dept.DeptId))
children, err := buildCompanySubTree(item, item.Ancestors)
if err != nil {
return nil, err
}
item.Children = children
data = append(data, item)
}
}
return data, nil
}
for _, root := range companyRoots {
ancestors := fmt.Sprintf("%d", root.DeptId)
if visibleRoot, ok := rootMap[root.ParentId]; ok {
ancestors = fmt.Sprintf("%d,%d", visibleRoot.DeptId, root.DeptId)
}
rootItem := l.deptTreeItem(root, ancestors)
children, err := buildCompanySubTree(rootItem, rootItem.Ancestors)
if err != nil {
return nil, err
}
rootItem.Children = children
items = append(items, rootItem)
}
return items, nil
}
return make([]*model.SysDeptTreeModel, 0), nil
}
// deptTreeItem 构建部门树项
func (l *sSysDept) deptTreeItem(dept *model.SysDeptListModel, ancestors string) *model.SysDeptTreeModel {
return &model.SysDeptTreeModel{
DeptId: dept.DeptId,
ParentId: dept.ParentId,
DeptName: dept.DeptName,
Key: fmt.Sprintf("%d", dept.DeptId),
Children: nil,
Ancestors: ancestors,
DeptType: dept.DeptType,
}
}
// 递归构建结构
func (l *sSysDept) RecursionDeptIds(ctx context.Context, parentId int64, deptList []*model.SysDeptListModel, data *[]int64) (err error) {
if err = validateDeptHierarchy(deptList); err != nil {
return err
}
var walk func(int64)
walk = func(currentId int64) {
for _, dept := range deptList {
if dept.ParentId == currentId {
*data = append(*data, dept.DeptId)
walk(dept.DeptId)
}
}
}
walk(parentId)
return nil
}
func validateDeptHierarchy(depts []*model.SysDeptListModel) error {
parents := make(map[int64]int64, len(depts))
for _, dept := range depts {
parents[dept.DeptId] = dept.ParentId
}
checked := make(map[int64]bool, len(depts))
for deptId := range parents {
seen := make(map[int64]bool)
for currentId := deptId; currentId != 0 && !checked[currentId]; {
if seen[currentId] {
return gerror.New("组织层级存在循环引用")
}
seen[currentId] = true
parentId, exists := parents[currentId]
if !exists {
break
}
currentId = parentId
}
for visitedId := range seen {
checked[visitedId] = true
}
}
return nil
}
// 根据父部门id获取部门列表
func (l *sSysDept) GetDeptIdsByParentId(ctx context.Context, parentId int64) (ids []int64, err error) {
depts, _, err := l.GetDeptList(ctx, model.SysDeptListParam{})
if err != nil {
return nil, err
}
ids = make([]int64, 0)
err = l.RecursionDeptIds(ctx, parentId, depts, &ids)
if err != nil {
return nil, err
}
// g.Log().Infof(ctx, "sSysDept.GetDeptIdsByParentId 获取部门列表: ids:%+v", ids)
return ids, nil
}
// AddDept 添加部门
func (l *sSysDept) AddDept(ctx context.Context, dept *model.SysDeptAddModel) (deptId int64, err error) {
user := contexts.GetUser(ctx)
if user == nil {
return 0, gerror.NewCode(consts.CodeLoginExpired, "登录已过期")
}
if dept.DeptType == 1 && dept.ParentId != 0 {
isCompany, err := l.ValidateParentIsCompany(ctx, dept.ParentId)
if err != nil {
g.Log().Errorf(ctx, "sSysDept.AddDept ValidateParentIsCompany err: %v, parentId:%d, dept:%+v", err, dept.ParentId, dept)
return 0, err
}
if !isCompany {
return 0, gerror.New("上级部门必须是公司")
}
}
data := do.SysDept{}
gconv.Struct(dept, &data)
data.CreatedDept = user.DeptId
data.CreatedBy = user.ID
data.CreatedAt = gtime.Now()
data.UpdatedBy = user.ID
data.UpdatedAt = gtime.Now()
data.TenantId = user.TenantId
deptId, err = l.Model(ctx).Data(data).InsertAndGetId()
if err != nil {
return 0, err
}
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysDeptCreate, deptId)
// 刷新部门祖先
err = l.RefreshDeptAncestors(ctx)
if err != nil {
return 0, err
}
return deptId, nil
}
// EditDept 编辑部门
func (l *sSysDept) EditDept(ctx context.Context, dept *model.SysDeptEditModel) (deptId int64, err error) {
user := contexts.GetUser(ctx)
if user == nil {
return dept.DeptId, gerror.NewCode(consts.CodeLoginExpired, "登录已过期")
}
// 查询现有数据
existingDept, err := l.GetDeptById(ctx, dept.DeptId)
if err != nil {
g.Log().Errorf(ctx, "sSysDept.EditDept GetDeptById err: %v, deptId:%d, dept:%+v", err, dept.DeptId, gconv.String(dept))
return 0, err
}
if existingDept == nil || existingDept.DeptId == 0 || existingDept.DeptId != dept.DeptId {
g.Log().Warningf(ctx, "sSysDept.EditDept GetDeptById err: %v, deptId:%d, dept:%+v", err, dept.DeptId, gconv.String(dept))
return 0, gerror.New("部门不存在")
}
newParentId := existingDept.ParentId
if dept.ParentId != nil {
newParentId = *dept.ParentId
var parentDepts []*model.SysDeptViewModel
err = l.Model(ctx, &handler.Option{FilterTenant: true, FilterAuth: false}).Scan(&parentDepts)
if err != nil {
return 0, err
}
if err = validateDeptParent(dept.DeptId, newParentId, parentDepts); err != nil {
return 0, err
}
}
newDeptType := existingDept.DeptType
if dept.DeptType != nil {
newDeptType = *dept.DeptType
}
if newDeptType == 1 && newParentId != 0 {
isCompany, err := l.ValidateParentIsCompany(ctx, newParentId)
if err != nil {
return 0, err
}
if !isCompany {
return 0, gerror.New("上级组织必须是公司")
}
}
if dept.DeptType != nil && *dept.DeptType != existingDept.DeptType {
if existingDept.DeptType == 1 {
return 0, gerror.New("组织类型不能修改")
}
}
data := do.SysDept{}
gconv.Struct(dept, &data)
data.UpdatedBy = user.ID
data.UpdatedAt = gtime.Now()
_, err = l.Model(ctx).Data(data).Where(dao.SysDept.Columns().DeptId, dept.DeptId).Update()
if err != nil {
return dept.DeptId, err
}
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysDeptUpdate, dept.DeptId)
// 刷新部门祖先
err = l.RefreshDeptAncestors(ctx)
if err != nil {
return 0, err
}
return dept.DeptId, nil
}
// DeleteDept 删除部门
func (l *sSysDept) DeleteDept(ctx context.Context, dept *model.SysDeptDeleteModel) (deptId int64, err error) {
count, err := l.ModelQuery(ctx).Where(dao.SysDept.Columns().ParentId, dept.DeptId).Count()
if err != nil {
return dept.DeptId, err
}
if count > 0 {
return dept.DeptId, gerror.NewCode(consts.CodeDeptHasSub, "该部门下有子部门,不能删除")
}
// 部门下有用户,不能删除
userList, _, err := service.SysUser().List(ctx, &request.PageInfo{
Page: 1,
PageSize: 100,
}, &model.UserListParam{
DeptId: dept.DeptId,
})
if err != nil {
return dept.DeptId, err
}
if len(userList) > 0 {
return dept.DeptId, gerror.New("该部门下有用户,不能删除")
}
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
err = service.AgentEnterprise().Delete(ctx, &model.TAgentEnterpriseDeleteParam{
DeptId: dept.DeptId,
})
if err != nil {
g.Log().Errorf(ctx, "sSysDept.DeleteDept Delete err: %v, deptId:%d, Input:%+v", err, dept.DeptId, dept)
return err
}
_, err = l.Model(ctx).Where(dao.SysDept.Columns().DeptId, dept.DeptId).Delete()
if err != nil {
return err
}
return nil
})
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysDeptDelete, dept.DeptId)
return dept.DeptId, nil
}
// 刷新部门 ancestors
func (l *sSysDept) RefreshDeptAncestors(ctx context.Context) (err error) {
var depts []*model.SysDeptViewModel
err = l.ModelQuery(ctx, &handler.Option{
FilterTenant: true,
FilterAuth: false,
}).Scan(&depts)
if err != nil {
return err
}
for _, dept := range depts {
ancestors := ""
if err = l.GetParentIDAncestors(ctx, depts, dept.ParentId, &ancestors); err != nil {
return err
}
if ancestors != dept.Ancestors {
_, err = l.Model(ctx).Data(do.SysDept{
Ancestors: ancestors,
}).Where(dao.SysDept.Columns().DeptId, dept.DeptId).Update()
if err != nil {
return err
}
event.EventsInstance().Emit(ctx, consts.EventKeyDBSysDeptUpdate, dept.DeptId)
g.Log().Infof(ctx, "sSysDept.RefreshDeptAncestors 刷新部门祖先: deptId:%d, ancestors:%s", dept.DeptId, ancestors)
}
}
return nil
}
// GetParentIDAncestors 获取部门祖先
func (l *sSysDept) GetParentIDAncestors(ctx context.Context, depts []*model.SysDeptViewModel, printId int64, ancestors *string) (err error) {
parents := make(map[int64]int64, len(depts))
for _, dept := range depts {
parents[dept.DeptId] = dept.ParentId
}
seen := make(map[int64]bool)
for printId != 0 {
if seen[printId] {
return gerror.New("组织层级存在循环引用")
}
seen[printId] = true
parentId, exists := parents[printId]
if !exists {
break
}
*ancestors = fmt.Sprintf("%d,", printId) + *ancestors
printId = parentId
}
*ancestors = "0," + *ancestors
return nil
}
func validateDeptParent(deptId, parentId int64, depts []*model.SysDeptViewModel) error {
parents := make(map[int64]int64, len(depts))
for _, dept := range depts {
parents[dept.DeptId] = dept.ParentId
}
seen := make(map[int64]bool)
for parentId != 0 {
if parentId == deptId {
return gerror.New("上级组织不能是当前组织或其下级组织")
}
if parentId < 0 || seen[parentId] {
return gerror.New("组织层级存在循环引用")
}
seen[parentId] = true
next, exists := parents[parentId]
if !exists {
return gerror.New("上级组织不存在")
}
parentId = next
}
return nil
}
// GetDeptListByIds 根据部门ID列表获取部门列表
func (l *sSysDept) GetDeptListByIds(ctx context.Context, ids []int64) (depts []*model.SysDeptListModel, err error) {
err = l.ModelQuery(ctx).WhereIn(dao.SysDept.Columns().DeptId, ids).Scan(&depts)
if err != nil {
return nil, err
}
return depts, nil
}
// 验证上级是否是公司
func (l *sSysDept) ValidateParentIsCompany(ctx context.Context, parentId int64) (isCompany bool, err error) {
dept, err := l.GetDeptById(ctx, parentId)
if err != nil {
return false, err
}
if dept == nil || dept.DeptId == 0 {
return false, gerror.New("上级组织不存在")
}
return dept.DeptType == 1 || dept.ParentId == 0, nil
}
// 获取上级公司名称
func (l *sSysDept) getParentCompanyInfo(ctx context.Context, depts []*model.SysDeptListModel, deptId int64) *model.SysDeptListModel {
var topDept *model.SysDeptListModel
byID := make(map[int64]*model.SysDeptListModel, len(depts))
for _, dept := range depts {
byID[dept.DeptId] = dept
if dept.ParentId == 0 {
topDept = dept
}
}
seen := make(map[int64]bool)
for deptId != 0 {
if seen[deptId] {
return nil
}
seen[deptId] = true
dept, exists := byID[deptId]
if !exists {
break
}
if dept.ParentId == 0 || dept.DeptType == 1 {
return dept
}
deptId = dept.ParentId
}
return topDept
}
// 获取组织公司Map
func (l *sSysDept) GetDeptCompanyMap(ctx context.Context) (map[int64]string, error) {
depts, _, err := l.GetDeptList(ctx, model.SysDeptListParam{})
if err != nil {
return nil, err
}
companyMap := make(map[int64]string)
for _, dept := range depts {
if dept.ParentId == 0 {
companyMap[0] = dept.DeptName
}
if dept.DeptType == 1 {
companyMap[dept.DeptId] = dept.DeptName
} else if dept.ParentId == 0 {
companyMap[dept.DeptId] = dept.DeptName
} else {
parentDept := l.getParentCompanyInfo(ctx, depts, dept.DeptId)
if parentDept != nil {
companyMap[dept.DeptId] = parentDept.DeptName
} else {
companyMap[dept.DeptId] = ""
}
}
}
return companyMap, nil
}
// 获取上级公司组织信息
func (l *sSysDept) GetParentCompanyInfo(ctx context.Context, deptId int64) *model.SysDeptListModel {
depts, _, err := l.GetDeptList(ctx, model.SysDeptListParam{})
if err != nil {
g.Log().Errorf(ctx, "sSysDept.GetParentCompanyInfo GetDeptList err: %v, deptId:%d", err, deptId)
return nil
}
return l.getParentCompanyInfo(ctx, depts, deptId)
}