diff --git a/internal/models/organization/organization.go b/internal/models/organization/organization.go new file mode 100644 index 0000000..dac1986 --- /dev/null +++ b/internal/models/organization/organization.go @@ -0,0 +1,16 @@ +package organization + +import "time" + +type Organization struct { + ID uint `json:"id"` // id + Name string `json:"name"` // 组织名 + ParentID uint `json:"parent_id"` // 上级组织id + Sort uint `json:"sort"` // 层级序号 + Status int `json:"status"` // 状态:0-无效,1-有效 + Path string `json:"path"` // 全路径 + CreatedOn time.Time `json:"created_on"` // 记录创建时间 + Children []Organization `json:"children"` +} + +type OrgTree []Organization diff --git a/internal/models/organization/request.go b/internal/models/organization/request.go new file mode 100644 index 0000000..8560eca --- /dev/null +++ b/internal/models/organization/request.go @@ -0,0 +1,27 @@ +package organization + +type CreateOrgReq struct { + Name string `json:"name" binding:"required"` + ParentId uint `json:"parent_id" binding:"required"` + Sort int `json:"sort" binding:"required"` +} + +type DelOrgReq struct { + Id uint `json:"id" binding:"required"` +} + +type GetOrgReq struct { + Id uint `from:"id" json:"id" binding:"required"` +} + +type MoveOrgReq struct { + Id uint `json:"id" binding:"required"` + DestId uint `json:"dest_id" binding:"required"` +} + +type SaveOrgReq struct { + Id uint `json:"id" binding:"required"` + Name string `json:"name" binding:"required"` + ParentId uint `json:"parent_id" binding:"required"` + Sort int `json:"sort" binding:"required"` +} diff --git a/internal/repo/organization.go b/internal/repo/organization.go index 6f91f48..9a9dc91 100644 --- a/internal/repo/organization.go +++ b/internal/repo/organization.go @@ -2,12 +2,24 @@ package repo import ( + "context" "time" "github.com/samber/do" "gorm.io/gorm" ) +func init(){ + do.Provide[OrganizationRepo](nil,NewOrganization) +} + +type OrganizationStatus int + +const( + OrganizationDisableStatus OrganizationStatus = 0 + OrganizationAbleStatus OrganizationStatus = 1 +) + // 组织表 type Organization struct { ID uint `gorm:"column:id;primary_key;AUTO_INCREMENT"` // id @@ -27,7 +39,12 @@ func (m *Organization) TableName() string { } type OrganizationRepo interface{ - + Create(ctx context.Context,org Organization)(id uint,err error) + Save(ctx context.Context,org Organization)(error) + GetById(ctx context.Context,id uint)(org Organization,err error) + DelById(ctx context.Context,id uint)(err error) + GetOrgs(ctx context.Context)(org []Organization,err error) + ExistOrgId(ctx context.Context,id uint)(bool,error) } type orginizationRepo struct{ @@ -38,4 +55,38 @@ func NewOrganization(i *do.Injector)(OrganizationRepo,error){ return &orginizationRepo{ db:do.MustInvoke[*gorm.DB](i), },nil +} + +func (o *orginizationRepo)Create(ctx context.Context,org Organization)(id uint,err error){ + err = o.db.Create(org).Error + id = org.ID + return +} + +func (o *orginizationRepo)Save(ctx context.Context,org Organization)(error){ + return o.db.Save(&org).Error +} + +func (o *orginizationRepo)GetById(ctx context.Context,id uint)(org Organization,err error){ + err = o.db.Where("id = ?",id).Take(&org).Error + return +} + +func (o *orginizationRepo)DelById(ctx context.Context,id uint)(err error){ + return o.db.Where("id = ?",id).Delete(&Organization{}).Error +} + +func (o *orginizationRepo)GetOrgs(ctx context.Context)(orgs []Organization,err error){ + err = o.db.Model(&Organization{}).Find(&orgs).Error + return +} + +func (o *orginizationRepo)ExistOrgId(ctx context.Context,id uint)(bool,error){ + var count int64 = 0 + err := o.db.Where("id = ?",id).Count(&count).Error + if count >0{ + return false,err + } + + return true,err } \ No newline at end of file diff --git a/internal/service/organization/interface.go b/internal/service/organization/interface.go index e69de29..166ed94 100644 --- a/internal/service/organization/interface.go +++ b/internal/service/organization/interface.go @@ -0,0 +1,17 @@ +package organization + +import ( + orgModel "busniess-user-center/internal/models/organization" + "context" +) + +type OrganizationService interface { + CreateOrganization(ctx context.Context, info orgModel.CreateOrgReq) error + SaveOrganization(ctx context.Context, info orgModel.SaveOrgReq) error + DelOrganization(ctx context.Context, info orgModel.DelOrgReq) error + Organization(ctx context.Context, info orgModel.GetOrgReq) (orgModel.Organization, error) + MoveOrganization(ctx context.Context, info orgModel.MoveOrgReq) error + OrganizationTree(ctx context.Context) (orgModel.OrgTree, error) + DisableOrganization(ctx context.Context) error + EnableOrganization(ctx context.Context) error +} diff --git a/internal/service/organization/organization.go b/internal/service/organization/organization.go index 7d95f36..f46cdfd 100644 --- a/internal/service/organization/organization.go +++ b/internal/service/organization/organization.go @@ -2,11 +2,16 @@ package organization import ( "busniess-user-center/config" + orgModel "busniess-user-center/internal/models/organization" "busniess-user-center/internal/repo" "busniess-user-center/pkg/redis" + "context" + "fmt" + "github.com/jinzhu/copier" "github.com/samber/do" "go.uber.org/zap" + "gorm.io/gorm" ) func init() { @@ -14,19 +19,124 @@ func init() { } type organizationService struct { - logger *zap.SugaredLogger - appRepo repo.OrganizationRepo - menuRepo repo.MenuRepo - redis *redis.Redis - conf *config.AppConfig + logger *zap.SugaredLogger + orgRepo repo.OrganizationRepo + redis *redis.Redis + conf *config.AppConfig } func NewOrganizationService(i *do.Injector) (OrganizationService, error) { return &organizationService{ - logger: do.MustInvoke[*zap.SugaredLogger](i), - appRepo: do.MustInvoke[repo.OrganizationRepo](i), - menuRepo: do.MustInvoke[repo.MenuRepo](i), - redis: do.MustInvoke[*redis.Redis](i), - conf: do.MustInvoke[*config.AppConfig](i), + logger: do.MustInvoke[*zap.SugaredLogger](i), + orgRepo: do.MustInvoke[repo.OrganizationRepo](i), + redis: do.MustInvoke[*redis.Redis](i), + conf: do.MustInvoke[*config.AppConfig](i), }, nil } + +func (o *organizationService) CreateOrganization(ctx context.Context, info *orgModel.CreateOrgReq) error { + if info.ParentId > 0 { + if exist, err := o.orgRepo.ExistOrgId(ctx, info.ParentId); err != nil { + return err + } else if !exist { + return fmt.Errorf("父组织%d不存在", info.ParentId) + } + } + + dbOrg := repo.Organization{ + Name: info.Name, + ParentID: info.ParentId, + Sort: uint(info.Sort), + Status: int(repo.OrganizationAbleStatus), + } + + _, err := o.orgRepo.Create(ctx, dbOrg) + + return err +} + +func (o *organizationService) SaveOrganization(ctx context.Context, info *orgModel.SaveOrgReq) error { + dbOrg, err := o.orgRepo.GetById(ctx, info.Id) + if err != nil && err != gorm.ErrRecordNotFound { + return err + } + + if err == gorm.ErrRecordNotFound { + return fmt.Errorf("组织不存在") + } + + dbOrg.Name = info.Name + err = o.orgRepo.Save(ctx, dbOrg) + return err +} + +func (o *organizationService) DelOrganization(ctx context.Context, info *orgModel.DelOrgReq) error { + // todo 判断权限 + // todo 判断组织下面有没有成员 + // todo 删除组织授权和相应的用户 + return o.orgRepo.DelById(ctx, info.Id) +} + +func (o *organizationService) Organization(ctx context.Context, info *orgModel.GetOrgReq) (org orgModel.Organization, err error) { + dbOrg, err := o.orgRepo.GetById(ctx, info.Id) + if err != nil && err != gorm.ErrRecordNotFound { + return + } + + if err != gorm.ErrRecordNotFound { + err = fmt.Errorf("组织不存在") + return + } + + copier.Copy(org, dbOrg) + return +} + +func (o *organizationService) MoveOrganization(ctx context.Context, info *orgModel.MoveOrgReq) error { + // todo 判断权限 + dbOrg, err := o.orgRepo.GetById(ctx, info.Id) + if err != nil && err != gorm.ErrRecordNotFound { + return err + } + + if err == gorm.ErrRecordNotFound { + return fmt.Errorf("组织%d不存在", info.Id) + } + + if dbOrg.ID == info.DestId || dbOrg.ParentID == info.DestId { + return nil + } + + _, err = o.orgRepo.GetById(ctx, info.DestId) + if err != nil && err != gorm.ErrRecordNotFound { + return err + } + + if err != gorm.ErrRecordNotFound { + return fmt.Errorf("目标组织%d不存在", info.DestId) + } + + dbOrg.ParentID = info.DestId + return o.orgRepo.Save(ctx, dbOrg) +} + +func (o *organizationService) OrganizationTree(ctx context.Context) (orgModel.OrgTree, error) { + dbOrgs, err := o.orgRepo.GetOrgs(ctx) + if err != nil { + return nil, err + } + + tree := getOrgTree(dbOrgs) + return tree, nil +} + +func (o *organizationService) DisableOrganization(ctx context.Context) error { + // todo 判断有没有权限 + // 判断是否成员 + return nil +} + +func (o *organizationService) EnableOrganization(ctx context.Context) error { + // todo 判断有没有权限 + return nil +} diff --git a/internal/service/organization/util.go b/internal/service/organization/util.go new file mode 100644 index 0000000..ddac31c --- /dev/null +++ b/internal/service/organization/util.go @@ -0,0 +1,171 @@ +package organization + +import ( + orgModel "busniess-user-center/internal/models/organization" + "busniess-user-center/internal/repo" + "sort" +) + +func getOrgParentsWihtSelf(orgId uint, allOrgs []repo.Organization) (orgs []repo.Organization) { + orgMap := make(map[uint]repo.Organization, 0) + orgChildMap := make(map[uint][]repo.Organization, 0) + for _, org := range allOrgs { + if org.Status == 1 { + orgMap[org.ID] = org + orgChildMap[org.ParentID] = append(orgChildMap[org.ParentID], org) + } + } + + orgs = getParents(orgId, orgMap) + + if org, ok := orgMap[orgId]; ok { + orgs = append(orgs, org) + } + + return +} + +func getOrgParents(orgId uint, allOrgs []repo.Organization) (orgs []repo.Organization) { + orgMap := make(map[uint]repo.Organization, 0) + orgChildMap := make(map[uint][]repo.Organization, 0) + for _, org := range allOrgs { + if org.Status == 1 { + orgMap[org.ID] = org + orgChildMap[org.ParentID] = append(orgChildMap[org.ParentID], org) + } + } + + orgs = getParents(orgId, orgMap) + return +} + +func getParents(orgId uint, orgParentMap map[uint]repo.Organization) (orgs []repo.Organization) { + org, ok := orgParentMap[orgId] + if !ok { + return + } + + if org.ParentID == 0 { + return + } + + parentOrg, ok := orgParentMap[org.ParentID] + if ok { + orgs = append(orgs, parentOrg) + orgs = append(orgs, getParents(parentOrg.ID, orgParentMap)...) + } + + return +} + +func getOrgMap(allOrgs []repo.Organization) map[uint]repo.Organization { + orgMap := make(map[uint]repo.Organization, 0) + + for _, org := range allOrgs { + if org.Status == 1 { + orgMap[org.ID] = org + } + } + + return orgMap +} + +func getOrgChildrenWithSelf(orgId uint, allOrgs []repo.Organization) (orgs []repo.Organization) { + orgMap := make(map[uint]repo.Organization, 0) + orgChildMap := make(map[uint][]repo.Organization, 0) + for _, org := range allOrgs { + if org.Status == 1 { + orgMap[org.ID] = org + orgChildMap[org.ParentID] = append(orgChildMap[org.ParentID], org) + } + } + + orgs = getChildren(orgId, orgChildMap) + + if org, ok := orgMap[orgId]; ok { + orgs = append(orgs, org) + } + + return +} + +func getOrgChildren(orgId uint, allOrgs []repo.Organization) (orgs []repo.Organization) { + orgMap := make(map[uint]repo.Organization, 0) + orgChildMap := make(map[uint][]repo.Organization, 0) + for _, org := range allOrgs { + if org.Status == 1 { + orgMap[org.ID] = org + orgChildMap[org.ParentID] = append(orgChildMap[org.ParentID], org) + } + } + + orgs = getChildren(orgId, orgChildMap) + return +} + +func getChildren(orgId uint, orgChildrenMap map[uint][]repo.Organization) (orgs []repo.Organization) { + if children, ok := orgChildrenMap[orgId]; ok { + for _, child := range children { + orgs = append(orgs, getChildren(child.ID, orgChildrenMap)...) + orgs = append(orgs, child) + } + } + + return +} + +func getOrgTree(orgs []repo.Organization) orgModel.OrgTree { + // top + topOrgs := []orgModel.Organization{} + parentMap := make(map[uint][]orgModel.Organization) + for _, org := range orgs { + mOrg := convertOrgDTM(org) + if org.ParentID == 0 { + topOrgs = append(topOrgs, mOrg) + } + + if _, ok := parentMap[org.ID]; !ok { + parentMap[org.ID] = []orgModel.Organization{mOrg} + } else { + parentMap[org.ID] = append(parentMap[org.ID], mOrg) + } + } + + for _, subOrgs := range parentMap { + for _, org := range subOrgs { + children := parentMap[org.ID] + sort.Slice(children, func(i, j int) bool { + return children[i].Sort < children[j].Sort + }) + + org.Children = children + } + } + + for _, org := range topOrgs { + children := parentMap[org.ID] + sort.Slice(children, func(i, j int) bool { + return children[i].Sort < children[j].Sort + }) + + org.Children = children + } + + sort.Slice(topOrgs, func(i, j int) bool { + return topOrgs[i].Sort < topOrgs[j].Sort + }) + + return topOrgs +} + +func convertOrgDTM(org repo.Organization) orgModel.Organization { + return orgModel.Organization{ + ID: org.ID, // id + Name: org.Name, // 组织名 + ParentID: org.ParentID, // 上级组织id + Sort: org.Sort, // 层级序号 + Status: org.Status, // 状态:0-无效,1-有效 + Path: org.Path, // 全路径 + CreatedOn: org.CreatedOn, // 记录创建时间 + } +} diff --git a/server/organization/organization.go b/server/organization/organization.go new file mode 100644 index 0000000..81518e6 --- /dev/null +++ b/server/organization/organization.go @@ -0,0 +1,74 @@ +package organization + +import ( + "context" + + orgModel "busniess-user-center/internal/models/organization" + organizationService "busniess-user-center/internal/service/organization" + ginUtil "busniess-user-center/pkg/utils/gin" + + "github.com/gin-gonic/gin" + "github.com/samber/do" + "go.uber.org/zap" +) + +func init() { + do.Provide(nil, NewOrganizationServer) +} + +type OrganizationServer struct { + organizationService organizationService.OrganizationService + logger *zap.SugaredLogger +} + +func NewOrganizationServer(i *do.Injector) (*OrganizationServer, error) { + return &OrganizationServer{ + organizationService: do.MustInvoke[organizationService.OrganizationService](i), + logger: do.MustInvoke[*zap.SugaredLogger](i), + }, nil +} + +func RegisterRoute(api *gin.RouterGroup) { + server := do.MustInvoke[*OrganizationServer](nil) + api.POST("/create", ginUtil.WrapNoRsp(server.Create)) + api.POST("/save", ginUtil.WrapNoRsp(server.SaveOrganization)) + api.POST("/delete", ginUtil.WrapNoRsp(server.DelOrganization)) + api.GET("/get", ginUtil.Wrap(server.Organization)) + api.POST("/move", ginUtil.WrapNoRsp(server.MoveOrganization)) + api.GET("/orgs", ginUtil.WrapNoReq(server.OrganizationTree)) + api.POST("/disenable", ginUtil.WrapNo(server.DisableOrganization)) + api.POST("/enable", ginUtil.WrapNo(server.EnableOrganization)) +} + +func (u *OrganizationServer) Create(ctx context.Context, req *orgModel.CreateOrgReq) (err error) { + // 转换dto + return u.organizationService.CreateOrganization(ctx, req) +} + +func (u *OrganizationServer) SaveOrganization(ctx context.Context, info *orgModel.SaveOrgReq) error { + return u.organizationService.SaveOrganization(ctx, info) +} + +func (u *OrganizationServer) DelOrganization(ctx context.Context, info *orgModel.DelOrgReq) error { + return u.organizationService.DelOrganization(ctx, info) +} + +func (u *OrganizationServer) Organization(ctx context.Context, info *orgModel.GetOrgReq) (orgModel.Organization, error) { + return u.organizationService.Organization(ctx, info) +} + +func (u *OrganizationServer) MoveOrganization(ctx context.Context, info *orgModel.MoveOrgReq) error { + return u.organizationService.MoveOrganization(ctx, info) +} + +func (u *OrganizationServer) OrganizationTree(ctx context.Context) (orgModel.OrgTree, error) { + return u.organizationService.OrganizationTree(ctx) +} + +func (u *OrganizationServer) DisableOrganization(ctx context.Context) error { + return u.organizationService.DisableOrganization(ctx) +} + +func (u *OrganizationServer) EnableOrganization(ctx context.Context) error { + return u.organizationService.EnableOrganization(ctx) +}