package sys import ( "context" "fmt" "hotgo/internal/consts" "hotgo/internal/model/input/form" "hotgo/internal/model/input/sysin" "hotgo/internal/service" "hotgo/utility/convert" "hotgo/utility/excel" "net/url" "strings" "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/gctx" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/guid" ) type sSysImOrg struct{} func NewSysImOrg() *sSysImOrg { return &sSysImOrg{} } func init() { service.RegisterSysImOrg(NewSysImOrg()) } func (s *sSysImOrg) DepartmentList(ctx context.Context, in *sysin.ImOrgDepartmentListInp) (list []*sysin.ImOrgDepartmentModel, totalCount int, err error) { if err = in.Filter(ctx); err != nil { return } tenant, err := resolveImAdminTenant(ctx, in.TenantId) if err != nil { return } values := addPageQuery(url.Values{}, in.Page, in.PerPage) if in.DepartmentId != "" { values.Set("department_id", strings.TrimSpace(in.DepartmentId)) } if in.ParentId != "" { values.Set("parent_id", strings.TrimSpace(in.ParentId)) } if in.Name != "" { values.Set("name", strings.TrimSpace(in.Name)) } if in.State != "" { values.Set("state", strings.TrimSpace(in.State)) } return imAdminQueryList[sysin.ImOrgDepartmentModel](ctx, tenant, "/internal/admin-query/tenants/"+url.PathEscape(tenant.Code)+"/org/departments", values, nil, ) } func (s *sSysImOrg) DepartmentExport(ctx context.Context, in *sysin.ImOrgDepartmentListInp) (err error) { list, totalCount, err := s.DepartmentList(ctx, in) if err != nil { return } tags, err := convert.GetEntityDescTags(sysin.ImOrgDepartmentExportModel{}) if err != nil { return } var exports []sysin.ImOrgDepartmentExportModel if err = gconv.Scan(list, &exports); err != nil { return } fileName := "导出IM组织部门-" + gctx.CtxId(ctx) sheetName := fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list)) if len(list) > 0 { _ = writeImAdminAudit(ctx, list[0].TenantId, "im_org_department_export", "im_org_department", 0, "success", g.Map{"count": len(list)}) } return excel.ExportByStructs(ctx, tags, exports, fileName, sheetName) } func (s *sSysImOrg) DepartmentEdit(ctx context.Context, in *sysin.ImOrgDepartmentEditInp) (err error) { if err = in.Filter(ctx); err != nil { return } db, tenant, err := resolveImTenantDB(ctx, in.TenantId) if err != nil { return } if in.ParentId != "root" { if exists, err := s.departmentExists(ctx, db, in.ParentId); err != nil { return err } else if !exists { return gerror.New("上级部门不存在") } } if in.Id > 0 { current, err := db.Model("im_org_departments").Safe().Ctx(ctx).Where("id", in.Id).One() if err != nil { return gerror.Wrap(err, "获取IM组织部门失败,请稍后重试!") } if current.IsEmpty() { return gerror.New("部门不存在") } departmentId := current["department_id"].String() if in.ParentId == departmentId { return gerror.New("上级部门不能选择自身") } if err = s.ensureNoDepartmentCycle(ctx, db, departmentId, in.ParentId); err != nil { return err } if _, err = db.Model("im_org_departments").Safe().Ctx(ctx). Where("id", in.Id). Data(g.Map{ "name": in.Name, "parent_id": in.ParentId, "sort_order": in.SortOrder, "state": in.State, "version": gdb.Raw("version+1"), "updated_at": gtime.Now(), }).Update(); err != nil { return gerror.Wrap(err, "更新IM组织部门失败,请稍后重试!") } _ = writeImAdminAudit(ctx, tenant.Id, "im_org_department_update", "im_org_department", in.Id, "success", g.Map{"department_id": departmentId}) return nil } departmentId := in.DepartmentId if departmentId == "" { departmentId = "dep_" + strings.ToLower(guid.S()) } if exists, err := s.departmentExists(ctx, db, departmentId); err != nil { return err } else if exists { return gerror.New("部门ID已存在") } if _, err = db.Model("im_org_departments").Safe().Ctx(ctx).Data(g.Map{ "department_id": departmentId, "name": in.Name, "parent_id": in.ParentId, "sort_order": in.SortOrder, "state": in.State, "version": 1, "created_at": gtime.Now(), "updated_at": gtime.Now(), }).Insert(); err != nil { return gerror.Wrap(err, "创建IM组织部门失败,请稍后重试!") } _ = writeImAdminAudit(ctx, tenant.Id, "im_org_department_create", "im_org_department", 0, "success", g.Map{"department_id": departmentId}) return nil } func (s *sSysImOrg) DepartmentDelete(ctx context.Context, in *sysin.ImOrgDepartmentDeleteInp) (err error) { if err = in.Filter(ctx); err != nil { return } db, tenant, err := resolveImTenantDB(ctx, in.TenantId) if err != nil { return } if in.DepartmentId == "root" { return gerror.New("根部门不能删除") } if exists, err := s.departmentExists(ctx, db, in.DepartmentId); err != nil { return err } else if !exists { return gerror.New("部门不存在") } childCount, err := db.Model("im_org_departments").Safe().Ctx(ctx).Where("parent_id", in.DepartmentId).Count() if err != nil { return gerror.Wrap(err, "检查子部门失败,请稍后重试!") } if childCount > 0 { return gerror.New("存在子部门,请先处理后再删除") } memberCount, err := db.Model("im_org_members").Safe().Ctx(ctx).Where("department_id", in.DepartmentId).Count() if err != nil { return gerror.Wrap(err, "检查部门成员失败,请稍后重试!") } if memberCount > 0 { return gerror.New("存在部门成员,请先移除后再删除") } if _, err = db.Model("im_org_departments").Safe().Ctx(ctx).Where("department_id", in.DepartmentId).Delete(); err != nil { return gerror.Wrap(err, "删除IM组织部门失败,请稍后重试!") } _ = writeImAdminAudit(ctx, tenant.Id, "im_org_department_delete", "im_org_department", 0, "success", g.Map{"department_id": in.DepartmentId}) return nil } func (s *sSysImOrg) MemberList(ctx context.Context, in *sysin.ImOrgMemberListInp) (list []*sysin.ImOrgMemberModel, totalCount int, err error) { if err = in.Filter(ctx); err != nil { return } tenant, err := resolveImAdminTenant(ctx, in.TenantId) if err != nil { return } values := addPageQuery(url.Values{}, in.Page, in.PerPage) if in.DepartmentId != "" { values.Set("department_id", strings.TrimSpace(in.DepartmentId)) } if in.UserId > 0 { values.Set("user_id", int64ToString(in.UserId)) } if in.EmploymentState != "" { values.Set("employment_state", strings.TrimSpace(in.EmploymentState)) } if in.Query != "" { values.Set("q", strings.TrimSpace(in.Query)) } return imAdminQueryList[sysin.ImOrgMemberModel](ctx, tenant, "/internal/admin-query/tenants/"+url.PathEscape(tenant.Code)+"/org/members", values, nil, ) } func (s *sSysImOrg) MemberExport(ctx context.Context, in *sysin.ImOrgMemberListInp) (err error) { list, totalCount, err := s.MemberList(ctx, in) if err != nil { return } tags, err := convert.GetEntityDescTags(sysin.ImOrgMemberExportModel{}) if err != nil { return } var exports []sysin.ImOrgMemberExportModel if err = gconv.Scan(list, &exports); err != nil { return } fileName := "导出IM组织成员-" + gctx.CtxId(ctx) sheetName := fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list)) if len(list) > 0 { _ = writeImAdminAudit(ctx, list[0].TenantId, "im_org_member_export", "im_org_member", 0, "success", g.Map{"count": len(list), "department_id": in.DepartmentId}) } return excel.ExportByStructs(ctx, tags, exports, fileName, sheetName) } func (s *sSysImOrg) MemberEdit(ctx context.Context, in *sysin.ImOrgMemberEditInp) (err error) { if err = in.Filter(ctx); err != nil { return } db, tenant, err := resolveImTenantDB(ctx, in.TenantId) if err != nil { return } if exists, err := s.departmentExists(ctx, db, in.DepartmentId); err != nil { return err } else if !exists { return gerror.New("部门不存在") } userCount, err := db.Model("im_users").Safe().Ctx(ctx).Where("id", in.UserId).Count() if err != nil { return gerror.Wrap(err, "检查IM用户失败,请稍后重试!") } if userCount == 0 { return gerror.New("IM用户不存在") } data := g.Map{ "department_id": in.DepartmentId, "user_id": in.UserId, "display_name": in.DisplayName, "position": in.Position, "avatar_file_id": in.AvatarFileId, "mobile": in.Mobile, "email": in.Email, "employment_state": in.EmploymentState, "contactable": in.Contactable, "sort_order": in.SortOrder, "updated_at": gtime.Now(), } if in.Id > 0 { data["version"] = gdb.Raw("version+1") if _, err = db.Model("im_org_members").Safe().Ctx(ctx).Where("id", in.Id).Data(data).Update(); err != nil { return gerror.Wrap(err, "更新IM组织成员失败,请稍后重试!") } _ = writeImAdminAudit(ctx, tenant.Id, "im_org_member_update", "im_org_member", in.Id, "success", g.Map{"department_id": in.DepartmentId, "user_id": in.UserId}) return nil } data["version"] = 1 data["created_at"] = gtime.Now() if _, err = db.Model("im_org_members").Safe().Ctx(ctx).Data(data).Insert(); err != nil { return gerror.Wrap(err, "添加IM组织成员失败,请稍后重试!") } _ = writeImAdminAudit(ctx, tenant.Id, "im_org_member_create", "im_org_member", 0, "success", g.Map{"department_id": in.DepartmentId, "user_id": in.UserId}) return nil } func (s *sSysImOrg) MemberDelete(ctx context.Context, in *sysin.ImOrgMemberDeleteInp) (err error) { if err = in.Filter(ctx); err != nil { return } db, tenant, err := resolveImTenantDB(ctx, in.TenantId) if err != nil { return } current, err := db.Model("im_org_members").Safe().Ctx(ctx).Where("id", in.Id).One() if err != nil { return gerror.Wrap(err, "获取IM组织成员失败,请稍后重试!") } if current.IsEmpty() { return gerror.New("组织成员不存在") } if _, err = db.Model("im_org_members").Safe().Ctx(ctx).Where("id", in.Id).Delete(); err != nil { return gerror.Wrap(err, "删除IM组织成员失败,请稍后重试!") } _ = writeImAdminAudit(ctx, tenant.Id, "im_org_member_delete", "im_org_member", in.Id, "success", g.Map{"department_id": current["department_id"].String(), "user_id": current["user_id"].Int64()}) return nil } func (s *sSysImOrg) Search(ctx context.Context, in *sysin.ImOrgSearchInp) (res *sysin.ImOrgSearchModel, err error) { if err = in.Filter(ctx); err != nil { return } departments, _, err := s.DepartmentList(ctx, &sysin.ImOrgDepartmentListInp{ PageReq: form.PageReq{Page: 1, PerPage: 20, Pagination: true}, TenantId: in.TenantId, Name: in.Query, State: consts.ImOrgDepartmentStateActive, }) if err != nil { return nil, err } members, _, err := s.MemberList(ctx, &sysin.ImOrgMemberListInp{ PageReq: form.PageReq{Page: 1, PerPage: 20, Pagination: true}, TenantId: in.TenantId, Query: in.Query, EmploymentState: consts.ImOrgEmploymentStateActive, }) if err != nil { return nil, err } res = &sysin.ImOrgSearchModel{Departments: departments, Members: members} return } func (s *sSysImOrg) departmentModel(ctx context.Context, db gdb.DB) *gdb.Model { return db.Model("im_org_departments d").Safe().Ctx(ctx).Fields( "d.id", "d.department_id", "d.name", "d.parent_id", "d.sort_order", "d.version", "d.state", "d.created_at", "d.updated_at", "(SELECT COUNT(*) FROM im_org_departments child WHERE child.parent_id=d.department_id AND child.state='active') child_count", "(SELECT COUNT(*) FROM im_org_members m WHERE m.department_id=d.department_id) member_count", ) } func (s *sSysImOrg) memberModel(ctx context.Context, db gdb.DB) *gdb.Model { return db.Model("im_org_members m").Safe().Ctx(ctx). LeftJoin("im_org_departments d", "d.department_id=m.department_id"). Fields( "m.id", "m.department_id", "COALESCE(d.name,'') department_name", "m.user_id", "m.display_name", "m.position", "m.avatar_file_id", "m.mobile", "m.email", "m.employment_state", "m.contactable", "m.sort_order", "m.version", "m.created_at", "m.updated_at", ) } func (s *sSysImOrg) departmentExists(ctx context.Context, db gdb.DB, departmentId string) (bool, error) { count, err := db.Model("im_org_departments").Safe().Ctx(ctx).Where("department_id", strings.TrimSpace(departmentId)).Count() if err != nil { return false, gerror.Wrap(err, "检查IM组织部门失败,请稍后重试!") } return count > 0, nil } func (s *sSysImOrg) ensureNoDepartmentCycle(ctx context.Context, db gdb.DB, departmentId, parentId string) error { next := strings.TrimSpace(parentId) for next != "" && next != "root" { if next == departmentId { return gerror.New("上级部门不能选择自身或下级部门") } row, err := db.Model("im_org_departments").Safe().Ctx(ctx).Fields("parent_id").Where("department_id", next).One() if err != nil { return gerror.Wrap(err, "检查部门层级失败,请稍后重试!") } if row.IsEmpty() { return gerror.New("上级部门不存在") } next = row["parent_id"].String() } return nil }