From 1ef88c17068aa94b4a32dd477fc5853f32d184fb Mon Sep 17 00:00:00 2001 From: hejl Date: Tue, 9 Sep 2025 11:10:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=E9=87=8D=E5=A4=8D=E8=A1=A8?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../route-sync/direct-route-mappings.js | 2 +- .../auth/controller/menu_group_controller.go | 354 +++++++++++++++++ .../auth/controller/menu_route_controller.go | 366 ------------------ .../auth/migration/create_route_tables.go | 174 +-------- gofaster/backend/internal/auth/model/menu.go | 27 -- .../backend/internal/auth/model/menu_group.go | 20 + .../backend/internal/auth/model/menu_route.go | 20 - .../internal/auth/model/route_mapping.go | 22 -- gofaster/backend/internal/auth/module.go | 18 +- .../auth/repository/menu_group_repo.go | 127 ++++++ .../internal/auth/repository/menu_repo.go | 89 ----- .../auth/repository/menu_route_repo.go | 142 ------- .../auth/repository/route_mapping_repo.go | 108 ------ .../internal/auth/routes/menu_group_routes.go | 45 +++ .../internal/auth/routes/menu_route_routes.go | 52 --- .../internal/auth/routes/route_sync_routes.go | 4 +- .../auth/service/menu_group_service.go | 229 +++++++++++ .../auth/service/menu_route_service.go | 180 --------- .../auth/service/route_sync_service.go | 18 +- 19 files changed, 799 insertions(+), 1198 deletions(-) create mode 100644 gofaster/backend/internal/auth/controller/menu_group_controller.go delete mode 100644 gofaster/backend/internal/auth/controller/menu_route_controller.go delete mode 100644 gofaster/backend/internal/auth/model/menu.go create mode 100644 gofaster/backend/internal/auth/model/menu_group.go delete mode 100644 gofaster/backend/internal/auth/model/menu_route.go delete mode 100644 gofaster/backend/internal/auth/model/route_mapping.go create mode 100644 gofaster/backend/internal/auth/repository/menu_group_repo.go delete mode 100644 gofaster/backend/internal/auth/repository/menu_repo.go delete mode 100644 gofaster/backend/internal/auth/repository/menu_route_repo.go delete mode 100644 gofaster/backend/internal/auth/repository/route_mapping_repo.go create mode 100644 gofaster/backend/internal/auth/routes/menu_group_routes.go delete mode 100644 gofaster/backend/internal/auth/routes/menu_route_routes.go create mode 100644 gofaster/backend/internal/auth/service/menu_group_service.go delete mode 100644 gofaster/backend/internal/auth/service/menu_route_service.go diff --git a/gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js b/gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js index 0f2f632..bf6d98a 100644 --- a/gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js +++ b/gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js @@ -252,7 +252,7 @@ export default { "id": 15, "component": "PasswordChangeModal", "module": "user-management", - "triggerName": "passwordchangemodal-input-zhkdt8", + "triggerName": "passwordchangemodal-input-8m1x12", "triggerType": "input" } ] diff --git a/gofaster/backend/internal/auth/controller/menu_group_controller.go b/gofaster/backend/internal/auth/controller/menu_group_controller.go new file mode 100644 index 0000000..e5ccc3f --- /dev/null +++ b/gofaster/backend/internal/auth/controller/menu_group_controller.go @@ -0,0 +1,354 @@ +package controller + +import ( + "net/http" + "strconv" + "gofaster/internal/auth/model" + "gofaster/internal/auth/service" + "gofaster/internal/shared/response" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +// MenuGroupController 菜单分组控制器 +type MenuGroupController struct { + menuGroupService *service.MenuGroupService + logger *zap.Logger +} + +// NewMenuGroupController 创建菜单分组控制器实例 +func NewMenuGroupController(menuGroupService *service.MenuGroupService, logger *zap.Logger) *MenuGroupController { + return &MenuGroupController{ + menuGroupService: menuGroupService, + logger: logger, + } +} + +// CreateMenuGroup 创建菜单分组 +// @Summary 创建菜单分组 +// @Description 创建新的菜单分组 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param menuGroup body model.MenuGroup true "菜单分组信息" +// @Success 200 {object} response.Response{data=model.MenuGroup} +// @Failure 400 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /api/menu-groups [post] +func (c *MenuGroupController) CreateMenuGroup(ctx *gin.Context) { + var menuGroup model.MenuGroup + if err := ctx.ShouldBindJSON(&menuGroup); err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) + return + } + + // 验证必填字段 + if menuGroup.Name == "" { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "分组名称不能为空") + return + } + + if err := c.menuGroupService.CreateMenuGroup(&menuGroup); err != nil { + response.Error(ctx, http.StatusInternalServerError, "创建菜单分组失败", err.Error()) + return + } + + response.Success(ctx, "创建菜单分组成功", menuGroup) +} + +// GetMenuGroup 获取菜单分组详情 +// @Summary 获取菜单分组详情 +// @Description 根据ID获取菜单分组详情 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param id path int true "菜单分组ID" +// @Success 200 {object} response.Response{data=model.MenuGroup} +// @Failure 400 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /api/menu-groups/{id} [get] +func (c *MenuGroupController) GetMenuGroup(ctx *gin.Context) { + idStr := ctx.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "ID格式错误") + return + } + + menuGroup, err := c.menuGroupService.GetMenuGroupByID(uint(id)) + if err != nil { + response.Error(ctx, http.StatusNotFound, "菜单分组不存在", err.Error()) + return + } + + response.Success(ctx, "获取菜单分组成功", menuGroup) +} + +// ListMenuGroups 获取菜单分组列表 +// @Summary 获取菜单分组列表 +// @Description 获取所有菜单分组列表 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param status query int false "状态筛选" Enums(0,1) +// @Success 200 {object} response.Response{data=[]model.MenuGroup} +// @Failure 500 {object} response.Response +// @Router /api/menu-groups [get] +func (c *MenuGroupController) ListMenuGroups(ctx *gin.Context) { + statusStr := ctx.Query("status") + + var menuGroups []*model.MenuGroup + var err error + + if statusStr != "" { + status, parseErr := strconv.Atoi(statusStr) + if parseErr != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "状态参数格式错误") + return + } + menuGroups, err = c.menuGroupService.ListMenuGroupsByStatus(status) + } else { + menuGroups, err = c.menuGroupService.ListMenuGroups() + } + + if err != nil { + response.Error(ctx, http.StatusInternalServerError, "获取菜单分组列表失败", err.Error()) + return + } + + response.Success(ctx, "获取菜单分组列表成功", menuGroups) +} + +// UpdateMenuGroup 更新菜单分组 +// @Summary 更新菜单分组 +// @Description 更新菜单分组信息 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param id path int true "菜单分组ID" +// @Param menuGroup body model.MenuGroup true "菜单分组信息" +// @Success 200 {object} response.Response{data=model.MenuGroup} +// @Failure 400 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /api/menu-groups/{id} [put] +func (c *MenuGroupController) UpdateMenuGroup(ctx *gin.Context) { + idStr := ctx.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "ID格式错误") + return + } + + var menuGroup model.MenuGroup + if err := ctx.ShouldBindJSON(&menuGroup); err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) + return + } + + // 验证必填字段 + if menuGroup.Name == "" { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "分组名称不能为空") + return + } + + menuGroup.ID = uint(id) + + if err := c.menuGroupService.UpdateMenuGroup(&menuGroup); err != nil { + response.Error(ctx, http.StatusInternalServerError, "更新菜单分组失败", err.Error()) + return + } + + response.Success(ctx, "更新菜单分组成功", menuGroup) +} + +// DeleteMenuGroup 删除菜单分组 +// @Summary 删除菜单分组 +// @Description 根据ID删除菜单分组 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param id path int true "菜单分组ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /api/menu-groups/{id} [delete] +func (c *MenuGroupController) DeleteMenuGroup(ctx *gin.Context) { + idStr := ctx.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "ID格式错误") + return + } + + if err := c.menuGroupService.DeleteMenuGroup(uint(id)); err != nil { + response.Error(ctx, http.StatusInternalServerError, "删除菜单分组失败", err.Error()) + return + } + + response.Success(ctx, "删除菜单分组成功", nil) +} + +// BatchDeleteMenuGroups 批量删除菜单分组 +// @Summary 批量删除菜单分组 +// @Description 批量删除菜单分组 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param ids body []uint true "菜单分组ID列表" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /api/menu-groups/batch-delete [post] +func (c *MenuGroupController) BatchDeleteMenuGroups(ctx *gin.Context) { + var request struct { + IDs []uint `json:"ids" binding:"required"` + } + + if err := ctx.ShouldBindJSON(&request); err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) + return + } + + if len(request.IDs) == 0 { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "ID列表不能为空") + return + } + + if err := c.menuGroupService.BatchDeleteMenuGroups(request.IDs); err != nil { + response.Error(ctx, http.StatusInternalServerError, "批量删除菜单分组失败", err.Error()) + return + } + + response.Success(ctx, "批量删除菜单分组成功", nil) +} + +// UpdateMenuGroupStatus 更新菜单分组状态 +// @Summary 更新菜单分组状态 +// @Description 更新菜单分组状态 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param id path int true "菜单分组ID" +// @Param request body map[string]int true "状态信息" SchemaExample({"status": 1}) +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /api/menu-groups/{id}/status [put] +func (c *MenuGroupController) UpdateMenuGroupStatus(ctx *gin.Context) { + idStr := ctx.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "ID格式错误") + return + } + + var request struct { + Status int `json:"status" binding:"required"` + } + + if err := ctx.ShouldBindJSON(&request); err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) + return + } + + if err := c.menuGroupService.UpdateMenuGroupStatus(uint(id), request.Status); err != nil { + response.Error(ctx, http.StatusInternalServerError, "更新菜单分组状态失败", err.Error()) + return + } + + response.Success(ctx, "更新菜单分组状态成功", nil) +} + +// UpdateMenuGroupSort 更新菜单分组排序 +// @Summary 更新菜单分组排序 +// @Description 更新菜单分组排序 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param id path int true "菜单分组ID" +// @Param request body map[string]int true "排序信息" SchemaExample({"sort": 1}) +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /api/menu-groups/{id}/sort [put] +func (c *MenuGroupController) UpdateMenuGroupSort(ctx *gin.Context) { + idStr := ctx.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "ID格式错误") + return + } + + var request struct { + Sort int `json:"sort" binding:"required"` + } + + if err := ctx.ShouldBindJSON(&request); err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) + return + } + + if err := c.menuGroupService.UpdateMenuGroupSort(uint(id), request.Sort); err != nil { + response.Error(ctx, http.StatusInternalServerError, "更新菜单分组排序失败", err.Error()) + return + } + + response.Success(ctx, "更新菜单分组排序成功", nil) +} + +// BatchUpdateMenuGroupSort 批量更新菜单分组排序 +// @Summary 批量更新菜单分组排序 +// @Description 批量更新菜单分组排序 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Param request body map[uint]int true "排序信息" SchemaExample({"1": 1, "2": 2}) +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /api/menu-groups/batch-sort [put] +func (c *MenuGroupController) BatchUpdateMenuGroupSort(ctx *gin.Context) { + var request map[uint]int + + if err := ctx.ShouldBindJSON(&request); err != nil { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) + return + } + + if len(request) == 0 { + response.Error(ctx, http.StatusBadRequest, "请求参数错误", "排序数据不能为空") + return + } + + if err := c.menuGroupService.BatchUpdateMenuGroupSort(request); err != nil { + response.Error(ctx, http.StatusInternalServerError, "批量更新菜单分组排序失败", err.Error()) + return + } + + response.Success(ctx, "批量更新菜单分组排序成功", nil) +} + +// GetMenuGroupStats 获取菜单分组统计信息 +// @Summary 获取菜单分组统计信息 +// @Description 获取菜单分组统计信息 +// @Tags 菜单分组管理 +// @Accept json +// @Produce json +// @Success 200 {object} response.Response{data=map[string]interface{}} +// @Failure 500 {object} response.Response +// @Router /api/menu-groups/stats [get] +func (c *MenuGroupController) GetMenuGroupStats(ctx *gin.Context) { + stats, err := c.menuGroupService.GetMenuGroupStats() + if err != nil { + response.Error(ctx, http.StatusInternalServerError, "获取菜单分组统计信息失败", err.Error()) + return + } + + response.Success(ctx, "获取菜单分组统计信息成功", stats) +} diff --git a/gofaster/backend/internal/auth/controller/menu_route_controller.go b/gofaster/backend/internal/auth/controller/menu_route_controller.go deleted file mode 100644 index ad62b35..0000000 --- a/gofaster/backend/internal/auth/controller/menu_route_controller.go +++ /dev/null @@ -1,366 +0,0 @@ -package controller - -import ( - "net/http" - "strconv" - - "gofaster/internal/auth/model" - "gofaster/internal/auth/service" - "gofaster/internal/shared/response" - - "github.com/gin-gonic/gin" - "go.uber.org/zap" -) - -// MenuRouteController 菜单路由关联表控制器 -type MenuRouteController struct { - menuRouteService *service.MenuRouteService - log *zap.Logger -} - -// NewMenuRouteController 创建菜单路由关联表控制器 -func NewMenuRouteController(menuRouteService *service.MenuRouteService, log *zap.Logger) *MenuRouteController { - return &MenuRouteController{ - menuRouteService: menuRouteService, - log: log, - } -} - -// CreateMenuRoute 创建菜单路由关联 -// @Summary 创建菜单路由关联 -// @Description 创建菜单与路由的多对多关联关系 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param menuRoute body model.MenuRoute true "菜单路由关联信息" -// @Success 200 {object} response.Response{data=model.MenuRoute} -// @Failure 400 {object} response.Response -// @Router /api/menu-routes [post] -func (c *MenuRouteController) CreateMenuRoute(ctx *gin.Context) { - var menuRoute model.MenuRoute - if err := ctx.ShouldBindJSON(&menuRoute); err != nil { - response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) - return - } - - if err := c.menuRouteService.CreateMenuRoute(&menuRoute); err != nil { - response.Error(ctx, http.StatusInternalServerError, "创建菜单路由关联失败", err.Error()) - return - } - - response.Success(ctx, "创建菜单路由关联成功", menuRoute) -} - -// CreateMenuRoutes 批量创建菜单路由关联 -// @Summary 批量创建菜单路由关联 -// @Description 为指定菜单批量创建路由关联 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param menuID path int true "菜单ID" -// @Param routeMappingIDs body []uint true "路由映射ID列表" -// @Success 200 {object} response.Response -// @Failure 400 {object} response.Response -// @Router /api/menus/{menuID}/routes [post] -func (c *MenuRouteController) CreateMenuRoutes(ctx *gin.Context) { - menuIDStr := ctx.Param("menuID") - menuID, err := strconv.ParseUint(menuIDStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "菜单ID格式错误", err.Error()) - return - } - - var routeMappingIDs []uint - if err := ctx.ShouldBindJSON(&routeMappingIDs); err != nil { - response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) - return - } - - if err := c.menuRouteService.CreateMenuRoutes(uint(menuID), routeMappingIDs); err != nil { - response.Error(ctx, http.StatusInternalServerError, "批量创建菜单路由关联失败", err.Error()) - return - } - - response.Success(ctx, "批量创建菜单路由关联成功", nil) -} - -// GetMenuRoutes 获取菜单的路由关联 -// @Summary 获取菜单的路由关联 -// @Description 获取指定菜单的所有路由关联 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param menuID path int true "菜单ID" -// @Success 200 {object} response.Response{data=[]model.MenuRoute} -// @Failure 400 {object} response.Response -// @Router /api/menus/{menuID}/routes [get] -func (c *MenuRouteController) GetMenuRoutes(ctx *gin.Context) { - menuIDStr := ctx.Param("menuID") - menuID, err := strconv.ParseUint(menuIDStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "菜单ID格式错误", err.Error()) - return - } - - menuRoutes, err := c.menuRouteService.GetMenuRoutes(uint(menuID)) - if err != nil { - response.Error(ctx, http.StatusInternalServerError, "获取菜单路由关联失败", err.Error()) - return - } - - response.Success(ctx, "获取菜单路由关联成功", menuRoutes) -} - -// GetRouteMenus 获取路由的菜单关联 -// @Summary 获取路由的菜单关联 -// @Description 获取指定路由的所有菜单关联 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param routeMappingID path int true "路由映射ID" -// @Success 200 {object} response.Response{data=[]model.MenuRoute} -// @Failure 400 {object} response.Response -// @Router /api/route-mappings/{routeMappingID}/menus [get] -func (c *MenuRouteController) GetRouteMenus(ctx *gin.Context) { - routeMappingIDStr := ctx.Param("routeMappingID") - routeMappingID, err := strconv.ParseUint(routeMappingIDStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "路由映射ID格式错误", err.Error()) - return - } - - menuRoutes, err := c.menuRouteService.GetRouteMenus(uint(routeMappingID)) - if err != nil { - response.Error(ctx, http.StatusInternalServerError, "获取路由菜单关联失败", err.Error()) - return - } - - response.Success(ctx, "获取路由菜单关联成功", menuRoutes) -} - -// GetMenuWithRoutes 获取菜单及其关联的路由信息 -// @Summary 获取菜单及其关联的路由信息 -// @Description 获取菜单详细信息及其关联的所有路由 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param menuID path int true "菜单ID" -// @Success 200 {object} response.Response{data=map[string]interface{}} -// @Failure 400 {object} response.Response -// @Router /api/menus/{menuID}/routes/detail [get] -func (c *MenuRouteController) GetMenuWithRoutes(ctx *gin.Context) { - menuIDStr := ctx.Param("menuID") - menuID, err := strconv.ParseUint(menuIDStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "菜单ID格式错误", err.Error()) - return - } - - menu, routes, err := c.menuRouteService.GetMenuWithRoutes(uint(menuID)) - if err != nil { - response.Error(ctx, http.StatusInternalServerError, "获取菜单及路由信息失败", err.Error()) - return - } - - result := map[string]interface{}{ - "menu": menu, - "routes": routes, - } - - response.Success(ctx, "获取菜单及路由信息成功", result) -} - -// GetRouteWithMenus 获取路由及其关联的菜单信息 -// @Summary 获取路由及其关联的菜单信息 -// @Description 获取路由详细信息及其关联的所有菜单 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param routeMappingID path int true "路由映射ID" -// @Success 200 {object} response.Response{data=map[string]interface{}} -// @Failure 400 {object} response.Response -// @Router /api/route-mappings/{routeMappingID}/menus/detail [get] -func (c *MenuRouteController) GetRouteWithMenus(ctx *gin.Context) { - routeMappingIDStr := ctx.Param("routeMappingID") - routeMappingID, err := strconv.ParseUint(routeMappingIDStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "路由映射ID格式错误", err.Error()) - return - } - - route, menus, err := c.menuRouteService.GetRouteWithMenus(uint(routeMappingID)) - if err != nil { - response.Error(ctx, http.StatusInternalServerError, "获取路由及菜单信息失败", err.Error()) - return - } - - result := map[string]interface{}{ - "route": route, - "menus": menus, - } - - response.Success(ctx, "获取路由及菜单信息成功", result) -} - -// UpdateMenuRoute 更新菜单路由关联 -// @Summary 更新菜单路由关联 -// @Description 更新菜单路由关联信息 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param id path int true "关联ID" -// @Param menuRoute body model.MenuRoute true "菜单路由关联信息" -// @Success 200 {object} response.Response{data=model.MenuRoute} -// @Failure 400 {object} response.Response -// @Router /api/menu-routes/{id} [put] -func (c *MenuRouteController) UpdateMenuRoute(ctx *gin.Context) { - idStr := ctx.Param("id") - id, err := strconv.ParseUint(idStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "ID格式错误", err.Error()) - return - } - - var menuRoute model.MenuRoute - if err := ctx.ShouldBindJSON(&menuRoute); err != nil { - response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) - return - } - - menuRoute.ID = uint(id) - if err := c.menuRouteService.UpdateMenuRoute(&menuRoute); err != nil { - response.Error(ctx, http.StatusInternalServerError, "更新菜单路由关联失败", err.Error()) - return - } - - response.Success(ctx, "更新菜单路由关联成功", menuRoute) -} - -// DeleteMenuRoute 删除菜单路由关联 -// @Summary 删除菜单路由关联 -// @Description 删除指定的菜单路由关联 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param id path int true "关联ID" -// @Success 200 {object} response.Response -// @Failure 400 {object} response.Response -// @Router /api/menu-routes/{id} [delete] -func (c *MenuRouteController) DeleteMenuRoute(ctx *gin.Context) { - idStr := ctx.Param("id") - id, err := strconv.ParseUint(idStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "ID格式错误", err.Error()) - return - } - - if err := c.menuRouteService.DeleteMenuRoute(uint(id)); err != nil { - response.Error(ctx, http.StatusInternalServerError, "删除菜单路由关联失败", err.Error()) - return - } - - response.Success(ctx, "删除菜单路由关联成功", nil) -} - -// DeleteMenuRoutes 删除菜单的所有路由关联 -// @Summary 删除菜单的所有路由关联 -// @Description 删除指定菜单的所有路由关联 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param menuID path int true "菜单ID" -// @Success 200 {object} response.Response -// @Failure 400 {object} response.Response -// @Router /api/menus/{menuID}/routes [delete] -func (c *MenuRouteController) DeleteMenuRoutes(ctx *gin.Context) { - menuIDStr := ctx.Param("menuID") - menuID, err := strconv.ParseUint(menuIDStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "菜单ID格式错误", err.Error()) - return - } - - if err := c.menuRouteService.DeleteMenuRoutes(uint(menuID)); err != nil { - response.Error(ctx, http.StatusInternalServerError, "删除菜单路由关联失败", err.Error()) - return - } - - response.Success(ctx, "删除菜单路由关联成功", nil) -} - -// SyncMenuRoutes 同步菜单路由关联 -// @Summary 同步菜单路由关联 -// @Description 同步菜单的路由关联(删除现有关联并创建新关联) -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param menuID path int true "菜单ID" -// @Param routeMappingIDs body []uint true "路由映射ID列表" -// @Success 200 {object} response.Response -// @Failure 400 {object} response.Response -// @Router /api/menus/{menuID}/routes/sync [post] -func (c *MenuRouteController) SyncMenuRoutes(ctx *gin.Context) { - menuIDStr := ctx.Param("menuID") - menuID, err := strconv.ParseUint(menuIDStr, 10, 32) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "菜单ID格式错误", err.Error()) - return - } - - var routeMappingIDs []uint - if err := ctx.ShouldBindJSON(&routeMappingIDs); err != nil { - response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error()) - return - } - - if err := c.menuRouteService.SyncMenuRoutes(uint(menuID), routeMappingIDs); err != nil { - response.Error(ctx, http.StatusInternalServerError, "同步菜单路由关联失败", err.Error()) - return - } - - response.Success(ctx, "同步菜单路由关联成功", nil) -} - -// ListMenuRoutes 获取菜单路由关联列表 -// @Summary 获取菜单路由关联列表 -// @Description 分页获取菜单路由关联列表 -// @Tags 菜单路由关联 -// @Accept json -// @Produce json -// @Param page query int false "页码" default(1) -// @Param pageSize query int false "每页数量" default(10) -// @Success 200 {object} response.Response{data=map[string]interface{}} -// @Failure 400 {object} response.Response -// @Router /api/menu-routes [get] -func (c *MenuRouteController) ListMenuRoutes(ctx *gin.Context) { - pageStr := ctx.DefaultQuery("page", "1") - pageSizeStr := ctx.DefaultQuery("pageSize", "10") - - page, err := strconv.Atoi(pageStr) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "页码格式错误", err.Error()) - return - } - - pageSize, err := strconv.Atoi(pageSizeStr) - if err != nil { - response.Error(ctx, http.StatusBadRequest, "每页数量格式错误", err.Error()) - return - } - - menuRoutes, total, err := c.menuRouteService.ListMenuRoutes(page, pageSize) - if err != nil { - response.Error(ctx, http.StatusInternalServerError, "获取菜单路由关联列表失败", err.Error()) - return - } - - result := map[string]interface{}{ - "list": menuRoutes, - "total": total, - "page": page, - "pageSize": pageSize, - "pageCount": (total + int64(pageSize) - 1) / int64(pageSize), - } - - response.Success(ctx, "获取菜单路由关联列表成功", result) -} diff --git a/gofaster/backend/internal/auth/migration/create_route_tables.go b/gofaster/backend/internal/auth/migration/create_route_tables.go index 28847cb..afc5fea 100644 --- a/gofaster/backend/internal/auth/migration/create_route_tables.go +++ b/gofaster/backend/internal/auth/migration/create_route_tables.go @@ -2,7 +2,6 @@ package migration import ( "gofaster/internal/auth/model" - "regexp" "go.uber.org/zap" "gorm.io/gorm" @@ -12,37 +11,12 @@ import ( func CreateRouteTables(db *gorm.DB, log *zap.Logger) error { log.Info("开始创建路由相关表...") - // 创建菜单表 - if err := db.AutoMigrate(&model.Menu{}); err != nil { - log.Error("创建菜单表失败", zap.Error(err)) + // 创建菜单分组表 + if err := db.AutoMigrate(&model.MenuGroup{}); err != nil { + log.Error("创建菜单分组表失败", zap.Error(err)) return err } - log.Info("✅ 菜单表创建完成") - - // 创建路由映射表 - log.Info("🔧 开始创建route_mappings表...") - log.Info("📋 RouteMapping模型字段:", - zap.String("ID", "uint"), - zap.String("UpdatedAt", "time.Time"), - zap.String("BackendRoute", "string"), - zap.String("HTTPMethod", "string"), - zap.String("ResourceID", "*uint"), - zap.String("Module", "string"), - zap.String("Description", "string"), - zap.String("Status", "int")) - - if err := db.AutoMigrate(&model.RouteMapping{}); err != nil { - log.Error("❌ 创建路由映射表失败", zap.Error(err)) - return err - } - log.Info("✅ 路由映射表创建完成") - - // 创建菜单路由关联表 - if err := db.AutoMigrate(&model.MenuRoute{}); err != nil { - log.Error("创建菜单路由关联表失败", zap.Error(err)) - return err - } - log.Info("✅ 菜单路由关联表创建完成") + log.Info("✅ 菜单分组表创建完成") // 创建前台路由表 if err := db.AutoMigrate(&model.FrontendRoute{}); err != nil { @@ -65,150 +39,10 @@ func CreateRouteTables(db *gorm.DB, log *zap.Logger) error { } log.Info("✅ Resource表菜单字段添加完成") - // 处理RouteMapping表的字段变更 - if err := updateRouteMappingFields(db, log); err != nil { - log.Error("更新RouteMapping表字段失败", zap.Error(err)) - return err - } - log.Info("✅ RouteMapping表字段更新完成") - log.Info("路由相关表创建完成") return nil } -// updateRouteMappingFields 更新RouteMapping表的字段 -func updateRouteMappingFields(db *gorm.DB, log *zap.Logger) error { - log.Info("🔧 开始更新route_mappings表字段...") - - // 检查表是否存在 - var tableExists bool - err := db.Raw("SELECT COUNT(*) > 0 FROM information_schema.tables WHERE table_name = 'route_mappings'").Scan(&tableExists).Error - if err != nil { - log.Error("❌ 检查表是否存在失败", zap.Error(err)) - return err - } - - log.Info("📊 表存在状态检查", zap.Bool("tableExists", tableExists)) - - if !tableExists { - log.Info("⚠️ route_mappings表不存在,跳过字段更新") - return nil - } - - // 获取当前表结构 - log.Info("📋 获取当前表结构...") - var columns []struct { - ColumnName string `gorm:"column:COLUMN_NAME"` - DataType string `gorm:"column:DATA_TYPE"` - } - if err := db.Raw("SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.columns WHERE table_name = 'route_mappings' ORDER BY ORDINAL_POSITION").Scan(&columns).Error; err != nil { - log.Error("❌ 获取表结构失败", zap.Error(err)) - } else { - log.Info("📋 当前表结构:") - for _, col := range columns { - log.Info(" - " + col.ColumnName + " (" + col.DataType + ")") - } - } - - // 删除不需要的字段 - fieldsToRemove := []string{"created_at", "frontend_route", "auth_group", "menu_id"} - log.Info("🗑️ 准备删除字段", zap.Strings("fields", fieldsToRemove)) - - for _, field := range fieldsToRemove { - var hasField bool - err := db.Raw("SELECT COUNT(*) > 0 FROM information_schema.columns WHERE table_name = 'route_mappings' AND column_name = ?", field).Scan(&hasField).Error - if err != nil { - log.Warn("⚠️ 检查字段失败", zap.String("field", field), zap.Error(err)) - continue - } - - log.Info("🔍 字段检查结果", zap.String("field", field), zap.Bool("exists", hasField)) - - if hasField { - // 删除字段 - log.Info("🗑️ 执行DDL: ALTER TABLE route_mappings DROP COLUMN " + field) - if err := db.Exec("ALTER TABLE route_mappings DROP COLUMN " + field).Error; err != nil { - log.Warn("❌ 删除字段失败", zap.String("field", field), zap.Error(err)) - } else { - log.Info("✅ 删除字段成功", zap.String("field", field)) - } - } else { - log.Info("ℹ️ 字段不存在,跳过删除", zap.String("field", field)) - } - } - - // 更新现有记录的路径格式(将:var改为{var}) - log.Info("🔄 开始更新路径格式...") - if err := updatePathFormat(db, log); err != nil { - log.Error("❌ 更新路径格式失败", zap.Error(err)) - return err - } - - log.Info("✅ route_mappings表字段更新完成") - return nil -} - -// updatePathFormat 更新路径格式:将:var改为{var} -func updatePathFormat(db *gorm.DB, log *zap.Logger) error { - log.Info("🔍 查找需要更新路径格式的记录...") - - // 获取所有需要更新的记录 - var mappings []struct { - ID uint `gorm:"column:id"` - BackendRoute string `gorm:"column:backend_route"` - } - - query := "SELECT id, backend_route FROM route_mappings WHERE backend_route LIKE '%:%'" - log.Info("📝 执行查询", zap.String("query", query)) - - if err := db.Raw(query).Scan(&mappings).Error; err != nil { - log.Error("❌ 查询需要更新的记录失败", zap.Error(err)) - return err - } - - log.Info("📊 找到需要更新路径格式的记录", zap.Int("count", len(mappings))) - - if len(mappings) == 0 { - log.Info("ℹ️ 没有需要更新路径格式的记录") - return nil - } - - // 更新每条记录 - updatedCount := 0 - for _, mapping := range mappings { - // 转换路径格式 - newPath := convertPathFormat(mapping.BackendRoute) - log.Info("🔄 路径转换", - zap.Uint("id", mapping.ID), - zap.String("old", mapping.BackendRoute), - zap.String("new", newPath)) - - if newPath != mapping.BackendRoute { - updateQuery := "UPDATE route_mappings SET backend_route = ? WHERE id = ?" - log.Info("📝 执行更新", zap.String("query", updateQuery), zap.String("newPath", newPath), zap.Uint("id", mapping.ID)) - - if err := db.Exec(updateQuery, newPath, mapping.ID).Error; err != nil { - log.Error("❌ 更新路径失败", zap.Uint("id", mapping.ID), zap.Error(err)) - } else { - log.Info("✅ 更新路径成功", zap.Uint("id", mapping.ID), zap.String("old", mapping.BackendRoute), zap.String("new", newPath)) - updatedCount++ - } - } else { - log.Info("ℹ️ 路径无需更新", zap.Uint("id", mapping.ID)) - } - } - - log.Info("📊 路径格式更新完成", zap.Int("total", len(mappings)), zap.Int("updated", updatedCount)) - return nil -} - -// convertPathFormat 转换路径格式:将:var格式改为{var}格式 -func convertPathFormat(path string) string { - // 使用正则表达式替换:var格式为{var}格式 - re := regexp.MustCompile(`:([a-zA-Z0-9_]+)`) - return re.ReplaceAllString(path, "{$1}") -} - // addMenuFieldsToResource 为Resource表添加菜单相关字段 func addMenuFieldsToResource(db *gorm.DB, log *zap.Logger) error { // 检查IsMenu字段是否存在 diff --git a/gofaster/backend/internal/auth/model/menu.go b/gofaster/backend/internal/auth/model/menu.go deleted file mode 100644 index f20834d..0000000 --- a/gofaster/backend/internal/auth/model/menu.go +++ /dev/null @@ -1,27 +0,0 @@ -package model - -import ( - "gofaster/internal/shared/model" -) - -// Menu 菜单模型 -type Menu struct { - model.BaseModel - Name string `json:"name"` // 菜单名称 - Path string `json:"path"` // 前台路由路径 - Component string `json:"component"` // Vue组件路径 - Icon string `json:"icon"` // 图标 - Sort int `json:"sort"` // 排序 - ParentID *uint `json:"parent_id"` // 父菜单ID - Level int `json:"level"` // 菜单层级 - IsVisible bool `json:"is_visible"` // 是否可见 - IsExternal bool `json:"is_external"` // 是否外部链接 - Meta string `json:"meta"` // 元数据(JSON) - Module string `json:"module"` // 所属模块 - Status int `gorm:"default:1" json:"status"` // 状态:1-启用,0-禁用 -} - -// TableName 指定表名 -func (Menu) TableName() string { - return "menus" -} diff --git a/gofaster/backend/internal/auth/model/menu_group.go b/gofaster/backend/internal/auth/model/menu_group.go new file mode 100644 index 0000000..135fa85 --- /dev/null +++ b/gofaster/backend/internal/auth/model/menu_group.go @@ -0,0 +1,20 @@ +package model + +import ( + "time" +) + +// MenuGroup 菜单分组模型 +type MenuGroup struct { + ID uint `gorm:"primarykey" json:"id"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` // 分组名称 + Description string `json:"description"` // 分组描述 + Sort int `gorm:"default:0" json:"sort"` // 排序 + Status int `gorm:"default:1" json:"status"` // 状态:1-启用,0-禁用 +} + +// TableName 指定表名 +func (MenuGroup) TableName() string { + return "menu_groups" +} diff --git a/gofaster/backend/internal/auth/model/menu_route.go b/gofaster/backend/internal/auth/model/menu_route.go deleted file mode 100644 index cac57b6..0000000 --- a/gofaster/backend/internal/auth/model/menu_route.go +++ /dev/null @@ -1,20 +0,0 @@ -package model - -import ( - "gofaster/internal/shared/model" -) - -// MenuRoute 菜单路由关联表(多对多关系) -type MenuRoute struct { - model.BaseModel - MenuID uint `gorm:"primaryKey;index" json:"menu_id"` // 菜单ID - RouteMappingID uint `gorm:"primaryKey;index" json:"route_mapping_id"` // 路由映射ID - Sort int `gorm:"default:0" json:"sort"` // 排序 - IsDefault bool `gorm:"default:false" json:"is_default"` // 是否为默认路由 - Status int `gorm:"default:1" json:"status"` // 状态:1-启用,0-禁用 -} - -// TableName 指定表名 -func (MenuRoute) TableName() string { - return "menu_routes" -} diff --git a/gofaster/backend/internal/auth/model/route_mapping.go b/gofaster/backend/internal/auth/model/route_mapping.go deleted file mode 100644 index 5a5b573..0000000 --- a/gofaster/backend/internal/auth/model/route_mapping.go +++ /dev/null @@ -1,22 +0,0 @@ -package model - -import ( - "time" -) - -// RouteMapping 路由映射模型 -type RouteMapping struct { - ID uint `gorm:"primarykey" json:"id"` - UpdatedAt time.Time `json:"updated_at"` - BackendRoute string `json:"backend_route"` // 后台API路径 - HTTPMethod string `json:"http_method"` // HTTP方法 - ResourceID *uint `json:"resource_id"` // 关联的资源ID - Module string `json:"module"` // 所属模块 - Description string `json:"description"` // 描述 - Status int `gorm:"default:1" json:"status"` // 状态:1-启用,0-禁用 -} - -// TableName 指定表名 -func (RouteMapping) TableName() string { - return "route_mappings" -} diff --git a/gofaster/backend/internal/auth/module.go b/gofaster/backend/internal/auth/module.go index 3909958..4e9ac83 100644 --- a/gofaster/backend/internal/auth/module.go +++ b/gofaster/backend/internal/auth/module.go @@ -21,8 +21,8 @@ type Module struct { userController *controller.UserController authController *controller.AuthController passwordController *controller.PasswordController - menuRouteController *controller.MenuRouteController frontendRouteController *controller.FrontendRouteController + menuGroupController *controller.MenuGroupController routeSyncService *service.RouteSyncService db *gorm.DB logger *zap.Logger @@ -48,10 +48,8 @@ func (m *Module) Init(cfg *config.Config, logger *zap.Logger, db *gorm.DB, redis passwordPolicyRepo := repository.NewPasswordPolicyRepository(db) passwordHistoryRepo := repository.NewPasswordHistoryRepository(db) passwordResetRepo := repository.NewPasswordResetRepository(db) - routeMappingRepo := repository.NewRouteMappingRepository(db) resourceRepo := repository.NewResourceRepository(db) - menuRepo := repository.NewMenuRepository(db) - menuRouteRepo := repository.NewMenuRouteRepository(db) + menuGroupRepo := repository.NewMenuGroupRepository(db) frontendRouteRepo := repository.NewFrontendRouteRepository(db) frontendBackendRouteRepo := repository.NewFrontendBackendRouteRepository(db) @@ -65,15 +63,15 @@ func (m *Module) Init(cfg *config.Config, logger *zap.Logger, db *gorm.DB, redis passwordHistoryRepo, passwordResetRepo, ) - menuRouteService := service.NewMenuRouteService(menuRouteRepo, menuRepo, routeMappingRepo, logger) + menuGroupService := service.NewMenuGroupService(menuGroupRepo, logger) frontendRouteService := service.NewFrontendRouteService(frontendRouteRepo, frontendBackendRouteRepo, logger) - m.routeSyncService = service.NewRouteSyncService(routeMappingRepo, resourceRepo, logger) + m.routeSyncService = service.NewRouteSyncService(resourceRepo, frontendBackendRouteRepo, logger) // 初始化控制器 m.userController = controller.NewUserController(userService) m.authController = controller.NewAuthController(authService) m.passwordController = controller.NewPasswordController(passwordService, userService) - m.menuRouteController = controller.NewMenuRouteController(menuRouteService, logger) + m.menuGroupController = controller.NewMenuGroupController(menuGroupService, logger) m.frontendRouteController = controller.NewFrontendRouteController(frontendRouteService, logger) log.Printf("✅ 认证模块初始化完成") @@ -103,14 +101,14 @@ func (m *Module) RegisterRoutes(router *gin.RouterGroup) { jwtSecret := "your-jwt-secret" // 应该从配置中获取 routes.RegisterAuthRoutes(router, m.db, jwtSecret) - // 注册菜单路由关联表路由 - routes.RegisterMenuRouteRoutes(router, m.menuRouteController) - // 注册路由同步路由 routes.RegisterRouteSyncRoutes(router, m.db, m.logger) // 注册前台路由路由 routes.RegisterFrontendRouteRoutes(router, m.db, m.logger) + + // 注册菜单分组路由 + routes.RegisterMenuGroupRoutes(router, m.db, m.logger) } // SyncRoutes 同步路由信息 diff --git a/gofaster/backend/internal/auth/repository/menu_group_repo.go b/gofaster/backend/internal/auth/repository/menu_group_repo.go new file mode 100644 index 0000000..bf5ef8b --- /dev/null +++ b/gofaster/backend/internal/auth/repository/menu_group_repo.go @@ -0,0 +1,127 @@ +package repository + +import ( + "gofaster/internal/auth/model" + + "gorm.io/gorm" +) + +// MenuGroupRepository 菜单分组仓库 +type MenuGroupRepository struct { + db *gorm.DB +} + +// NewMenuGroupRepository 创建菜单分组仓库实例 +func NewMenuGroupRepository(db *gorm.DB) *MenuGroupRepository { + return &MenuGroupRepository{db: db} +} + +// Create 创建菜单分组 +func (r *MenuGroupRepository) Create(menuGroup *model.MenuGroup) error { + return r.db.Create(menuGroup).Error +} + +// FindByID 根据ID查找菜单分组 +func (r *MenuGroupRepository) FindByID(id uint) (*model.MenuGroup, error) { + var menuGroup model.MenuGroup + err := r.db.Where("id = ?", id).First(&menuGroup).Error + if err != nil { + return nil, err + } + return &menuGroup, nil +} + +// FindByName 根据名称查找菜单分组 +func (r *MenuGroupRepository) FindByName(name string) (*model.MenuGroup, error) { + var menuGroup model.MenuGroup + err := r.db.Where("name = ?", name).First(&menuGroup).Error + if err != nil { + return nil, err + } + return &menuGroup, nil +} + +// List 获取菜单分组列表 +func (r *MenuGroupRepository) List() ([]*model.MenuGroup, error) { + var menuGroups []*model.MenuGroup + err := r.db.Order("sort ASC, id ASC").Find(&menuGroups).Error + return menuGroups, err +} + +// ListByStatus 根据状态获取菜单分组列表 +func (r *MenuGroupRepository) ListByStatus(status int) ([]*model.MenuGroup, error) { + var menuGroups []*model.MenuGroup + err := r.db.Where("status = ?", status).Order("sort ASC, id ASC").Find(&menuGroups).Error + return menuGroups, err +} + +// Update 更新菜单分组 +func (r *MenuGroupRepository) Update(menuGroup *model.MenuGroup) error { + return r.db.Save(menuGroup).Error +} + +// Delete 删除菜单分组 +func (r *MenuGroupRepository) Delete(id uint) error { + return r.db.Delete(&model.MenuGroup{}, id).Error +} + +// BatchDelete 批量删除菜单分组 +func (r *MenuGroupRepository) BatchDelete(ids []uint) error { + return r.db.Where("id IN ?", ids).Delete(&model.MenuGroup{}).Error +} + +// UpdateStatus 更新菜单分组状态 +func (r *MenuGroupRepository) UpdateStatus(id uint, status int) error { + return r.db.Model(&model.MenuGroup{}).Where("id = ?", id).Update("status", status).Error +} + +// UpdateSort 更新菜单分组排序 +func (r *MenuGroupRepository) UpdateSort(id uint, sort int) error { + return r.db.Model(&model.MenuGroup{}).Where("id = ?", id).Update("sort", sort).Error +} + +// BatchUpdateSort 批量更新菜单分组排序 +func (r *MenuGroupRepository) BatchUpdateSort(sorts map[uint]int) error { + for id, sort := range sorts { + if err := r.UpdateSort(id, sort); err != nil { + return err + } + } + return nil +} + +// GetStats 获取菜单分组统计信息 +func (r *MenuGroupRepository) GetStats() (map[string]interface{}, error) { + var total int64 + var statusStats []struct { + Status int `json:"status"` + Count int64 `json:"count"` + } + + if err := r.db.Model(&model.MenuGroup{}).Count(&total).Error; err != nil { + return nil, err + } + + if err := r.db.Model(&model.MenuGroup{}). + Select("status, count(*) as count"). + Group("status"). + Scan(&statusStats).Error; err != nil { + return nil, err + } + + return map[string]interface{}{ + "total": total, + "status_stats": statusStats, + }, nil +} + +// ExistsByName 检查名称是否存在(排除指定ID) +func (r *MenuGroupRepository) ExistsByName(name string, excludeID ...uint) bool { + query := r.db.Model(&model.MenuGroup{}).Where("name = ?", name) + if len(excludeID) > 0 && excludeID[0] > 0 { + query = query.Where("id != ?", excludeID[0]) + } + var count int64 + query.Count(&count) + return count > 0 +} diff --git a/gofaster/backend/internal/auth/repository/menu_repo.go b/gofaster/backend/internal/auth/repository/menu_repo.go deleted file mode 100644 index 651b799..0000000 --- a/gofaster/backend/internal/auth/repository/menu_repo.go +++ /dev/null @@ -1,89 +0,0 @@ -package repository - -import ( - "gofaster/internal/auth/model" - "gofaster/internal/shared/repository" - - "gorm.io/gorm" -) - -// MenuRepository 菜单仓库 -type MenuRepository struct { - repository.BaseRepo -} - -// NewMenuRepository 创建菜单仓库实例 -func NewMenuRepository(db *gorm.DB) *MenuRepository { - return &MenuRepository{ - BaseRepo: *repository.NewBaseRepo(db), - } -} - -// Create 创建菜单 -func (r *MenuRepository) Create(menu *model.Menu) error { - return r.DB().Create(menu).Error -} - -// Update 更新菜单 -func (r *MenuRepository) Update(menu *model.Menu) error { - return r.DB().Save(menu).Error -} - -// Delete 删除菜单 -func (r *MenuRepository) Delete(id uint) error { - return r.DB().Delete(&model.Menu{}, id).Error -} - -// FindByID 根据ID查找菜单 -func (r *MenuRepository) FindByID(id uint) (*model.Menu, error) { - var menu model.Menu - err := r.DB().First(&menu, id).Error - if err != nil { - return nil, err - } - return &menu, nil -} - -// FindByPath 根据路径查找菜单 -func (r *MenuRepository) FindByPath(path string) (*model.Menu, error) { - var menu model.Menu - err := r.DB().Where("path = ?", path).First(&menu).Error - if err != nil { - return nil, err - } - return &menu, nil -} - -// FindAll 查找所有菜单 -func (r *MenuRepository) FindAll() ([]model.Menu, error) { - var menus []model.Menu - err := r.DB().Order("sort ASC, id ASC").Find(&menus).Error - return menus, err -} - -// FindByModule 根据模块查找菜单 -func (r *MenuRepository) FindByModule(module string) ([]model.Menu, error) { - var menus []model.Menu - err := r.DB().Where("module = ?", module).Order("sort ASC, id ASC").Find(&menus).Error - return menus, err -} - -// FindByParentID 根据父ID查找子菜单 -func (r *MenuRepository) FindByParentID(parentID *uint) ([]model.Menu, error) { - var menus []model.Menu - query := r.DB() - if parentID == nil { - query = query.Where("parent_id IS NULL") - } else { - query = query.Where("parent_id = ?", *parentID) - } - err := query.Order("sort ASC, id ASC").Find(&menus).Error - return menus, err -} - -// FindTree 查找菜单树 -func (r *MenuRepository) FindTree() ([]model.Menu, error) { - var menus []model.Menu - err := r.DB().Order("sort ASC, id ASC").Find(&menus).Error - return menus, err -} diff --git a/gofaster/backend/internal/auth/repository/menu_route_repo.go b/gofaster/backend/internal/auth/repository/menu_route_repo.go deleted file mode 100644 index aea898c..0000000 --- a/gofaster/backend/internal/auth/repository/menu_route_repo.go +++ /dev/null @@ -1,142 +0,0 @@ -package repository - -import ( - "gofaster/internal/auth/model" - - "gorm.io/gorm" -) - -// MenuRouteRepository 菜单路由关联表仓储 -type MenuRouteRepository struct { - db *gorm.DB -} - -// NewMenuRouteRepository 创建菜单路由关联表仓储 -func NewMenuRouteRepository(db *gorm.DB) *MenuRouteRepository { - return &MenuRouteRepository{db: db} -} - -// Create 创建菜单路由关联 -func (r *MenuRouteRepository) Create(menuRoute *model.MenuRoute) error { - return r.db.Create(menuRoute).Error -} - -// CreateBatch 批量创建菜单路由关联 -func (r *MenuRouteRepository) CreateBatch(menuRoutes []*model.MenuRoute) error { - return r.db.CreateInBatches(menuRoutes, 100).Error -} - -// GetByID 根据ID获取菜单路由关联 -func (r *MenuRouteRepository) GetByID(id uint) (*model.MenuRoute, error) { - var menuRoute model.MenuRoute - err := r.db.Where("id = ?", id).First(&menuRoute).Error - if err != nil { - return nil, err - } - return &menuRoute, nil -} - -// GetByMenuAndRoute 根据菜单ID和路由ID获取关联 -func (r *MenuRouteRepository) GetByMenuAndRoute(menuID, routeMappingID uint) (*model.MenuRoute, error) { - var menuRoute model.MenuRoute - err := r.db.Where("menu_id = ? AND route_mapping_id = ?", menuID, routeMappingID).First(&menuRoute).Error - if err != nil { - return nil, err - } - return &menuRoute, nil -} - -// GetByMenuID 根据菜单ID获取所有关联的路由 -func (r *MenuRouteRepository) GetByMenuID(menuID uint) ([]*model.MenuRoute, error) { - var menuRoutes []*model.MenuRoute - err := r.db.Where("menu_id = ?", menuID).Order("sort ASC").Find(&menuRoutes).Error - return menuRoutes, err -} - -// GetByRouteMappingID 根据路由映射ID获取所有关联的菜单 -func (r *MenuRouteRepository) GetByRouteMappingID(routeMappingID uint) ([]*model.MenuRoute, error) { - var menuRoutes []*model.MenuRoute - err := r.db.Where("route_mapping_id = ?", routeMappingID).Order("sort ASC").Find(&menuRoutes).Error - return menuRoutes, err -} - -// List 获取菜单路由关联列表 -func (r *MenuRouteRepository) List(page, pageSize int) ([]*model.MenuRoute, int64, error) { - var menuRoutes []*model.MenuRoute - var total int64 - - // 获取总数 - if err := r.db.Model(&model.MenuRoute{}).Count(&total).Error; err != nil { - return nil, 0, err - } - - // 获取分页数据 - offset := (page - 1) * pageSize - err := r.db.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&menuRoutes).Error - return menuRoutes, total, err -} - -// Update 更新菜单路由关联 -func (r *MenuRouteRepository) Update(menuRoute *model.MenuRoute) error { - return r.db.Save(menuRoute).Error -} - -// Delete 删除菜单路由关联 -func (r *MenuRouteRepository) Delete(id uint) error { - return r.db.Delete(&model.MenuRoute{}, id).Error -} - -// DeleteByMenuID 根据菜单ID删除所有关联 -func (r *MenuRouteRepository) DeleteByMenuID(menuID uint) error { - return r.db.Where("menu_id = ?", menuID).Delete(&model.MenuRoute{}).Error -} - -// DeleteByRouteMappingID 根据路由映射ID删除所有关联 -func (r *MenuRouteRepository) DeleteByRouteMappingID(routeMappingID uint) error { - return r.db.Where("route_mapping_id = ?", routeMappingID).Delete(&model.MenuRoute{}).Error -} - -// DeleteByMenuAndRoute 根据菜单ID和路由ID删除关联 -func (r *MenuRouteRepository) DeleteByMenuAndRoute(menuID, routeMappingID uint) error { - return r.db.Where("menu_id = ? AND route_mapping_id = ?", menuID, routeMappingID).Delete(&model.MenuRoute{}).Error -} - -// GetMenuWithRoutes 获取菜单及其关联的路由信息 -func (r *MenuRouteRepository) GetMenuWithRoutes(menuID uint) (*model.Menu, []*model.RouteMapping, error) { - var menu model.Menu - var routeMappings []*model.RouteMapping - - // 获取菜单信息 - if err := r.db.First(&menu, menuID).Error; err != nil { - return nil, nil, err - } - - // 获取关联的路由映射 - err := r.db.Table("route_mappings"). - Joins("JOIN menu_routes ON route_mappings.id = menu_routes.route_mapping_id"). - Where("menu_routes.menu_id = ?", menuID). - Order("menu_routes.sort ASC"). - Find(&routeMappings).Error - - return &menu, routeMappings, err -} - -// GetRouteWithMenus 获取路由及其关联的菜单信息 -func (r *MenuRouteRepository) GetRouteWithMenus(routeMappingID uint) (*model.RouteMapping, []*model.Menu, error) { - var routeMapping model.RouteMapping - var menus []*model.Menu - - // 获取路由映射信息 - if err := r.db.First(&routeMapping, routeMappingID).Error; err != nil { - return nil, nil, err - } - - // 获取关联的菜单 - err := r.db.Table("menus"). - Joins("JOIN menu_routes ON menus.id = menu_routes.menu_id"). - Where("menu_routes.route_mapping_id = ?", routeMappingID). - Order("menu_routes.sort ASC"). - Find(&menus).Error - - return &routeMapping, menus, err -} diff --git a/gofaster/backend/internal/auth/repository/route_mapping_repo.go b/gofaster/backend/internal/auth/repository/route_mapping_repo.go deleted file mode 100644 index bfdd6f7..0000000 --- a/gofaster/backend/internal/auth/repository/route_mapping_repo.go +++ /dev/null @@ -1,108 +0,0 @@ -package repository - -import ( - "gofaster/internal/auth/model" - "gofaster/internal/shared/repository" - - "gorm.io/gorm" -) - -// RouteMappingRepository 路由映射仓库 -type RouteMappingRepository struct { - repository.BaseRepo -} - -// NewRouteMappingRepository 创建路由映射仓库实例 -func NewRouteMappingRepository(db *gorm.DB) *RouteMappingRepository { - return &RouteMappingRepository{ - BaseRepo: *repository.NewBaseRepo(db), - } -} - -// Create 创建路由映射 -func (r *RouteMappingRepository) Create(mapping *model.RouteMapping) error { - return r.DB().Create(mapping).Error -} - -// Update 更新路由映射 -func (r *RouteMappingRepository) Update(mapping *model.RouteMapping) error { - return r.DB().Save(mapping).Error -} - -// Delete 删除路由映射 -func (r *RouteMappingRepository) Delete(id uint) error { - return r.DB().Delete(&model.RouteMapping{}, id).Error -} - -// FindByID 根据ID查找路由映射 -func (r *RouteMappingRepository) FindByID(id uint) (*model.RouteMapping, error) { - var mapping model.RouteMapping - err := r.DB().First(&mapping, id).Error - if err != nil { - return nil, err - } - return &mapping, nil -} - -// FindByBackendRoute 根据后台路由查找映射 -func (r *RouteMappingRepository) FindByBackendRoute(backendRoute, httpMethod string) (*model.RouteMapping, error) { - var mapping model.RouteMapping - err := r.DB().Where("backend_route = ? AND http_method = ?", backendRoute, httpMethod).First(&mapping).Error - if err != nil { - return nil, err - } - return &mapping, nil -} - -// FindByFrontendRoute 根据前台路由查找映射 -func (r *RouteMappingRepository) FindByFrontendRoute(frontendRoute string) ([]model.RouteMapping, error) { - var mappings []model.RouteMapping - err := r.DB().Where("frontend_route = ?", frontendRoute).Find(&mappings).Error - return mappings, err -} - -// FindAll 查找所有路由映射 -func (r *RouteMappingRepository) FindAll() ([]model.RouteMapping, error) { - var mappings []model.RouteMapping - err := r.DB().Order("id ASC").Find(&mappings).Error - return mappings, err -} - -// FindByModule 根据模块查找路由映射 -func (r *RouteMappingRepository) FindByModule(module string) ([]model.RouteMapping, error) { - var mappings []model.RouteMapping - err := r.DB().Where("module = ?", module).Order("id ASC").Find(&mappings).Error - return mappings, err -} - -// FindByResourceID 根据资源ID查找路由映射 -func (r *RouteMappingRepository) FindByResourceID(resourceID uint) ([]model.RouteMapping, error) { - var mappings []model.RouteMapping - err := r.DB().Where("resource_id = ?", resourceID).Find(&mappings).Error - return mappings, err -} - -// FindByMenuID 根据菜单ID查找路由映射 -func (r *RouteMappingRepository) FindByMenuID(menuID uint) ([]model.RouteMapping, error) { - var mappings []model.RouteMapping - err := r.DB().Where("menu_id = ?", menuID).Find(&mappings).Error - return mappings, err -} - -// CreateOrUpdate 创建或更新路由映射(用于增量同步) -func (r *RouteMappingRepository) CreateOrUpdate(mapping *model.RouteMapping) error { - var existing model.RouteMapping - err := r.DB().Where("backend_route = ? AND http_method = ?", mapping.BackendRoute, mapping.HTTPMethod).First(&existing).Error - - if err != nil { - if err == gorm.ErrRecordNotFound { - // 不存在则创建 - return r.Create(mapping) - } - return err - } - - // 存在则更新 - mapping.ID = existing.ID - return r.Update(mapping) -} diff --git a/gofaster/backend/internal/auth/routes/menu_group_routes.go b/gofaster/backend/internal/auth/routes/menu_group_routes.go new file mode 100644 index 0000000..09265fa --- /dev/null +++ b/gofaster/backend/internal/auth/routes/menu_group_routes.go @@ -0,0 +1,45 @@ +package routes + +import ( + "gofaster/internal/auth/controller" + "gofaster/internal/auth/repository" + "gofaster/internal/auth/service" + "gofaster/internal/shared/middleware" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" + "gorm.io/gorm" +) + +func RegisterMenuGroupRoutes(router *gin.RouterGroup, db *gorm.DB, logger *zap.Logger) { + // 初始化依赖 + menuGroupRepo := repository.NewMenuGroupRepository(db) + menuGroupService := service.NewMenuGroupService(menuGroupRepo, logger) + menuGroupController := controller.NewMenuGroupController(menuGroupService, logger) + + // 菜单分组管理路由组 + menuGroupGroup := router.Group("/menu-groups") + { + // 需要权限验证的路由 + menuGroupGroup.Use(middleware.JWTAuth()) // 暂时只检查JWT,不检查权限 + { + // 菜单分组CRUD操作 + menuGroupGroup.GET("", menuGroupController.ListMenuGroups) // 获取菜单分组列表 + menuGroupGroup.POST("", menuGroupController.CreateMenuGroup) // 创建菜单分组 + menuGroupGroup.GET("/:id", menuGroupController.GetMenuGroup) // 获取菜单分组详情 + menuGroupGroup.PUT("/:id", menuGroupController.UpdateMenuGroup) // 更新菜单分组 + menuGroupGroup.DELETE("/:id", menuGroupController.DeleteMenuGroup) // 删除菜单分组 + + // 批量操作 + menuGroupGroup.POST("/batch-delete", menuGroupController.BatchDeleteMenuGroups) // 批量删除菜单分组 + menuGroupGroup.PUT("/batch-sort", menuGroupController.BatchUpdateMenuGroupSort) // 批量更新排序 + + // 状态和排序管理 + menuGroupGroup.PUT("/:id/status", menuGroupController.UpdateMenuGroupStatus) // 更新状态 + menuGroupGroup.PUT("/:id/sort", menuGroupController.UpdateMenuGroupSort) // 更新排序 + + // 统计信息 + menuGroupGroup.GET("/stats", menuGroupController.GetMenuGroupStats) // 获取统计信息 + } + } +} diff --git a/gofaster/backend/internal/auth/routes/menu_route_routes.go b/gofaster/backend/internal/auth/routes/menu_route_routes.go deleted file mode 100644 index 3ceae9b..0000000 --- a/gofaster/backend/internal/auth/routes/menu_route_routes.go +++ /dev/null @@ -1,52 +0,0 @@ -package routes - -import ( - "gofaster/internal/auth/controller" - "gofaster/internal/shared/middleware" - - "github.com/gin-gonic/gin" -) - -// RegisterMenuRouteRoutes 注册菜单路由关联表路由 -func RegisterMenuRouteRoutes(r *gin.RouterGroup, menuRouteController *controller.MenuRouteController) { - // 菜单路由关联表路由组 - menuRouteGroup := r.Group("/menu-routes") - { - // 需要认证的接口 - menuRouteGroup.Use(middleware.JWTAuth()) - { - // 基础CRUD操作 - menuRouteGroup.POST("", menuRouteController.CreateMenuRoute) // 创建菜单路由关联 - menuRouteGroup.GET("", menuRouteController.ListMenuRoutes) // 获取菜单路由关联列表 - menuRouteGroup.PUT("/:id", menuRouteController.UpdateMenuRoute) // 更新菜单路由关联 - menuRouteGroup.DELETE("/:id", menuRouteController.DeleteMenuRoute) // 删除菜单路由关联 - } - } - - // 菜单相关路由 - menuGroup := r.Group("/menus") - { - // 需要认证的接口 - menuGroup.Use(middleware.JWTAuth()) - { - // 菜单路由关联操作 - menuGroup.POST("/:menuID/routes", menuRouteController.CreateMenuRoutes) // 批量创建菜单路由关联 - menuGroup.GET("/:menuID/routes", menuRouteController.GetMenuRoutes) // 获取菜单的路由关联 - menuGroup.GET("/:menuID/routes/detail", menuRouteController.GetMenuWithRoutes) // 获取菜单及其关联的路由信息 - menuGroup.DELETE("/:menuID/routes", menuRouteController.DeleteMenuRoutes) // 删除菜单的所有路由关联 - menuGroup.POST("/:menuID/routes/sync", menuRouteController.SyncMenuRoutes) // 同步菜单路由关联 - } - } - - // 路由映射相关路由 - routeMappingGroup := r.Group("/route-mappings") - { - // 需要认证的接口 - routeMappingGroup.Use(middleware.JWTAuth()) - { - // 路由菜单关联操作 - routeMappingGroup.GET("/:routeMappingID/menus", menuRouteController.GetRouteMenus) // 获取路由的菜单关联 - routeMappingGroup.GET("/:routeMappingID/menus/detail", menuRouteController.GetRouteWithMenus) // 获取路由及其关联的菜单信息 - } - } -} diff --git a/gofaster/backend/internal/auth/routes/route_sync_routes.go b/gofaster/backend/internal/auth/routes/route_sync_routes.go index cb1fc32..c306312 100644 --- a/gofaster/backend/internal/auth/routes/route_sync_routes.go +++ b/gofaster/backend/internal/auth/routes/route_sync_routes.go @@ -13,9 +13,9 @@ import ( // RegisterRouteSyncRoutes 注册路由同步相关路由 func RegisterRouteSyncRoutes(router *gin.RouterGroup, db *gorm.DB, logger *zap.Logger) { // 初始化依赖 - routeMappingRepo := repository.NewRouteMappingRepository(db) resourceRepo := repository.NewResourceRepository(db) - routeSyncService := service.NewRouteSyncService(routeMappingRepo, resourceRepo, logger) + frontendBackendRouteRepo := repository.NewFrontendBackendRouteRepository(db) + routeSyncService := service.NewRouteSyncService(resourceRepo, frontendBackendRouteRepo, logger) routeSyncController := controller.NewRouteSyncController(routeSyncService, logger) // 路由同步路由组 diff --git a/gofaster/backend/internal/auth/service/menu_group_service.go b/gofaster/backend/internal/auth/service/menu_group_service.go new file mode 100644 index 0000000..020bb9e --- /dev/null +++ b/gofaster/backend/internal/auth/service/menu_group_service.go @@ -0,0 +1,229 @@ +package service + +import ( + "fmt" + "gofaster/internal/auth/model" + "gofaster/internal/auth/repository" + + "go.uber.org/zap" +) + +// MenuGroupService 菜单分组服务 +type MenuGroupService struct { + menuGroupRepo *repository.MenuGroupRepository + logger *zap.Logger +} + +// NewMenuGroupService 创建菜单分组服务实例 +func NewMenuGroupService(menuGroupRepo *repository.MenuGroupRepository, logger *zap.Logger) *MenuGroupService { + return &MenuGroupService{ + menuGroupRepo: menuGroupRepo, + logger: logger, + } +} + +// CreateMenuGroup 创建菜单分组 +func (s *MenuGroupService) CreateMenuGroup(menuGroup *model.MenuGroup) error { + s.logger.Info("开始创建菜单分组", zap.String("name", menuGroup.Name)) + + // 检查名称是否已存在 + if s.menuGroupRepo.ExistsByName(menuGroup.Name) { + return fmt.Errorf("菜单分组名称已存在: %s", menuGroup.Name) + } + + // 设置默认值 + if menuGroup.Sort == 0 { + menuGroup.Sort = 999 // 默认排序值 + } + if menuGroup.Status == 0 { + menuGroup.Status = 1 // 默认启用 + } + + if err := s.menuGroupRepo.Create(menuGroup); err != nil { + s.logger.Error("创建菜单分组失败", zap.Error(err)) + return fmt.Errorf("创建菜单分组失败: %w", err) + } + + s.logger.Info("菜单分组创建成功", zap.String("name", menuGroup.Name), zap.Uint("id", menuGroup.ID)) + return nil +} + +// GetMenuGroupByID 根据ID获取菜单分组 +func (s *MenuGroupService) GetMenuGroupByID(id uint) (*model.MenuGroup, error) { + menuGroup, err := s.menuGroupRepo.FindByID(id) + if err != nil { + s.logger.Error("获取菜单分组失败", zap.Uint("id", id), zap.Error(err)) + return nil, fmt.Errorf("获取菜单分组失败: %w", err) + } + return menuGroup, nil +} + +// GetMenuGroupByName 根据名称获取菜单分组 +func (s *MenuGroupService) GetMenuGroupByName(name string) (*model.MenuGroup, error) { + menuGroup, err := s.menuGroupRepo.FindByName(name) + if err != nil { + s.logger.Error("获取菜单分组失败", zap.String("name", name), zap.Error(err)) + return nil, fmt.Errorf("获取菜单分组失败: %w", err) + } + return menuGroup, nil +} + +// ListMenuGroups 获取菜单分组列表 +func (s *MenuGroupService) ListMenuGroups() ([]*model.MenuGroup, error) { + menuGroups, err := s.menuGroupRepo.List() + if err != nil { + s.logger.Error("获取菜单分组列表失败", zap.Error(err)) + return nil, fmt.Errorf("获取菜单分组列表失败: %w", err) + } + return menuGroups, nil +} + +// ListMenuGroupsByStatus 根据状态获取菜单分组列表 +func (s *MenuGroupService) ListMenuGroupsByStatus(status int) ([]*model.MenuGroup, error) { + menuGroups, err := s.menuGroupRepo.ListByStatus(status) + if err != nil { + s.logger.Error("根据状态获取菜单分组列表失败", zap.Int("status", status), zap.Error(err)) + return nil, fmt.Errorf("根据状态获取菜单分组列表失败: %w", err) + } + return menuGroups, nil +} + +// UpdateMenuGroup 更新菜单分组 +func (s *MenuGroupService) UpdateMenuGroup(menuGroup *model.MenuGroup) error { + s.logger.Info("开始更新菜单分组", zap.Uint("id", menuGroup.ID), zap.String("name", menuGroup.Name)) + + // 检查菜单分组是否存在 + existing, err := s.menuGroupRepo.FindByID(menuGroup.ID) + if err != nil { + s.logger.Error("菜单分组不存在", zap.Uint("id", menuGroup.ID), zap.Error(err)) + return fmt.Errorf("菜单分组不存在: %w", err) + } + + // 检查名称是否已被其他记录使用 + if s.menuGroupRepo.ExistsByName(menuGroup.Name, menuGroup.ID) { + return fmt.Errorf("菜单分组名称已存在: %s", menuGroup.Name) + } + + // 更新字段 + existing.Name = menuGroup.Name + existing.Description = menuGroup.Description + existing.Sort = menuGroup.Sort + existing.Status = menuGroup.Status + + if err := s.menuGroupRepo.Update(existing); err != nil { + s.logger.Error("更新菜单分组失败", zap.Error(err)) + return fmt.Errorf("更新菜单分组失败: %w", err) + } + + s.logger.Info("菜单分组更新成功", zap.Uint("id", menuGroup.ID)) + return nil +} + +// DeleteMenuGroup 删除菜单分组 +func (s *MenuGroupService) DeleteMenuGroup(id uint) error { + s.logger.Info("开始删除菜单分组", zap.Uint("id", id)) + + // 检查菜单分组是否存在 + _, err := s.menuGroupRepo.FindByID(id) + if err != nil { + s.logger.Error("菜单分组不存在", zap.Uint("id", id), zap.Error(err)) + return fmt.Errorf("菜单分组不存在: %w", err) + } + + if err := s.menuGroupRepo.Delete(id); err != nil { + s.logger.Error("删除菜单分组失败", zap.Error(err)) + return fmt.Errorf("删除菜单分组失败: %w", err) + } + + s.logger.Info("菜单分组删除成功", zap.Uint("id", id)) + return nil +} + +// BatchDeleteMenuGroups 批量删除菜单分组 +func (s *MenuGroupService) BatchDeleteMenuGroups(ids []uint) error { + s.logger.Info("开始批量删除菜单分组", zap.Uints("ids", ids)) + + if len(ids) == 0 { + return fmt.Errorf("删除ID列表不能为空") + } + + if err := s.menuGroupRepo.BatchDelete(ids); err != nil { + s.logger.Error("批量删除菜单分组失败", zap.Error(err)) + return fmt.Errorf("批量删除菜单分组失败: %w", err) + } + + s.logger.Info("批量删除菜单分组成功", zap.Uints("ids", ids)) + return nil +} + +// UpdateMenuGroupStatus 更新菜单分组状态 +func (s *MenuGroupService) UpdateMenuGroupStatus(id uint, status int) error { + s.logger.Info("开始更新菜单分组状态", zap.Uint("id", id), zap.Int("status", status)) + + // 检查菜单分组是否存在 + _, err := s.menuGroupRepo.FindByID(id) + if err != nil { + s.logger.Error("菜单分组不存在", zap.Uint("id", id), zap.Error(err)) + return fmt.Errorf("菜单分组不存在: %w", err) + } + + // 验证状态值 + if status != 0 && status != 1 { + return fmt.Errorf("状态值无效,只能是0或1") + } + + if err := s.menuGroupRepo.UpdateStatus(id, status); err != nil { + s.logger.Error("更新菜单分组状态失败", zap.Error(err)) + return fmt.Errorf("更新菜单分组状态失败: %w", err) + } + + s.logger.Info("菜单分组状态更新成功", zap.Uint("id", id), zap.Int("status", status)) + return nil +} + +// UpdateMenuGroupSort 更新菜单分组排序 +func (s *MenuGroupService) UpdateMenuGroupSort(id uint, sort int) error { + s.logger.Info("开始更新菜单分组排序", zap.Uint("id", id), zap.Int("sort", sort)) + + // 检查菜单分组是否存在 + _, err := s.menuGroupRepo.FindByID(id) + if err != nil { + s.logger.Error("菜单分组不存在", zap.Uint("id", id), zap.Error(err)) + return fmt.Errorf("菜单分组不存在: %w", err) + } + + if err := s.menuGroupRepo.UpdateSort(id, sort); err != nil { + s.logger.Error("更新菜单分组排序失败", zap.Error(err)) + return fmt.Errorf("更新菜单分组排序失败: %w", err) + } + + s.logger.Info("菜单分组排序更新成功", zap.Uint("id", id), zap.Int("sort", sort)) + return nil +} + +// BatchUpdateMenuGroupSort 批量更新菜单分组排序 +func (s *MenuGroupService) BatchUpdateMenuGroupSort(sorts map[uint]int) error { + s.logger.Info("开始批量更新菜单分组排序", zap.Any("sorts", sorts)) + + if len(sorts) == 0 { + return fmt.Errorf("排序数据不能为空") + } + + if err := s.menuGroupRepo.BatchUpdateSort(sorts); err != nil { + s.logger.Error("批量更新菜单分组排序失败", zap.Error(err)) + return fmt.Errorf("批量更新菜单分组排序失败: %w", err) + } + + s.logger.Info("批量更新菜单分组排序成功") + return nil +} + +// GetMenuGroupStats 获取菜单分组统计信息 +func (s *MenuGroupService) GetMenuGroupStats() (map[string]interface{}, error) { + stats, err := s.menuGroupRepo.GetStats() + if err != nil { + s.logger.Error("获取菜单分组统计信息失败", zap.Error(err)) + return nil, fmt.Errorf("获取菜单分组统计信息失败: %w", err) + } + return stats, nil +} diff --git a/gofaster/backend/internal/auth/service/menu_route_service.go b/gofaster/backend/internal/auth/service/menu_route_service.go deleted file mode 100644 index 2c23cfd..0000000 --- a/gofaster/backend/internal/auth/service/menu_route_service.go +++ /dev/null @@ -1,180 +0,0 @@ -package service - -import ( - "fmt" - "gofaster/internal/auth/model" - "gofaster/internal/auth/repository" - - "go.uber.org/zap" -) - -// MenuRouteService 菜单路由关联表服务 -type MenuRouteService struct { - menuRouteRepo *repository.MenuRouteRepository - menuRepo *repository.MenuRepository - routeRepo *repository.RouteMappingRepository - log *zap.Logger -} - -// NewMenuRouteService 创建菜单路由关联表服务 -func NewMenuRouteService( - menuRouteRepo *repository.MenuRouteRepository, - menuRepo *repository.MenuRepository, - routeRepo *repository.RouteMappingRepository, - log *zap.Logger, -) *MenuRouteService { - return &MenuRouteService{ - menuRouteRepo: menuRouteRepo, - menuRepo: menuRepo, - routeRepo: routeRepo, - log: log, - } -} - -// CreateMenuRoute 创建菜单路由关联 -func (s *MenuRouteService) CreateMenuRoute(menuRoute *model.MenuRoute) error { - // 验证菜单是否存在 - if _, err := s.menuRepo.FindByID(menuRoute.MenuID); err != nil { - return fmt.Errorf("菜单不存在") - } - - // 验证路由映射是否存在 - if _, err := s.routeRepo.FindByID(menuRoute.RouteMappingID); err != nil { - return fmt.Errorf("路由映射不存在") - } - - // 检查关联是否已存在 - if existing, err := s.menuRouteRepo.GetByMenuAndRoute(menuRoute.MenuID, menuRoute.RouteMappingID); err == nil && existing != nil { - return fmt.Errorf("菜单路由关联已存在") - } - - return s.menuRouteRepo.Create(menuRoute) -} - -// CreateMenuRoutes 批量创建菜单路由关联 -func (s *MenuRouteService) CreateMenuRoutes(menuID uint, routeMappingIDs []uint) error { - // 验证菜单是否存在 - if _, err := s.menuRepo.FindByID(menuID); err != nil { - return fmt.Errorf("菜单不存在") - } - - // 验证所有路由映射是否存在 - for _, routeID := range routeMappingIDs { - if _, err := s.routeRepo.FindByID(routeID); err != nil { - return fmt.Errorf("路由映射不存在") - } - } - - // 删除现有关联 - if err := s.menuRouteRepo.DeleteByMenuID(menuID); err != nil { - return err - } - - // 创建新关联 - var menuRoutes []*model.MenuRoute - for i, routeID := range routeMappingIDs { - menuRoute := &model.MenuRoute{ - MenuID: menuID, - RouteMappingID: routeID, - Sort: i, - IsDefault: i == 0, // 第一个设为默认 - Status: 1, - } - menuRoutes = append(menuRoutes, menuRoute) - } - - return s.menuRouteRepo.CreateBatch(menuRoutes) -} - -// GetMenuRoutes 获取菜单的路由关联 -func (s *MenuRouteService) GetMenuRoutes(menuID uint) ([]*model.MenuRoute, error) { - return s.menuRouteRepo.GetByMenuID(menuID) -} - -// GetRouteMenus 获取路由的菜单关联 -func (s *MenuRouteService) GetRouteMenus(routeMappingID uint) ([]*model.MenuRoute, error) { - return s.menuRouteRepo.GetByRouteMappingID(routeMappingID) -} - -// GetMenuWithRoutes 获取菜单及其关联的路由信息 -func (s *MenuRouteService) GetMenuWithRoutes(menuID uint) (*model.Menu, []*model.RouteMapping, error) { - return s.menuRouteRepo.GetMenuWithRoutes(menuID) -} - -// GetRouteWithMenus 获取路由及其关联的菜单信息 -func (s *MenuRouteService) GetRouteWithMenus(routeMappingID uint) (*model.RouteMapping, []*model.Menu, error) { - return s.menuRouteRepo.GetRouteWithMenus(routeMappingID) -} - -// UpdateMenuRoute 更新菜单路由关联 -func (s *MenuRouteService) UpdateMenuRoute(menuRoute *model.MenuRoute) error { - // 验证关联是否存在 - if _, err := s.menuRouteRepo.GetByID(menuRoute.ID); err != nil { - return fmt.Errorf("菜单路由关联不存在") - } - - return s.menuRouteRepo.Update(menuRoute) -} - -// DeleteMenuRoute 删除菜单路由关联 -func (s *MenuRouteService) DeleteMenuRoute(id uint) error { - return s.menuRouteRepo.Delete(id) -} - -// DeleteMenuRoutes 删除菜单的所有路由关联 -func (s *MenuRouteService) DeleteMenuRoutes(menuID uint) error { - return s.menuRouteRepo.DeleteByMenuID(menuID) -} - -// DeleteRouteMenus 删除路由的所有菜单关联 -func (s *MenuRouteService) DeleteRouteMenus(routeMappingID uint) error { - return s.menuRouteRepo.DeleteByRouteMappingID(routeMappingID) -} - -// DeleteMenuRouteByMenuAndRoute 根据菜单ID和路由ID删除关联 -func (s *MenuRouteService) DeleteMenuRouteByMenuAndRoute(menuID, routeMappingID uint) error { - return s.menuRouteRepo.DeleteByMenuAndRoute(menuID, routeMappingID) -} - -// ListMenuRoutes 获取菜单路由关联列表 -func (s *MenuRouteService) ListMenuRoutes(page, pageSize int) ([]*model.MenuRoute, int64, error) { - return s.menuRouteRepo.List(page, pageSize) -} - -// SyncMenuRoutes 同步菜单路由关联(用于批量操作) -func (s *MenuRouteService) SyncMenuRoutes(menuID uint, routeMappingIDs []uint) error { - s.log.Info("开始同步菜单路由关联", zap.Uint("menuID", menuID), zap.Any("routeMappingIDs", routeMappingIDs)) - - // 删除现有关联 - if err := s.menuRouteRepo.DeleteByMenuID(menuID); err != nil { - s.log.Error("删除现有菜单路由关联失败", zap.Error(err)) - return err - } - - // 如果没有新的路由映射,直接返回 - if len(routeMappingIDs) == 0 { - s.log.Info("没有新的路由映射,同步完成") - return nil - } - - // 创建新关联 - var menuRoutes []*model.MenuRoute - for i, routeID := range routeMappingIDs { - menuRoute := &model.MenuRoute{ - MenuID: menuID, - RouteMappingID: routeID, - Sort: i, - IsDefault: i == 0, // 第一个设为默认 - Status: 1, - } - menuRoutes = append(menuRoutes, menuRoute) - } - - if err := s.menuRouteRepo.CreateBatch(menuRoutes); err != nil { - s.log.Error("创建菜单路由关联失败", zap.Error(err)) - return err - } - - s.log.Info("菜单路由关联同步完成", zap.Int("关联数量", len(menuRoutes))) - return nil -} diff --git a/gofaster/backend/internal/auth/service/route_sync_service.go b/gofaster/backend/internal/auth/service/route_sync_service.go index 6fd8445..bd955f4 100644 --- a/gofaster/backend/internal/auth/service/route_sync_service.go +++ b/gofaster/backend/internal/auth/service/route_sync_service.go @@ -23,16 +23,16 @@ type RouteInfo struct { // RouteSyncService 路由同步服务 type RouteSyncService struct { - routeMappingRepo *repository.RouteMappingRepository - resourceRepo repository.ResourceRepository - log *zap.Logger - swaggerParser *SwaggerParser + resourceRepo repository.ResourceRepository + frontendBackendRouteRepo *repository.FrontendBackendRouteRepository + log *zap.Logger + swaggerParser *SwaggerParser } // NewRouteSyncService 创建路由同步服务实例 func NewRouteSyncService( - routeMappingRepo *repository.RouteMappingRepository, resourceRepo repository.ResourceRepository, + frontendBackendRouteRepo *repository.FrontendBackendRouteRepository, log *zap.Logger, ) *RouteSyncService { swaggerParser := NewSwaggerParser() @@ -58,10 +58,10 @@ func NewRouteSyncService( } return &RouteSyncService{ - routeMappingRepo: routeMappingRepo, - resourceRepo: resourceRepo, - log: log, - swaggerParser: swaggerParser, + resourceRepo: resourceRepo, + frontendBackendRouteRepo: frontendBackendRouteRepo, + log: log, + swaggerParser: swaggerParser, } }