Browse Source

清理重复表结构

master
hejl 13 hours ago
parent
commit
1ef88c1706
  1. 2
      gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js
  2. 354
      gofaster/backend/internal/auth/controller/menu_group_controller.go
  3. 366
      gofaster/backend/internal/auth/controller/menu_route_controller.go
  4. 174
      gofaster/backend/internal/auth/migration/create_route_tables.go
  5. 27
      gofaster/backend/internal/auth/model/menu.go
  6. 20
      gofaster/backend/internal/auth/model/menu_group.go
  7. 20
      gofaster/backend/internal/auth/model/menu_route.go
  8. 22
      gofaster/backend/internal/auth/model/route_mapping.go
  9. 18
      gofaster/backend/internal/auth/module.go
  10. 127
      gofaster/backend/internal/auth/repository/menu_group_repo.go
  11. 89
      gofaster/backend/internal/auth/repository/menu_repo.go
  12. 142
      gofaster/backend/internal/auth/repository/menu_route_repo.go
  13. 108
      gofaster/backend/internal/auth/repository/route_mapping_repo.go
  14. 45
      gofaster/backend/internal/auth/routes/menu_group_routes.go
  15. 52
      gofaster/backend/internal/auth/routes/menu_route_routes.go
  16. 4
      gofaster/backend/internal/auth/routes/route_sync_routes.go
  17. 229
      gofaster/backend/internal/auth/service/menu_group_service.go
  18. 180
      gofaster/backend/internal/auth/service/menu_route_service.go
  19. 6
      gofaster/backend/internal/auth/service/route_sync_service.go

2
gofaster/app/src/renderer/modules/route-sync/direct-route-mappings.js

@ -252,7 +252,7 @@ export default { @@ -252,7 +252,7 @@ export default {
"id": 15,
"component": "PasswordChangeModal",
"module": "user-management",
"triggerName": "passwordchangemodal-input-zhkdt8",
"triggerName": "passwordchangemodal-input-8m1x12",
"triggerType": "input"
}
]

354
gofaster/backend/internal/auth/controller/menu_group_controller.go

@ -0,0 +1,354 @@ @@ -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)
}

366
gofaster/backend/internal/auth/controller/menu_route_controller.go

@ -1,366 +0,0 @@ @@ -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)
}

174
gofaster/backend/internal/auth/migration/create_route_tables.go

@ -2,7 +2,6 @@ package migration @@ -2,7 +2,6 @@ package migration
import (
"gofaster/internal/auth/model"
"regexp"
"go.uber.org/zap"
"gorm.io/gorm"
@ -12,37 +11,12 @@ import ( @@ -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 { @@ -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字段是否存在

27
gofaster/backend/internal/auth/model/menu.go

@ -1,27 +0,0 @@ @@ -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"
}

20
gofaster/backend/internal/auth/model/menu_group.go

@ -0,0 +1,20 @@ @@ -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"
}

20
gofaster/backend/internal/auth/model/menu_route.go

@ -1,20 +0,0 @@ @@ -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"
}

22
gofaster/backend/internal/auth/model/route_mapping.go

@ -1,22 +0,0 @@ @@ -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"
}

18
gofaster/backend/internal/auth/module.go

@ -21,8 +21,8 @@ type Module struct { @@ -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 @@ -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 @@ -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) { @@ -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 同步路由信息

127
gofaster/backend/internal/auth/repository/menu_group_repo.go

@ -0,0 +1,127 @@ @@ -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
}

89
gofaster/backend/internal/auth/repository/menu_repo.go

@ -1,89 +0,0 @@ @@ -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
}

142
gofaster/backend/internal/auth/repository/menu_route_repo.go

@ -1,142 +0,0 @@ @@ -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
}

108
gofaster/backend/internal/auth/repository/route_mapping_repo.go

@ -1,108 +0,0 @@ @@ -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)
}

45
gofaster/backend/internal/auth/routes/menu_group_routes.go

@ -0,0 +1,45 @@ @@ -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) // 获取统计信息
}
}
}

52
gofaster/backend/internal/auth/routes/menu_route_routes.go

@ -1,52 +0,0 @@ @@ -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) // 获取路由及其关联的菜单信息
}
}
}

4
gofaster/backend/internal/auth/routes/route_sync_routes.go

@ -13,9 +13,9 @@ import ( @@ -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)
// 路由同步路由组

229
gofaster/backend/internal/auth/service/menu_group_service.go

@ -0,0 +1,229 @@ @@ -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
}

180
gofaster/backend/internal/auth/service/menu_route_service.go

@ -1,180 +0,0 @@ @@ -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
}

6
gofaster/backend/internal/auth/service/route_sync_service.go

@ -23,16 +23,16 @@ type RouteInfo struct { @@ -23,16 +23,16 @@ type RouteInfo struct {
// RouteSyncService 路由同步服务
type RouteSyncService struct {
routeMappingRepo *repository.RouteMappingRepository
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,8 +58,8 @@ func NewRouteSyncService( @@ -58,8 +58,8 @@ func NewRouteSyncService(
}
return &RouteSyncService{
routeMappingRepo: routeMappingRepo,
resourceRepo: resourceRepo,
frontendBackendRouteRepo: frontendBackendRouteRepo,
log: log,
swaggerParser: swaggerParser,
}

Loading…
Cancel
Save