From 38f4e740fa419f6e6ad830435a4cd158e9b74e75 Mon Sep 17 00:00:00 2001 From: dark Date: Sat, 14 Feb 2026 11:30:58 +0800 Subject: [PATCH] feat: implement all backend logic for menu, role, org management --- .../internal/logic/menu/createmenulogic.go | 45 +++++- .../internal/logic/menu/deletemenulogic.go | 26 ++- .../logic/menu/getcurrentmenuslogic.go | 75 ++++++++- .../internal/logic/menu/getmenulistlogic.go | 13 +- .../internal/logic/menu/updatemenulogic.go | 56 ++++++- .../logic/organization/addorgmemberlogic.go | 42 ++++- .../organization/createorganizationlogic.go | 40 ++++- .../organization/deleteorganizationlogic.go | 39 ++++- .../organization/getorganizationlistlogic.go | 43 ++++- .../logic/organization/getorgmemberslogic.go | 41 ++++- .../organization/removeorgmemberlogic.go | 13 +- .../organization/updateorganizationlogic.go | 55 ++++++- .../organization/updateorgmemberlogic.go | 21 ++- .../logic/profile/getuserorgslogic.go | 45 +++++- .../internal/logic/profile/switchorglogic.go | 45 +++++- .../internal/logic/role/createrolelogic.go | 33 +++- .../internal/logic/role/deleterolelogic.go | 35 +++- .../internal/logic/role/getrolelistlogic.go | 26 ++- .../internal/logic/role/getrolemenuslogic.go | 15 +- .../internal/logic/role/setrolemenuslogic.go | 20 ++- .../internal/logic/role/updaterolelogic.go | 37 ++++- backend/internal/svc/servicecontext.go | 153 +++++++++++++++++- 22 files changed, 875 insertions(+), 43 deletions(-) diff --git a/backend/internal/logic/menu/createmenulogic.go b/backend/internal/logic/menu/createmenulogic.go index 272dc89..ed250d9 100644 --- a/backend/internal/logic/menu/createmenulogic.go +++ b/backend/internal/logic/menu/createmenulogic.go @@ -5,9 +5,11 @@ package menu import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,46 @@ func NewCreateMenuLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Create } func (l *CreateMenuLogic) CreateMenu(req *types.CreateMenuRequest) (resp *types.MenuItem, err error) { - // todo: add your logic here and delete this line + visible := true + if req.Visible != nil { + visible = *req.Visible + } + + menuType := "config" + if req.Type != "" { + menuType = req.Type + } + + menu := &model.Menu{ + ParentId: req.ParentId, + Name: req.Name, + Path: req.Path, + Icon: req.Icon, + Component: req.Component, + Type: menuType, + SortOrder: req.SortOrder, + Visible: visible, + Status: 1, + } + + _, err = model.MenuInsert(l.ctx, l.svcCtx.DB, menu) + if err != nil { + return nil, fmt.Errorf("创建菜单失败: %v", err) + } - return + return &types.MenuItem{ + Id: menu.Id, + ParentId: menu.ParentId, + Name: menu.Name, + Path: menu.Path, + Icon: menu.Icon, + Component: menu.Component, + Type: menu.Type, + SortOrder: menu.SortOrder, + Visible: menu.Visible, + Status: menu.Status, + Children: []types.MenuItem{}, + CreatedAt: menu.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: menu.UpdatedAt.Format("2006-01-02 15:04:05"), + }, nil } diff --git a/backend/internal/logic/menu/deletemenulogic.go b/backend/internal/logic/menu/deletemenulogic.go index 75106ed..23d9ef6 100644 --- a/backend/internal/logic/menu/deletemenulogic.go +++ b/backend/internal/logic/menu/deletemenulogic.go @@ -5,9 +5,11 @@ package menu import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,27 @@ func NewDeleteMenuLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete } func (l *DeleteMenuLogic) DeleteMenu(req *types.DeleteMenuRequest) (resp *types.Response, err error) { - // todo: add your logic here and delete this line + // 检查是否有子菜单 + hasChildren, err := model.MenuHasChildren(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("检查子菜单失败: %v", err) + } + if hasChildren { + return &types.Response{ + Code: 400, + Message: "该菜单下有子菜单,无法删除", + Success: false, + }, nil + } + + err = model.MenuDelete(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("删除菜单失败: %v", err) + } - return + return &types.Response{ + Code: 0, + Message: "删除成功", + Success: true, + }, nil } diff --git a/backend/internal/logic/menu/getcurrentmenuslogic.go b/backend/internal/logic/menu/getcurrentmenuslogic.go index 90467ec..ce16fdb 100644 --- a/backend/internal/logic/menu/getcurrentmenuslogic.go +++ b/backend/internal/logic/menu/getcurrentmenuslogic.go @@ -5,9 +5,11 @@ package menu import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,76 @@ func NewGetCurrentMenusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *G } func (l *GetCurrentMenusLogic) GetCurrentMenus() (resp *types.MenuListResponse, err error) { - // todo: add your logic here and delete this line + // 获取用户角色 + roleStr, _ := l.ctx.Value("role").(string) + if roleStr == "" { + roleStr = model.RoleGuest + } + + // 查找角色 + role, err := model.RoleFindOneByCode(l.ctx, l.svcCtx.DB, roleStr) + if err != nil { + return nil, fmt.Errorf("角色不存在: %v", err) + } + + // 获取角色关联的菜单ID + roleMenuIds, err := model.RoleMenuFindByRoleId(l.ctx, l.svcCtx.DB, role.Id) + if err != nil { + return nil, fmt.Errorf("获取角色菜单失败: %v", err) + } + + // 构建菜单ID集合用于快速查找 + menuIdSet := make(map[int64]bool) + for _, id := range roleMenuIds { + menuIdSet[id] = true + } + + // 获取所有菜单 + allMenus, err := model.MenuFindAll(l.ctx, l.svcCtx.DB) + if err != nil { + return nil, fmt.Errorf("获取菜单列表失败: %v", err) + } + + // 过滤:包含角色关联的菜单或 type=default 且 visible=true + var filteredMenus []model.Menu + for _, m := range allMenus { + if menuIdSet[m.Id] || (m.Type == "default" && m.Visible) { + filteredMenus = append(filteredMenus, m) + } + } + + // 构建树形结构 + tree := buildMenuTree(filteredMenus, 0) - return + return &types.MenuListResponse{ + List: tree, + }, nil +} + +func buildMenuTree(menus []model.Menu, parentId int64) []types.MenuItem { + var tree []types.MenuItem + for _, m := range menus { + if m.ParentId == parentId { + item := types.MenuItem{ + Id: m.Id, + ParentId: m.ParentId, + Name: m.Name, + Path: m.Path, + Icon: m.Icon, + Component: m.Component, + Type: m.Type, + SortOrder: m.SortOrder, + Visible: m.Visible, + Status: m.Status, + Children: buildMenuTree(menus, m.Id), + CreatedAt: m.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: m.UpdatedAt.Format("2006-01-02 15:04:05"), + } + tree = append(tree, item) + } + } + if tree == nil { + tree = []types.MenuItem{} + } + return tree } diff --git a/backend/internal/logic/menu/getmenulistlogic.go b/backend/internal/logic/menu/getmenulistlogic.go index c165544..6084ee7 100644 --- a/backend/internal/logic/menu/getmenulistlogic.go +++ b/backend/internal/logic/menu/getmenulistlogic.go @@ -5,9 +5,11 @@ package menu import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,14 @@ func NewGetMenuListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMe } func (l *GetMenuListLogic) GetMenuList() (resp *types.MenuListResponse, err error) { - // todo: add your logic here and delete this line + menus, err := model.MenuFindAll(l.ctx, l.svcCtx.DB) + if err != nil { + return nil, fmt.Errorf("获取菜单列表失败: %v", err) + } + + tree := buildMenuTree(menus, 0) - return + return &types.MenuListResponse{ + List: tree, + }, nil } diff --git a/backend/internal/logic/menu/updatemenulogic.go b/backend/internal/logic/menu/updatemenulogic.go index 6b5a490..8e374f7 100644 --- a/backend/internal/logic/menu/updatemenulogic.go +++ b/backend/internal/logic/menu/updatemenulogic.go @@ -5,9 +5,11 @@ package menu import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,57 @@ func NewUpdateMenuLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Update } func (l *UpdateMenuLogic) UpdateMenu(req *types.UpdateMenuRequest) (resp *types.MenuItem, err error) { - // todo: add your logic here and delete this line + menu, err := model.MenuFindOne(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("菜单不存在: %v", err) + } + + if req.ParentId != nil { + menu.ParentId = *req.ParentId + } + if req.Name != "" { + menu.Name = req.Name + } + if req.Path != "" { + menu.Path = req.Path + } + if req.Icon != "" { + menu.Icon = req.Icon + } + if req.Component != "" { + menu.Component = req.Component + } + if req.Type != "" { + menu.Type = req.Type + } + if req.SortOrder != nil { + menu.SortOrder = *req.SortOrder + } + if req.Visible != nil { + menu.Visible = *req.Visible + } + if req.Status != nil { + menu.Status = *req.Status + } + + err = model.MenuUpdate(l.ctx, l.svcCtx.DB, menu) + if err != nil { + return nil, fmt.Errorf("更新菜单失败: %v", err) + } - return + return &types.MenuItem{ + Id: menu.Id, + ParentId: menu.ParentId, + Name: menu.Name, + Path: menu.Path, + Icon: menu.Icon, + Component: menu.Component, + Type: menu.Type, + SortOrder: menu.SortOrder, + Visible: menu.Visible, + Status: menu.Status, + Children: []types.MenuItem{}, + CreatedAt: menu.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: menu.UpdatedAt.Format("2006-01-02 15:04:05"), + }, nil } diff --git a/backend/internal/logic/organization/addorgmemberlogic.go b/backend/internal/logic/organization/addorgmemberlogic.go index 72b7163..4dda4f9 100644 --- a/backend/internal/logic/organization/addorgmemberlogic.go +++ b/backend/internal/logic/organization/addorgmemberlogic.go @@ -5,9 +5,11 @@ package organization import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,43 @@ func NewAddOrgMemberLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddO } func (l *AddOrgMemberLogic) AddOrgMember(req *types.AddOrgMemberRequest) (resp *types.Response, err error) { - // todo: add your logic here and delete this line + // 验证机构是否存在 + _, err = model.OrgFindOne(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("机构不存在: %v", err) + } + + // 验证用户是否存在 + _, err = model.FindOne(l.ctx, l.svcCtx.DB, req.UserId) + if err != nil { + return nil, fmt.Errorf("用户不存在: %v", err) + } + + // 检查是否已经是成员 + existing, _ := model.UserOrgFindOne(l.ctx, l.svcCtx.DB, req.UserId, req.Id) + if existing != nil { + return &types.Response{ + Code: 400, + Message: "该用户已经是该机构成员", + Success: false, + }, nil + } + + // 添加成员 + uo := &model.UserOrganization{ + UserId: req.UserId, + OrgId: req.Id, + RoleId: req.RoleId, + } + + _, err = model.UserOrgInsert(l.ctx, l.svcCtx.DB, uo) + if err != nil { + return nil, fmt.Errorf("添加成员失败: %v", err) + } - return + return &types.Response{ + Code: 0, + Message: "添加成功", + Success: true, + }, nil } diff --git a/backend/internal/logic/organization/createorganizationlogic.go b/backend/internal/logic/organization/createorganizationlogic.go index ec0ee57..d0289cc 100644 --- a/backend/internal/logic/organization/createorganizationlogic.go +++ b/backend/internal/logic/organization/createorganizationlogic.go @@ -5,9 +5,11 @@ package organization import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,41 @@ func NewCreateOrganizationLogic(ctx context.Context, svcCtx *svc.ServiceContext) } func (l *CreateOrganizationLogic) CreateOrganization(req *types.CreateOrgRequest) (resp *types.OrgInfo, err error) { - // todo: add your logic here and delete this line + // 检查编码唯一性 + existing, _ := model.OrgFindOneByCode(l.ctx, l.svcCtx.DB, req.Code) + if existing != nil { + return nil, fmt.Errorf("机构编码 %s 已存在", req.Code) + } + + org := &model.Organization{ + ParentId: req.ParentId, + Name: req.Name, + Code: req.Code, + Leader: req.Leader, + Phone: req.Phone, + Email: req.Email, + SortOrder: req.SortOrder, + Status: 1, + } + + _, err = model.OrgInsert(l.ctx, l.svcCtx.DB, org) + if err != nil { + return nil, fmt.Errorf("创建机构失败: %v", err) + } - return + return &types.OrgInfo{ + Id: org.Id, + ParentId: org.ParentId, + Name: org.Name, + Code: org.Code, + Leader: org.Leader, + Phone: org.Phone, + Email: org.Email, + SortOrder: org.SortOrder, + Status: org.Status, + MemberCount: 0, + Children: []types.OrgInfo{}, + CreatedAt: org.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: org.UpdatedAt.Format("2006-01-02 15:04:05"), + }, nil } diff --git a/backend/internal/logic/organization/deleteorganizationlogic.go b/backend/internal/logic/organization/deleteorganizationlogic.go index e1d2093..bb16a2b 100644 --- a/backend/internal/logic/organization/deleteorganizationlogic.go +++ b/backend/internal/logic/organization/deleteorganizationlogic.go @@ -5,9 +5,11 @@ package organization import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,40 @@ func NewDeleteOrganizationLogic(ctx context.Context, svcCtx *svc.ServiceContext) } func (l *DeleteOrganizationLogic) DeleteOrganization(req *types.DeleteOrgRequest) (resp *types.Response, err error) { - // todo: add your logic here and delete this line + // 检查是否有子机构 + hasChildren, err := model.OrgHasChildren(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("检查子机构失败: %v", err) + } + if hasChildren { + return &types.Response{ + Code: 400, + Message: "该机构下有子机构,无法删除", + Success: false, + }, nil + } + + // 检查是否有成员 + memberCount, err := model.UserOrgCountByOrgId(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("检查机构成员失败: %v", err) + } + if memberCount > 0 { + return &types.Response{ + Code: 400, + Message: "该机构下有成员,无法删除", + Success: false, + }, nil + } + + err = model.OrgDelete(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("删除机构失败: %v", err) + } - return + return &types.Response{ + Code: 0, + Message: "删除成功", + Success: true, + }, nil } diff --git a/backend/internal/logic/organization/getorganizationlistlogic.go b/backend/internal/logic/organization/getorganizationlistlogic.go index 8435830..5e9ca68 100644 --- a/backend/internal/logic/organization/getorganizationlistlogic.go +++ b/backend/internal/logic/organization/getorganizationlistlogic.go @@ -5,11 +5,14 @@ package organization import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" + "gorm.io/gorm" ) type GetOrganizationListLogic struct { @@ -28,7 +31,43 @@ func NewGetOrganizationListLogic(ctx context.Context, svcCtx *svc.ServiceContext } func (l *GetOrganizationListLogic) GetOrganizationList() (resp *types.OrgListResponse, err error) { - // todo: add your logic here and delete this line + orgs, err := model.OrgFindAll(l.ctx, l.svcCtx.DB) + if err != nil { + return nil, fmt.Errorf("获取机构列表失败: %v", err) + } + + tree := buildOrgTree(l.ctx, l.svcCtx.DB, orgs, 0) + + return &types.OrgListResponse{ + List: tree, + }, nil +} - return +func buildOrgTree(ctx context.Context, db *gorm.DB, orgs []model.Organization, parentId int64) []types.OrgInfo { + var tree []types.OrgInfo + for _, o := range orgs { + if o.ParentId == parentId { + memberCount, _ := model.UserOrgCountByOrgId(ctx, db, o.Id) + item := types.OrgInfo{ + Id: o.Id, + ParentId: o.ParentId, + Name: o.Name, + Code: o.Code, + Leader: o.Leader, + Phone: o.Phone, + Email: o.Email, + SortOrder: o.SortOrder, + Status: o.Status, + MemberCount: memberCount, + Children: buildOrgTree(ctx, db, orgs, o.Id), + CreatedAt: o.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: o.UpdatedAt.Format("2006-01-02 15:04:05"), + } + tree = append(tree, item) + } + } + if tree == nil { + tree = []types.OrgInfo{} + } + return tree } diff --git a/backend/internal/logic/organization/getorgmemberslogic.go b/backend/internal/logic/organization/getorgmemberslogic.go index a9642e7..45263d6 100644 --- a/backend/internal/logic/organization/getorgmemberslogic.go +++ b/backend/internal/logic/organization/getorgmemberslogic.go @@ -5,9 +5,11 @@ package organization import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,42 @@ func NewGetOrgMembersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get } func (l *GetOrgMembersLogic) GetOrgMembers(req *types.GetOrgMembersRequest) (resp *types.OrgMembersResponse, err error) { - // todo: add your logic here and delete this line + userOrgs, err := model.UserOrgFindByOrgId(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("获取机构成员失败: %v", err) + } + + list := make([]types.OrgMember, 0, len(userOrgs)) + for _, uo := range userOrgs { + // 查询用户信息 + user, err := model.FindOne(l.ctx, l.svcCtx.DB, uo.UserId) + if err != nil { + continue + } + + // 查询角色信息 + var roleName, roleCode string + if uo.RoleId > 0 { + role, err := model.RoleFindOne(l.ctx, l.svcCtx.DB, uo.RoleId) + if err == nil { + roleName = role.Name + roleCode = role.Code + } + } + + list = append(list, types.OrgMember{ + UserId: user.Id, + Username: user.Username, + Email: user.Email, + Phone: user.Phone, + RoleId: uo.RoleId, + RoleName: roleName, + RoleCode: roleCode, + CreatedAt: uo.CreatedAt.Format("2006-01-02 15:04:05"), + }) + } - return + return &types.OrgMembersResponse{ + List: list, + }, nil } diff --git a/backend/internal/logic/organization/removeorgmemberlogic.go b/backend/internal/logic/organization/removeorgmemberlogic.go index 31abe72..5e8badc 100644 --- a/backend/internal/logic/organization/removeorgmemberlogic.go +++ b/backend/internal/logic/organization/removeorgmemberlogic.go @@ -5,9 +5,11 @@ package organization import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,14 @@ func NewRemoveOrgMemberLogic(ctx context.Context, svcCtx *svc.ServiceContext) *R } func (l *RemoveOrgMemberLogic) RemoveOrgMember(req *types.RemoveOrgMemberRequest) (resp *types.Response, err error) { - // todo: add your logic here and delete this line + err = model.UserOrgDelete(l.ctx, l.svcCtx.DB, req.UserId, req.Id) + if err != nil { + return nil, fmt.Errorf("移除成员失败: %v", err) + } - return + return &types.Response{ + Code: 0, + Message: "移除成功", + Success: true, + }, nil } diff --git a/backend/internal/logic/organization/updateorganizationlogic.go b/backend/internal/logic/organization/updateorganizationlogic.go index 78301cc..5a4b322 100644 --- a/backend/internal/logic/organization/updateorganizationlogic.go +++ b/backend/internal/logic/organization/updateorganizationlogic.go @@ -5,9 +5,11 @@ package organization import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,56 @@ func NewUpdateOrganizationLogic(ctx context.Context, svcCtx *svc.ServiceContext) } func (l *UpdateOrganizationLogic) UpdateOrganization(req *types.UpdateOrgRequest) (resp *types.OrgInfo, err error) { - // todo: add your logic here and delete this line + org, err := model.OrgFindOne(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("机构不存在: %v", err) + } + + if req.ParentId != nil { + org.ParentId = *req.ParentId + } + if req.Name != "" { + org.Name = req.Name + } + if req.Code != "" { + org.Code = req.Code + } + if req.Leader != "" { + org.Leader = req.Leader + } + if req.Phone != "" { + org.Phone = req.Phone + } + if req.Email != "" { + org.Email = req.Email + } + if req.SortOrder != nil { + org.SortOrder = *req.SortOrder + } + if req.Status != nil { + org.Status = *req.Status + } + + err = model.OrgUpdate(l.ctx, l.svcCtx.DB, org) + if err != nil { + return nil, fmt.Errorf("更新机构失败: %v", err) + } + + memberCount, _ := model.UserOrgCountByOrgId(l.ctx, l.svcCtx.DB, org.Id) - return + return &types.OrgInfo{ + Id: org.Id, + ParentId: org.ParentId, + Name: org.Name, + Code: org.Code, + Leader: org.Leader, + Phone: org.Phone, + Email: org.Email, + SortOrder: org.SortOrder, + Status: org.Status, + MemberCount: memberCount, + Children: []types.OrgInfo{}, + CreatedAt: org.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: org.UpdatedAt.Format("2006-01-02 15:04:05"), + }, nil } diff --git a/backend/internal/logic/organization/updateorgmemberlogic.go b/backend/internal/logic/organization/updateorgmemberlogic.go index f559bef..573b6a0 100644 --- a/backend/internal/logic/organization/updateorgmemberlogic.go +++ b/backend/internal/logic/organization/updateorgmemberlogic.go @@ -5,9 +5,11 @@ package organization import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,22 @@ func NewUpdateOrgMemberLogic(ctx context.Context, svcCtx *svc.ServiceContext) *U } func (l *UpdateOrgMemberLogic) UpdateOrgMember(req *types.UpdateOrgMemberRequest) (resp *types.Response, err error) { - // todo: add your logic here and delete this line + // 查找用户-机构关联 + uo, err := model.UserOrgFindOne(l.ctx, l.svcCtx.DB, req.UserId, req.Id) + if err != nil { + return nil, fmt.Errorf("该用户不是该机构成员: %v", err) + } + + // 更新角色 + uo.RoleId = req.RoleId + err = model.UserOrgUpdate(l.ctx, l.svcCtx.DB, uo) + if err != nil { + return nil, fmt.Errorf("更新成员角色失败: %v", err) + } - return + return &types.Response{ + Code: 0, + Message: "更新成功", + Success: true, + }, nil } diff --git a/backend/internal/logic/profile/getuserorgslogic.go b/backend/internal/logic/profile/getuserorgslogic.go index 57bc06c..9092fa6 100644 --- a/backend/internal/logic/profile/getuserorgslogic.go +++ b/backend/internal/logic/profile/getuserorgslogic.go @@ -5,9 +5,11 @@ package profile import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,46 @@ func NewGetUserOrgsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUs } func (l *GetUserOrgsLogic) GetUserOrgs() (resp *types.UserOrgsResponse, err error) { - // todo: add your logic here and delete this line + userId, _ := l.ctx.Value("userId").(int64) + if userId == 0 { + return nil, fmt.Errorf("未获取到用户信息") + } + + userOrgs, err := model.UserOrgFindByUserId(l.ctx, l.svcCtx.DB, userId) + if err != nil { + return nil, fmt.Errorf("获取用户机构列表失败: %v", err) + } + + list := make([]types.UserOrgInfo, 0, len(userOrgs)) + for _, uo := range userOrgs { + // 查询机构信息 + org, err := model.OrgFindOne(l.ctx, l.svcCtx.DB, uo.OrgId) + if err != nil { + continue + } + + // 查询角色信息 + var roleName, roleCode string + var roleId int64 + if uo.RoleId > 0 { + role, err := model.RoleFindOne(l.ctx, l.svcCtx.DB, uo.RoleId) + if err == nil { + roleId = role.Id + roleName = role.Name + roleCode = role.Code + } + } + + list = append(list, types.UserOrgInfo{ + OrgId: org.Id, + OrgName: org.Name, + RoleId: roleId, + RoleName: roleName, + RoleCode: roleCode, + }) + } - return + return &types.UserOrgsResponse{ + List: list, + }, nil } diff --git a/backend/internal/logic/profile/switchorglogic.go b/backend/internal/logic/profile/switchorglogic.go index 1265d96..0234755 100644 --- a/backend/internal/logic/profile/switchorglogic.go +++ b/backend/internal/logic/profile/switchorglogic.go @@ -5,9 +5,12 @@ package profile import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + jwtutil "github.com/youruser/base/internal/util/jwt" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +31,45 @@ func NewSwitchOrgLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SwitchO } func (l *SwitchOrgLogic) SwitchOrg(req *types.SwitchOrgRequest) (resp *types.SwitchOrgResponse, err error) { - // todo: add your logic here and delete this line + userId, _ := l.ctx.Value("userId").(int64) + username, _ := l.ctx.Value("username").(string) + if userId == 0 { + return nil, fmt.Errorf("未获取到用户信息") + } + + // 验证用户属于该机构 + userOrg, err := model.UserOrgFindOne(l.ctx, l.svcCtx.DB, userId, req.OrgId) + if err != nil { + return nil, fmt.Errorf("您不属于该机构") + } + + // 获取角色编码 + roleCode := model.RoleUser // 默认角色 + if userOrg.RoleId > 0 { + role, err := model.RoleFindOne(l.ctx, l.svcCtx.DB, userOrg.RoleId) + if err == nil { + roleCode = role.Code + } + } + + // 更新用户的 CurrentOrgId + user, err := model.FindOne(l.ctx, l.svcCtx.DB, userId) + if err != nil { + return nil, fmt.Errorf("获取用户信息失败: %v", err) + } + user.CurrentOrgId = req.OrgId + err = model.Update(l.ctx, l.svcCtx.DB, user) + if err != nil { + return nil, fmt.Errorf("更新用户机构失败: %v", err) + } + + // 生成新的 JWT Token + token, err := jwtutil.GenerateToken(userId, username, roleCode, req.OrgId) + if err != nil { + return nil, fmt.Errorf("生成Token失败: %v", err) + } - return + return &types.SwitchOrgResponse{ + Token: token, + }, nil } diff --git a/backend/internal/logic/role/createrolelogic.go b/backend/internal/logic/role/createrolelogic.go index 9ce8423..1572362 100644 --- a/backend/internal/logic/role/createrolelogic.go +++ b/backend/internal/logic/role/createrolelogic.go @@ -5,9 +5,11 @@ package role import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,34 @@ func NewCreateRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Create } func (l *CreateRoleLogic) CreateRole(req *types.CreateRoleRequest) (resp *types.RoleInfo, err error) { - // todo: add your logic here and delete this line + // 检查编码唯一性 + existing, _ := model.RoleFindOneByCode(l.ctx, l.svcCtx.DB, req.Code) + if existing != nil { + return nil, fmt.Errorf("角色编码 %s 已存在", req.Code) + } + + role := &model.Role{ + Name: req.Name, + Code: req.Code, + Description: req.Description, + SortOrder: req.SortOrder, + Status: 1, + } + + _, err = model.RoleInsert(l.ctx, l.svcCtx.DB, role) + if err != nil { + return nil, fmt.Errorf("创建角色失败: %v", err) + } - return + return &types.RoleInfo{ + Id: role.Id, + Name: role.Name, + Code: role.Code, + Description: role.Description, + IsSystem: role.IsSystem, + SortOrder: role.SortOrder, + Status: role.Status, + CreatedAt: role.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: role.UpdatedAt.Format("2006-01-02 15:04:05"), + }, nil } diff --git a/backend/internal/logic/role/deleterolelogic.go b/backend/internal/logic/role/deleterolelogic.go index ba381f4..d5edead 100644 --- a/backend/internal/logic/role/deleterolelogic.go +++ b/backend/internal/logic/role/deleterolelogic.go @@ -5,9 +5,11 @@ package role import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,36 @@ func NewDeleteRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete } func (l *DeleteRoleLogic) DeleteRole(req *types.DeleteRoleRequest) (resp *types.Response, err error) { - // todo: add your logic here and delete this line + // 查找角色 + role, err := model.RoleFindOne(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("角色不存在: %v", err) + } + + // 系统角色不允许删除 + if role.IsSystem { + return &types.Response{ + Code: 400, + Message: "系统角色不允许删除", + Success: false, + }, nil + } + + // 删除角色-菜单关联 + err = model.RoleMenuDeleteByRoleId(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("删除角色菜单关联失败: %v", err) + } + + // 删除角色 + err = model.RoleDelete(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("删除角色失败: %v", err) + } - return + return &types.Response{ + Code: 0, + Message: "删除成功", + Success: true, + }, nil } diff --git a/backend/internal/logic/role/getrolelistlogic.go b/backend/internal/logic/role/getrolelistlogic.go index ea84c61..2b557ad 100644 --- a/backend/internal/logic/role/getrolelistlogic.go +++ b/backend/internal/logic/role/getrolelistlogic.go @@ -5,9 +5,11 @@ package role import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,27 @@ func NewGetRoleListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRo } func (l *GetRoleListLogic) GetRoleList() (resp *types.RoleListResponse, err error) { - // todo: add your logic here and delete this line + roles, err := model.RoleFindAll(l.ctx, l.svcCtx.DB) + if err != nil { + return nil, fmt.Errorf("获取角色列表失败: %v", err) + } + + list := make([]types.RoleInfo, 0, len(roles)) + for _, r := range roles { + list = append(list, types.RoleInfo{ + Id: r.Id, + Name: r.Name, + Code: r.Code, + Description: r.Description, + IsSystem: r.IsSystem, + SortOrder: r.SortOrder, + Status: r.Status, + CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: r.UpdatedAt.Format("2006-01-02 15:04:05"), + }) + } - return + return &types.RoleListResponse{ + List: list, + }, nil } diff --git a/backend/internal/logic/role/getrolemenuslogic.go b/backend/internal/logic/role/getrolemenuslogic.go index fc5ae7f..4fc5d69 100644 --- a/backend/internal/logic/role/getrolemenuslogic.go +++ b/backend/internal/logic/role/getrolemenuslogic.go @@ -5,9 +5,11 @@ package role import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,16 @@ func NewGetRoleMenusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetR } func (l *GetRoleMenusLogic) GetRoleMenus(req *types.GetRoleMenusRequest) (resp *types.RoleMenusResponse, err error) { - // todo: add your logic here and delete this line + menuIds, err := model.RoleMenuFindByRoleId(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("获取角色菜单失败: %v", err) + } + + if menuIds == nil { + menuIds = []int64{} + } - return + return &types.RoleMenusResponse{ + MenuIds: menuIds, + }, nil } diff --git a/backend/internal/logic/role/setrolemenuslogic.go b/backend/internal/logic/role/setrolemenuslogic.go index 5acaf80..fb2d270 100644 --- a/backend/internal/logic/role/setrolemenuslogic.go +++ b/backend/internal/logic/role/setrolemenuslogic.go @@ -5,9 +5,11 @@ package role import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,21 @@ func NewSetRoleMenusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SetR } func (l *SetRoleMenusLogic) SetRoleMenus(req *types.SetRoleMenusRequest) (resp *types.Response, err error) { - // todo: add your logic here and delete this line + // 验证角色是否存在 + _, err = model.RoleFindOne(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("角色不存在: %v", err) + } + + // 全量设置角色的菜单 + err = model.RoleMenuSetForRole(l.ctx, l.svcCtx.DB, req.Id, req.MenuIds) + if err != nil { + return nil, fmt.Errorf("设置角色菜单失败: %v", err) + } - return + return &types.Response{ + Code: 0, + Message: "设置成功", + Success: true, + }, nil } diff --git a/backend/internal/logic/role/updaterolelogic.go b/backend/internal/logic/role/updaterolelogic.go index 5271429..2b1d0cb 100644 --- a/backend/internal/logic/role/updaterolelogic.go +++ b/backend/internal/logic/role/updaterolelogic.go @@ -5,9 +5,11 @@ package role import ( "context" + "fmt" "github.com/youruser/base/internal/svc" "github.com/youruser/base/internal/types" + "github.com/youruser/base/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -28,7 +30,38 @@ func NewUpdateRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Update } func (l *UpdateRoleLogic) UpdateRole(req *types.UpdateRoleRequest) (resp *types.RoleInfo, err error) { - // todo: add your logic here and delete this line + role, err := model.RoleFindOne(l.ctx, l.svcCtx.DB, req.Id) + if err != nil { + return nil, fmt.Errorf("角色不存在: %v", err) + } + + if req.Name != "" { + role.Name = req.Name + } + if req.Description != "" { + role.Description = req.Description + } + if req.SortOrder != nil { + role.SortOrder = *req.SortOrder + } + if req.Status != nil { + role.Status = *req.Status + } + + err = model.RoleUpdate(l.ctx, l.svcCtx.DB, role) + if err != nil { + return nil, fmt.Errorf("更新角色失败: %v", err) + } - return + return &types.RoleInfo{ + Id: role.Id, + Name: role.Name, + Code: role.Code, + Description: role.Description, + IsSystem: role.IsSystem, + SortOrder: role.SortOrder, + Status: role.Status, + CreatedAt: role.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: role.UpdatedAt.Format("2006-01-02 15:04:05"), + }, nil } diff --git a/backend/internal/svc/servicecontext.go b/backend/internal/svc/servicecontext.go index 5979251..9e92cff 100644 --- a/backend/internal/svc/servicecontext.go +++ b/backend/internal/svc/servicecontext.go @@ -15,6 +15,7 @@ import ( "github.com/youruser/base/internal/config" "github.com/youruser/base/internal/middleware" + "github.com/youruser/base/internal/storage" "github.com/youruser/base/model" "gorm.io/driver/mysql" @@ -50,6 +51,8 @@ type ServiceContext struct { DB *gorm.DB // Casbin enforcer Enforcer *casbin.Enforcer + // 文件存储 + Storage storage.Storage } func NewServiceContext(c config.Config) *ServiceContext { @@ -61,7 +64,7 @@ func NewServiceContext(c config.Config) *ServiceContext { } // 自动迁移表 - err = db.AutoMigrate(&model.User{}, &model.Profile{}) + err = db.AutoMigrate(&model.User{}, &model.Profile{}, &model.File{}, &model.Menu{}, &model.Role{}, &model.RoleMenu{}, &model.Organization{}, &model.UserOrganization{}) if err != nil { panic("Failed to migrate database: " + err.Error()) } @@ -75,6 +78,18 @@ func NewServiceContext(c config.Config) *ServiceContext { // 种子 Casbin 策略 seedCasbinPolicies(enforcer) + // 种子角色、菜单、角色-菜单关联 + seedRoles(db) + seedMenus(db) + seedRoleMenus(db) + + // 初始化存储 + store, err := storage.NewStorage(c.Storage) + if err != nil { + panic("Failed to initialize storage: " + err.Error()) + } + log.Printf("[Storage] Initialized with type: %s", c.Storage.Type) + return &ServiceContext{ Config: c, Cors: middleware.NewCorsMiddleware().Handle, @@ -83,6 +98,7 @@ func NewServiceContext(c config.Config) *ServiceContext { Authz: middleware.NewAuthzMiddleware(enforcer).Handle, DB: db, Enforcer: enforcer, + Storage: store, } } @@ -193,6 +209,50 @@ func seedCasbinPolicies(enforcer *casbin.Enforcer) { // super_admin: 用户删除 {"super_admin", "/api/v1/user/:id", "DELETE"}, + + // user: 文件管理 + {"user", "/api/v1/file/upload", "POST"}, + {"user", "/api/v1/files", "GET"}, + {"user", "/api/v1/file/:id", "GET"}, + {"user", "/api/v1/file/:id/url", "GET"}, + {"user", "/api/v1/file/:id", "PUT"}, + + // super_admin: 文件删除 + {"super_admin", "/api/v1/file/:id", "DELETE"}, + + // user: 个人机构相关 + {"user", "/api/v1/profile/orgs", "GET"}, + {"user", "/api/v1/profile/current-org", "PUT"}, + + // admin: 菜单管理(读取) + {"admin", "/api/v1/menus", "GET"}, + + // super_admin: 菜单管理(增删改) + {"super_admin", "/api/v1/menu", "POST"}, + {"super_admin", "/api/v1/menu/:id", "PUT"}, + {"super_admin", "/api/v1/menu/:id", "DELETE"}, + + // admin: 角色管理(读取) + {"admin", "/api/v1/roles", "GET"}, + {"admin", "/api/v1/role/:id/menus", "GET"}, + + // super_admin: 角色管理(增删改) + {"super_admin", "/api/v1/role", "POST"}, + {"super_admin", "/api/v1/role/:id", "PUT"}, + {"super_admin", "/api/v1/role/:id", "DELETE"}, + {"super_admin", "/api/v1/role/:id/menus", "PUT"}, + + // admin: 机构管理 + {"admin", "/api/v1/organizations", "GET"}, + {"admin", "/api/v1/organization", "POST"}, + {"admin", "/api/v1/organization/:id", "PUT"}, + {"admin", "/api/v1/organization/:id/members", "GET"}, + {"admin", "/api/v1/organization/:id/member", "POST"}, + {"admin", "/api/v1/organization/:id/member/:userId", "PUT"}, + {"admin", "/api/v1/organization/:id/member/:userId", "DELETE"}, + + // super_admin: 机构删除 + {"super_admin", "/api/v1/organization/:id", "DELETE"}, } for _, p := range policies { @@ -204,3 +264,94 @@ func seedCasbinPolicies(enforcer *casbin.Enforcer) { enforcer.SavePolicy() log.Println("[Casbin] Policies seeded successfully") } + +// seedRoles 种子系统角色(幂等) +func seedRoles(db *gorm.DB) { + roles := []model.Role{ + {Name: "超级管理员", Code: model.RoleSuperAdmin, Description: "系统超级管理员", IsSystem: true, SortOrder: 1, Status: 1}, + {Name: "管理员", Code: model.RoleAdmin, Description: "系统管理员", IsSystem: true, SortOrder: 2, Status: 1}, + {Name: "普通用户", Code: model.RoleUser, Description: "普通用户", IsSystem: true, SortOrder: 3, Status: 1}, + {Name: "访客", Code: model.RoleGuest, Description: "访客", IsSystem: true, SortOrder: 4, Status: 1}, + } + + for _, r := range roles { + var existing model.Role + if err := db.Where("code = ?", r.Code).First(&existing).Error; err != nil { + db.Create(&r) + } + } + log.Println("[Seed] Roles seeded successfully") +} + +// seedMenus 种子默认菜单(幂等) +func seedMenus(db *gorm.DB) { + menus := []model.Menu{ + {Name: "我的", Path: "/my", Icon: "User", Type: "default", SortOrder: 1, Visible: true, Status: 1}, + {Name: "仪表盘", Path: "/dashboard", Icon: "LayoutDashboard", Type: "config", SortOrder: 2, Visible: true, Status: 1}, + {Name: "用户管理", Path: "/users", Icon: "Users", Type: "config", SortOrder: 3, Visible: true, Status: 1}, + {Name: "文件管理", Path: "/files", Icon: "FolderOpen", Type: "config", SortOrder: 4, Visible: true, Status: 1}, + {Name: "角色管理", Path: "/roles", Icon: "Shield", Type: "config", SortOrder: 5, Visible: true, Status: 1}, + {Name: "菜单管理", Path: "/menus", Icon: "Menu", Type: "config", SortOrder: 6, Visible: true, Status: 1}, + {Name: "机构管理", Path: "/organizations", Icon: "Building2", Type: "config", SortOrder: 7, Visible: true, Status: 1}, + {Name: "设置", Path: "/settings", Icon: "Settings", Type: "default", SortOrder: 8, Visible: true, Status: 1}, + } + + for _, m := range menus { + var existing model.Menu + if err := db.Where("path = ?", m.Path).First(&existing).Error; err != nil { + db.Create(&m) + } + } + log.Println("[Seed] Menus seeded successfully") +} + +// seedRoleMenus 种子角色-菜单关联(幂等) +func seedRoleMenus(db *gorm.DB) { + // 获取所有角色 + var roles []model.Role + db.Find(&roles) + + // 获取所有菜单 + var menus []model.Menu + db.Find(&menus) + + if len(roles) == 0 || len(menus) == 0 { + return + } + + // 构建菜单分类 + var allMenuIds []int64 + var defaultMenuIds []int64 + for _, m := range menus { + allMenuIds = append(allMenuIds, m.Id) + if m.Type == "default" { + defaultMenuIds = append(defaultMenuIds, m.Id) + } + } + + for _, r := range roles { + // 检查角色是否已有菜单关联 + var count int64 + db.Model(&model.RoleMenu{}).Where("role_id = ?", r.Id).Count(&count) + if count > 0 { + continue + } + + var menuIds []int64 + switch r.Code { + case model.RoleSuperAdmin, model.RoleAdmin: + menuIds = allMenuIds + case model.RoleUser, model.RoleGuest: + menuIds = defaultMenuIds + } + + if len(menuIds) > 0 { + records := make([]model.RoleMenu, 0, len(menuIds)) + for _, menuId := range menuIds { + records = append(records, model.RoleMenu{RoleId: r.Id, MenuId: menuId}) + } + db.Create(&records) + } + } + log.Println("[Seed] RoleMenus seeded successfully") +}