chore: 初始开源发布
iotlcm-admin-svr:管理后台服务。 - Go module:iotlcm → git.5iot.com/iotlcm-public/iotlcm-admin-svr(729 文件) - 凭据与地址脱敏:JWT signingKey(含代码里的 MustGet 默认值)、 MySQL DSN、Redis 口令、告警邮件发件人邮箱全部改为占位; 私网 IP → 文档地址段;库名 xiujie_admin → iotlcm - 真实手机号/邮箱 → 合法 11 位编造号 / example.com - 移除内部资产:CI 触发器、过时 DDL(sql/db_iotlcm.sql)、 私有依赖 go get 行、明文 registry 登录口令 - 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名 - Dockerfile 改用公共基础镜像 - 拼错的 RADEME.md → README.md,README 重写 - 需要真实数据库的用例改为可用 IOTLCM_TEST_WITH_DB=1 显式启用 - 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 数据库 schema 不随本仓发布(生产 DDL 已过时),README 已说明。
This commit is contained in:
@@ -0,0 +1,681 @@
|
||||
// package system
|
||||
// @Link https://github.com/xiujiecn/xiu-admin
|
||||
// @Copyright Copyright (c) 2025 LiXiujie
|
||||
// @Author Lxj <li@xiujie.cn>
|
||||
// @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
|
||||
}
|
||||
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 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) {
|
||||
for _, dept := range deptList {
|
||||
if dept.ParentId == parentId {
|
||||
*data = append(*data, dept.DeptId)
|
||||
err := l.RecursionDeptIds(ctx, dept.DeptId, deptList, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 根据父部门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 {
|
||||
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
|
||||
}
|
||||
if dept.DeptType != nil && *dept.DeptType == 1 && *dept.ParentId != 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 := ""
|
||||
l.GetParentIDAncestors(ctx, depts, dept.ParentId, &ancestors)
|
||||
|
||||
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) {
|
||||
for _, dept := range depts {
|
||||
if dept.DeptId == printId {
|
||||
*ancestors = fmt.Sprintf("%d", dept.DeptId) + "," + *ancestors
|
||||
l.GetParentIDAncestors(ctx, depts, dept.ParentId, ancestors)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*ancestors = "0," + *ancestors
|
||||
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
|
||||
}
|
||||
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
|
||||
for _, dept := range depts {
|
||||
if dept.ParentId == 0 {
|
||||
topDept = dept
|
||||
}
|
||||
if dept.DeptId == deptId {
|
||||
if dept.ParentId == 0 {
|
||||
return dept
|
||||
}
|
||||
if dept.DeptType == 1 {
|
||||
return dept
|
||||
}
|
||||
return l.getParentCompanyInfo(ctx, depts, 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)
|
||||
}
|
||||
Reference in New Issue
Block a user