Browse Source

清理权限表结构

master
hejl 20 hours ago
parent
commit
8538623c69
  1. 115
      gofaster/backend/FRONTEND_BACKEND_ROUTE_TO_AUTH_RESOURCES_MIGRATION.md
  2. 8
      gofaster/backend/FRONTEND_ROUTE_SYNC_UPDATES.md
  3. 211
      gofaster/backend/internal/auth/controller/frontend_route_controller.go
  4. 211
      gofaster/backend/internal/auth/controller/menus_controller.go
  5. 20
      gofaster/backend/internal/auth/controller/permission_controller.go
  6. 58
      gofaster/backend/internal/auth/migration/add_permission_code_field.go
  7. 52
      gofaster/backend/internal/auth/migration/add_unique_index.go
  8. 24
      gofaster/backend/internal/auth/migration/create_auth_resources_table.sql
  9. 37
      gofaster/backend/internal/auth/migration/create_route_tables.go
  10. 221
      gofaster/backend/internal/auth/migration/migration.go
  11. 110
      gofaster/backend/internal/auth/migration/remove_delete_at_fields.go
  12. 24
      gofaster/backend/internal/auth/model/auth_resources.go
  13. 24
      gofaster/backend/internal/auth/model/frontend_backend_route.go
  14. 8
      gofaster/backend/internal/auth/model/menus.go
  15. 53
      gofaster/backend/internal/auth/module.go
  16. 170
      gofaster/backend/internal/auth/repository/auth_resources_repo.go
  17. 170
      gofaster/backend/internal/auth/repository/frontend_backend_route_repo.go
  18. 44
      gofaster/backend/internal/auth/repository/menus_repo.go
  19. 14
      gofaster/backend/internal/auth/repository/permission_repo.go
  20. 40
      gofaster/backend/internal/auth/routes/frontend_route_routes.go
  21. 40
      gofaster/backend/internal/auth/routes/menus_routes.go
  22. 10
      gofaster/backend/internal/auth/routes/permission_routes.go
  23. 4
      gofaster/backend/internal/auth/routes/route_sync_routes.go
  24. 155
      gofaster/backend/internal/auth/service/frontend_route_service.go
  25. 155
      gofaster/backend/internal/auth/service/menus_service.go
  26. 8
      gofaster/backend/internal/auth/service/permission_service.go
  27. 12
      gofaster/backend/internal/auth/service/route_sync_service.go
  28. 10
      gofaster/backend/internal/shared/middleware/permission_middleware.go
  29. BIN
      gofaster/backend/main.exe
  30. 8
      gofaster/backend/main.go

115
gofaster/backend/FRONTEND_BACKEND_ROUTE_TO_AUTH_RESOURCES_MIGRATION.md

@ -0,0 +1,115 @@ @@ -0,0 +1,115 @@
# Frontend Backend Route 重命名为 Auth Resources - 迁移说明
## 概述
`frontend_backend_routes` 表重命名为 `auth_resources` 表,并更新所有相关的代码引用。
## 更改内容
### 1. 数据库表结构
- **原表名**: `frontend_backend_routes`
- **新表名**: `auth_resources`
- **字段**: 保持不变,只修改表名和索引名
### 2. 新建表 SQL 语句
- 文件: `backend/internal/auth/migration/create_auth_resources_table.sql`
- 包含完整的 `auth_resources` 表建表语句
### 3. 代码更改
#### 3.1 模型文件
- **文件**: `backend/internal/auth/model/auth_resources.go` (原 `frontend_backend_route.go`)
- **更改**:
- 结构体名从 `FrontendBackendRoute` 改为 `AuthResources`
- `TableName()` 方法返回 `"auth_resources"` 而不是 `"frontend_backend_routes"`
- 唯一索引名从 `idx_frontend_backend_routes_unique` 改为 `idx_auth_resources_unique`
#### 3.2 Repository 文件
- **文件**: `backend/internal/auth/repository/auth_resources_repo.go` (原 `frontend_backend_route_repo.go`)
- **更改**:
- 结构体名从 `FrontendBackendRouteRepository` 改为 `AuthResourcesRepository`
- 构造函数从 `NewFrontendBackendRouteRepository` 改为 `NewAuthResourcesRepository`
- 所有方法中的模型引用从 `model.FrontendBackendRoute` 改为 `model.AuthResources`
- SQL 查询中的表名从 `frontend_backend_routes` 改为 `auth_resources`
#### 3.3 Service 文件
- **文件**: `backend/internal/auth/service/menus_service.go`
- **更改**:
- 字段名从 `frontendBackendRouteRepo` 改为 `authResourcesRepo`
- 类型从 `*repository.FrontendBackendRouteRepository` 改为 `*repository.AuthResourcesRepository`
- 模型引用从 `model.FrontendBackendRoute` 改为 `model.AuthResources`
- **文件**: `backend/internal/auth/service/route_sync_service.go`
- **更改**:
- 字段名从 `frontendBackendRouteRepo` 改为 `authResourcesRepo`
- 类型从 `*repository.FrontendBackendRouteRepository` 改为 `*repository.AuthResourcesRepository`
#### 3.4 Routes 文件
- **文件**: `backend/internal/auth/routes/menus_routes.go`
- **文件**: `backend/internal/auth/routes/route_sync_routes.go`
- **更改**:
- 变量名从 `frontendBackendRouteRepo` 改为 `authResourcesRepo`
- 构造函数调用从 `NewFrontendBackendRouteRepository` 改为 `NewAuthResourcesRepository`
#### 3.5 Module 文件
- **文件**: `backend/internal/auth/module.go`
- **更改**:
- 变量名从 `frontendBackendRouteRepo` 改为 `authResourcesRepo`
- 构造函数调用从 `NewFrontendBackendRouteRepository` 改为 `NewAuthResourcesRepository`
#### 3.6 Middleware 文件
- **文件**: `backend/internal/shared/middleware/permission_middleware.go`
- **更改**:
- 变量名从 `frontendBackendRouteRepo` 改为 `authResourcesRepo`
- 构造函数调用从 `NewFrontendBackendRouteRepository` 改为 `NewAuthResourcesRepository`
- 函数参数类型从 `*model.FrontendBackendRoute` 改为 `*model.AuthResources`
### 4. 迁移脚本
- **SQL 脚本**: `backend/internal/auth/migration/create_auth_resources_table.sql`
## 迁移步骤
### 方法一:使用 SQL 重命名(推荐)
```sql
-- 重命名表
RENAME TABLE frontend_backend_routes TO auth_resources;
-- 重命名索引
ALTER TABLE auth_resources DROP INDEX idx_frontend_backend_routes_unique;
ALTER TABLE auth_resources ADD UNIQUE INDEX idx_auth_resources_unique (frontend_route_id, backend_route);
```
### 方法二:如果表不存在,创建新表
```sql
-- 执行 create_auth_resources_table.sql 中的建表语句
```
## 注意事项
1. **备份数据**: 执行迁移前请务必备份现有数据
2. **停止应用**: 迁移期间需要停止应用程序
3. **测试验证**: 迁移完成后请验证所有功能正常
4. **API 兼容性**: API 路径和参数保持不变,只修改了数据库表名和内部结构
5. **字段不变**: 所有字段名称和类型保持不变
## 影响范围
- ✅ 数据库表名
- ✅ Go 模型中的表名引用
- ✅ Repository 中的 SQL 查询
- ✅ Service 中的依赖注入
- ✅ Routes 中的初始化
- ✅ Module 中的依赖管理
- ✅ Middleware 中的权限检查
- ❌ API 路径(保持不变)
- ❌ 字段名称(保持不变)
- ❌ 业务逻辑(保持不变)
## 验证清单
- [ ] 数据库表已重命名为 `auth_resources`
- [ ] 应用程序能正常启动
- [ ] 菜单/路由相关 API 正常工作
- [ ] 数据查询和统计功能正常
- [ ] 前后台路由关联功能正常
- [ ] 权限中间件正常工作
- [ ] 路由同步功能正常

8
gofaster/backend/FRONTEND_ROUTE_SYNC_UPDATES.md

@ -10,11 +10,9 @@ @@ -10,11 +10,9 @@
- 保留了 `UpdatedAt` 字段用于记录更新时间
#### 数据库迁移:
- **`backend/internal/auth/migration/remove_delete_at_fields.go`**
- 添加了 `removeCreatedAtFromFrontendBackendRoutes()` 函数
- 在 `RemoveDeleteAtFields()` 函数中调用该函数
- 包含完整的表存在检查和字段存在检查
- 添加了详细的调试日志
- **`backend/internal/auth/migration/remove_delete_at_fields.go`** (已删除)
- 该文件已删除,因为新的建表语句不再包含 `created_at` 字段
- 新的表结构直接不包含该字段,无需迁移
#### 迁移逻辑:
```go

211
gofaster/backend/internal/auth/controller/frontend_route_controller.go

@ -1,211 +0,0 @@ @@ -1,211 +0,0 @@
package controller
import (
"gofaster/internal/auth/service"
"gofaster/internal/shared/response"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// FrontendRouteController 前台路由控制器
type FrontendRouteController struct {
frontendRouteService *service.FrontendRouteService
logger *zap.Logger
}
// NewFrontendRouteController 创建前台路由控制器实例
func NewFrontendRouteController(frontendRouteService *service.FrontendRouteService, logger *zap.Logger) *FrontendRouteController {
return &FrontendRouteController{
frontendRouteService: frontendRouteService,
logger: logger,
}
}
// SyncFrontendRoute 同步单个前台路由
// @Summary 同步单个前台路由
// @Description 同步单个前台路由及其后台路由关联
// @Tags 前台路由
// @Accept json
// @Produce json
// @Param route body map[string]interface{} true "前台路由数据"
// @Success 200 {object} response.Response
// @Router /api/frontend-routes/sync [post]
func (c *FrontendRouteController) SyncFrontendRoute(ctx *gin.Context) {
var routeData map[string]interface{}
if err := ctx.ShouldBindJSON(&routeData); err != nil {
c.logger.Error("解析前台路由数据失败", zap.Error(err))
response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error())
return
}
if err := c.frontendRouteService.SyncFrontendRoute(routeData); err != nil {
c.logger.Error("同步前台路由失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "同步前台路由失败", err.Error())
return
}
response.Success(ctx, "前台路由同步成功", nil)
}
// BatchSyncFrontendRoutes 批量同步前台路由
// @Summary 批量同步前台路由
// @Description 批量同步前台路由及其后台路由关联
// @Tags 前台路由
// @Accept json
// @Produce json
// @Param routes body []map[string]interface{} true "前台路由数据列表"
// @Success 200 {object} response.Response
// @Router /api/frontend-routes/batch-sync [post]
func (c *FrontendRouteController) BatchSyncFrontendRoutes(ctx *gin.Context) {
var routesData []map[string]interface{}
if err := ctx.ShouldBindJSON(&routesData); err != nil {
c.logger.Error("解析前台路由数据失败", zap.Error(err))
response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error())
return
}
if err := c.frontendRouteService.BatchSyncFrontendRoutes(routesData); err != nil {
c.logger.Error("批量同步前台路由失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "批量同步前台路由失败", err.Error())
return
}
response.Success(ctx, "批量同步前台路由成功", nil)
}
// GetFrontendRoutes 获取前台路由列表
// @Summary 获取前台路由列表
// @Description 获取所有前台路由列表
// @Tags 前台路由
// @Produce json
// @Success 200 {object} response.Response
// @Router /api/frontend-routes [get]
func (c *FrontendRouteController) GetFrontendRoutes(ctx *gin.Context) {
routes, err := c.frontendRouteService.GetFrontendRoutes()
if err != nil {
c.logger.Error("获取前台路由列表失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取前台路由列表失败", err.Error())
return
}
response.Success(ctx, "获取前台路由列表成功", routes)
}
// GetFrontendRouteByID 根据ID获取前台路由
// @Summary 根据ID获取前台路由
// @Description 根据ID获取前台路由详情
// @Tags 前台路由
// @Produce json
// @Param id path int true "前台路由ID"
// @Success 200 {object} response.Response
// @Router /api/frontend-routes/{id} [get]
func (c *FrontendRouteController) GetFrontendRouteByID(ctx *gin.Context) {
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Error("解析前台路由ID失败", zap.Error(err))
response.Error(ctx, http.StatusBadRequest, "无效的前台路由ID", err.Error())
return
}
route, err := c.frontendRouteService.GetFrontendRouteByID(uint(id))
if err != nil {
c.logger.Error("获取前台路由失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取前台路由失败", err.Error())
return
}
response.Success(ctx, "获取前台路由成功", route)
}
// GetFrontendRoutesByModule 根据模块获取前台路由
// @Summary 根据模块获取前台路由
// @Description 根据模块获取前台路由列表
// @Tags 前台路由
// @Produce json
// @Param module query string true "模块名称"
// @Success 200 {object} response.Response
// @Router /api/frontend-routes/by-module [get]
func (c *FrontendRouteController) GetFrontendRoutesByModule(ctx *gin.Context) {
module := ctx.Query("module")
if module == "" {
c.logger.Error("模块参数为空")
response.Error(ctx, http.StatusBadRequest, "模块参数不能为空", "module parameter is required")
return
}
routes, err := c.frontendRouteService.GetFrontendRoutesByModule(module)
if err != nil {
c.logger.Error("根据模块获取前台路由失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "根据模块获取前台路由失败", err.Error())
return
}
response.Success(ctx, "根据模块获取前台路由成功", routes)
}
// GetRouteRelations 获取路由关联关系
// @Summary 获取路由关联关系
// @Description 获取前台路由与后台路由的关联关系
// @Tags 前台路由
// @Produce json
// @Success 200 {object} response.Response
// @Router /api/frontend-routes/relations [get]
func (c *FrontendRouteController) GetRouteRelations(ctx *gin.Context) {
relations, err := c.frontendRouteService.GetRouteRelations()
if err != nil {
c.logger.Error("获取路由关联关系失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取路由关联关系失败", err.Error())
return
}
response.Success(ctx, "获取路由关联关系成功", relations)
}
// GetRouteRelationsByFrontendRouteID 根据前台路由ID获取关联关系
// @Summary 根据前台路由ID获取关联关系
// @Description 根据前台路由ID获取其与后台路由的关联关系
// @Tags 前台路由
// @Produce json
// @Param id path int true "前台路由ID"
// @Success 200 {object} response.Response
// @Router /api/frontend-routes/{id}/relations [get]
func (c *FrontendRouteController) GetRouteRelationsByFrontendRouteID(ctx *gin.Context) {
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Error("解析前台路由ID失败", zap.Error(err))
response.Error(ctx, http.StatusBadRequest, "无效的前台路由ID", err.Error())
return
}
relations, err := c.frontendRouteService.GetRouteRelationsByFrontendRouteID(uint(id))
if err != nil {
c.logger.Error("获取前台路由关联关系失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取前台路由关联关系失败", err.Error())
return
}
response.Success(ctx, "获取前台路由关联关系成功", relations)
}
// GetStats 获取统计信息
// @Summary 获取前台路由统计信息
// @Description 获取前台路由和路由关联的统计信息
// @Tags 前台路由
// @Produce json
// @Success 200 {object} response.Response
// @Router /api/frontend-routes/stats [get]
func (c *FrontendRouteController) GetStats(ctx *gin.Context) {
stats, err := c.frontendRouteService.GetStats()
if err != nil {
c.logger.Error("获取前台路由统计信息失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取前台路由统计信息失败", err.Error())
return
}
response.Success(ctx, "获取前台路由统计信息成功", stats)
}

211
gofaster/backend/internal/auth/controller/menus_controller.go

@ -0,0 +1,211 @@ @@ -0,0 +1,211 @@
package controller
import (
"gofaster/internal/auth/service"
"gofaster/internal/shared/response"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// MenusController 菜单控制器
type MenusController struct {
menusService *service.MenusService
logger *zap.Logger
}
// NewMenusController 创建菜单控制器实例
func NewMenusController(menusService *service.MenusService, logger *zap.Logger) *MenusController {
return &MenusController{
menusService: menusService,
logger: logger,
}
}
// SyncMenu 同步单个菜单
// @Summary 同步单个菜单
// @Description 同步单个菜单及其后台路由关联
// @Tags 菜单
// @Accept json
// @Produce json
// @Param menu body map[string]interface{} true "菜单数据"
// @Success 200 {object} response.Response
// @Router /api/menus/sync [post]
func (c *MenusController) SyncMenu(ctx *gin.Context) {
var menuData map[string]interface{}
if err := ctx.ShouldBindJSON(&menuData); err != nil {
c.logger.Error("解析菜单数据失败", zap.Error(err))
response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error())
return
}
if err := c.menusService.SyncMenu(menuData); err != nil {
c.logger.Error("同步菜单失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "同步菜单失败", err.Error())
return
}
response.Success(ctx, "菜单同步成功", nil)
}
// BatchSyncMenus 批量同步菜单
// @Summary 批量同步菜单
// @Description 批量同步菜单及其后台路由关联
// @Tags 菜单
// @Accept json
// @Produce json
// @Param menus body []map[string]interface{} true "菜单数据列表"
// @Success 200 {object} response.Response
// @Router /api/menus/batch-sync [post]
func (c *MenusController) BatchSyncMenus(ctx *gin.Context) {
var menusData []map[string]interface{}
if err := ctx.ShouldBindJSON(&menusData); err != nil {
c.logger.Error("解析菜单数据失败", zap.Error(err))
response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error())
return
}
if err := c.menusService.BatchSyncMenus(menusData); err != nil {
c.logger.Error("批量同步菜单失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "批量同步菜单失败", err.Error())
return
}
response.Success(ctx, "批量同步菜单成功", nil)
}
// GetMenus 获取菜单列表
// @Summary 获取菜单列表
// @Description 获取所有菜单列表
// @Tags 菜单
// @Produce json
// @Success 200 {object} response.Response
// @Router /api/menus [get]
func (c *MenusController) GetMenus(ctx *gin.Context) {
menus, err := c.menusService.GetMenus()
if err != nil {
c.logger.Error("获取菜单列表失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取菜单列表失败", err.Error())
return
}
response.Success(ctx, "获取菜单列表成功", menus)
}
// GetMenuByID 根据ID获取菜单
// @Summary 根据ID获取菜单
// @Description 根据ID获取菜单详情
// @Tags 菜单
// @Produce json
// @Param id path int true "菜单ID"
// @Success 200 {object} response.Response
// @Router /api/menus/{id} [get]
func (c *MenusController) GetMenuByID(ctx *gin.Context) {
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Error("解析菜单ID失败", zap.Error(err))
response.Error(ctx, http.StatusBadRequest, "无效的菜单ID", err.Error())
return
}
menu, err := c.menusService.GetMenuByID(uint(id))
if err != nil {
c.logger.Error("获取菜单失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取菜单失败", err.Error())
return
}
response.Success(ctx, "获取菜单成功", menu)
}
// GetMenusByModule 根据模块获取菜单
// @Summary 根据模块获取菜单
// @Description 根据模块获取菜单列表
// @Tags 菜单
// @Produce json
// @Param module query string true "模块名称"
// @Success 200 {object} response.Response
// @Router /api/menus/by-module [get]
func (c *MenusController) GetMenusByModule(ctx *gin.Context) {
module := ctx.Query("module")
if module == "" {
c.logger.Error("模块参数为空")
response.Error(ctx, http.StatusBadRequest, "模块参数不能为空", "module parameter is required")
return
}
menus, err := c.menusService.GetMenusByModule(module)
if err != nil {
c.logger.Error("根据模块获取菜单失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "根据模块获取菜单失败", err.Error())
return
}
response.Success(ctx, "根据模块获取菜单成功", menus)
}
// GetRouteRelations 获取路由关联关系
// @Summary 获取路由关联关系
// @Description 获取菜单与后台路由的关联关系
// @Tags 菜单
// @Produce json
// @Success 200 {object} response.Response
// @Router /api/menus/relations [get]
func (c *MenusController) GetRouteRelations(ctx *gin.Context) {
relations, err := c.menusService.GetRouteRelations()
if err != nil {
c.logger.Error("获取路由关联关系失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取路由关联关系失败", err.Error())
return
}
response.Success(ctx, "获取路由关联关系成功", relations)
}
// GetRouteRelationsByMenuID 根据菜单ID获取关联关系
// @Summary 根据菜单ID获取关联关系
// @Description 根据菜单ID获取其与后台路由的关联关系
// @Tags 菜单
// @Produce json
// @Param id path int true "菜单ID"
// @Success 200 {object} response.Response
// @Router /api/menus/{id}/relations [get]
func (c *MenusController) GetRouteRelationsByMenuID(ctx *gin.Context) {
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Error("解析菜单ID失败", zap.Error(err))
response.Error(ctx, http.StatusBadRequest, "无效的菜单ID", err.Error())
return
}
relations, err := c.menusService.GetRouteRelationsByMenuID(uint(id))
if err != nil {
c.logger.Error("获取菜单关联关系失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取菜单关联关系失败", err.Error())
return
}
response.Success(ctx, "获取菜单关联关系成功", relations)
}
// GetStats 获取统计信息
// @Summary 获取菜单统计信息
// @Description 获取菜单和路由关联的统计信息
// @Tags 菜单
// @Produce json
// @Success 200 {object} response.Response
// @Router /api/menus/stats [get]
func (c *MenusController) GetStats(ctx *gin.Context) {
stats, err := c.menusService.GetStats()
if err != nil {
c.logger.Error("获取菜单统计信息失败", zap.Error(err))
response.Error(ctx, http.StatusInternalServerError, "获取菜单统计信息失败", err.Error())
return
}
response.Success(ctx, "获取菜单统计信息成功", stats)
}

20
gofaster/backend/internal/auth/controller/permission_controller.go

@ -114,25 +114,25 @@ func (c *PermissionController) GetPermissionsByBackendRoute(ctx *gin.Context) { @@ -114,25 +114,25 @@ func (c *PermissionController) GetPermissionsByBackendRoute(ctx *gin.Context) {
response.Success(ctx, "获取权限成功", permissions)
}
// GetPermissionsByFrontendRouteID 根据前端路由ID获取权限
// @Summary 根据前端路由ID获取权限
// @Description 根据前端路由ID获取相关权限
// GetPermissionsByMenuID 根据菜单ID获取权限
// @Summary 根据菜单ID获取权限
// @Description 根据菜单ID获取相关权限
// @Tags 权限管理
// @Accept json
// @Produce json
// @Param frontendRouteId path int true "前端路由ID"
// @Param menuId path int true "菜单ID"
// @Success 200 {object} response.Response{data=[]model.Permission}
// @Failure 400 {object} response.Response
// @Router /api/auth/permissions/frontend-route/{frontendRouteId} [get]
func (c *PermissionController) GetPermissionsByFrontendRouteID(ctx *gin.Context) {
idStr := ctx.Param("frontendRouteId")
// @Router /api/auth/permissions/menu/{menuId} [get]
func (c *PermissionController) GetPermissionsByMenuID(ctx *gin.Context) {
idStr := ctx.Param("menuId")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
response.Error(ctx, http.StatusBadRequest, "无效的前端路由ID", err.Error())
response.Error(ctx, http.StatusBadRequest, "无效的菜单ID", err.Error())
return
}
permissions, err := c.permissionService.GetPermissionsByFrontendRouteID(ctx.Request.Context(), uint(id))
permissions, err := c.permissionService.GetPermissionsByMenuID(ctx.Request.Context(), uint(id))
if err != nil {
response.Error(ctx, http.StatusInternalServerError, "获取权限失败", err.Error())
return
@ -193,4 +193,4 @@ func (c *PermissionController) GetUserPermissions(ctx *gin.Context) { @@ -193,4 +193,4 @@ func (c *PermissionController) GetUserPermissions(ctx *gin.Context) {
}
response.Success(ctx, "获取用户权限成功", permissions)
}
}

58
gofaster/backend/internal/auth/migration/add_permission_code_field.go

@ -1,58 +0,0 @@ @@ -1,58 +0,0 @@
package migration
import (
"fmt"
"gofaster/internal/auth/model"
"gorm.io/gorm"
)
// AddPermissionCodeField 为权限表添加code字段
func AddPermissionCodeField(db *gorm.DB) error {
fmt.Println("🔧 开始为权限表添加code字段...")
// 检查code字段是否已存在
var count int64
err := db.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'permissions' AND column_name = 'code'").Count(&count).Error
if err != nil {
return fmt.Errorf("检查code字段失败: %v", err)
}
if count > 0 {
fmt.Println("✅ code字段已存在,跳过添加")
return nil
}
// 添加code字段
err = db.Exec("ALTER TABLE permissions ADD COLUMN code VARCHAR(50)").Error
if err != nil {
return fmt.Errorf("添加code字段失败: %v", err)
}
// 为现有权限记录生成code
var permissions []model.Permission
err = db.Find(&permissions).Error
if err != nil {
return fmt.Errorf("查询现有权限失败: %v", err)
}
fmt.Printf("🔧 为 %d 条现有权限记录生成code...\n", len(permissions))
for _, permission := range permissions {
code := fmt.Sprintf("%s:%s", permission.Resource, permission.Action)
err = db.Model(&permission).Update("code", code).Error
if err != nil {
return fmt.Errorf("更新权限 %d 的code失败: %v", permission.ID, err)
}
fmt.Printf("✅ 已为权限 '%s' 生成code: %s\n", permission.Name, code)
}
// 添加唯一索引
err = db.Exec("CREATE UNIQUE INDEX idx_permissions_code ON permissions(code)").Error
if err != nil {
return fmt.Errorf("创建code唯一索引失败: %v", err)
}
fmt.Println("✅ 权限表code字段添加完成")
return nil
}

52
gofaster/backend/internal/auth/migration/add_unique_index.go

@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
package migration
import (
"fmt"
"go.uber.org/zap"
"gorm.io/gorm"
)
// AddUniqueIndexToFrontendBackendRoutes 为 frontend_backend_routes 表添加唯一索引
func AddUniqueIndexToFrontendBackendRoutes(db *gorm.DB, log *zap.Logger) error {
log.Info("开始为 frontend_backend_routes 表添加唯一索引...")
// 检查表是否存在
if !db.Migrator().HasTable("frontend_backend_routes") {
log.Info("frontend_backend_routes 表不存在,跳过添加唯一索引")
return nil
}
// 检查唯一索引是否已存在
var indexExists bool
err := db.Raw(`
SELECT COUNT(*) > 0
FROM pg_indexes
WHERE tablename = 'frontend_backend_routes'
AND indexname = 'idx_frontend_backend_routes_unique'
`).Scan(&indexExists).Error
if err != nil {
log.Error("检查唯一索引是否存在失败", zap.Error(err))
return fmt.Errorf("检查唯一索引失败: %w", err)
}
if indexExists {
log.Info("唯一索引已存在,跳过创建")
return nil
}
// 创建唯一索引
err = db.Exec(`
CREATE UNIQUE INDEX idx_frontend_backend_routes_unique
ON frontend_backend_routes (frontend_route_id, backend_route)
`).Error
if err != nil {
log.Error("创建唯一索引失败", zap.Error(err))
return fmt.Errorf("创建唯一索引失败: %w", err)
}
log.Info("✅ frontend_backend_routes 表唯一索引创建成功")
return nil
}

24
gofaster/backend/internal/auth/migration/create_auth_resources_table.sql

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
-- 创建 auth_resources 表(原 frontend_backend_routes 表)
CREATE TABLE IF NOT EXISTS `auth_resources` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`created_at` datetime(3) DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间',
`deleted_at` datetime(3) DEFAULT NULL COMMENT '删除时间',
`frontend_route_id` bigint(20) unsigned NOT NULL COMMENT '前台路由ID',
`backend_route` varchar(255) NOT NULL COMMENT '后台API路径',
`http_method` varchar(10) NOT NULL COMMENT 'HTTP方法',
`component` varchar(255) NOT NULL COMMENT '前端组件名称',
`module` varchar(255) NOT NULL COMMENT '所属模块',
`description` text COMMENT '描述',
`sort` int(11) DEFAULT '0' COMMENT '排序',
`status` int(11) DEFAULT '1' COMMENT '状态:1-启用,0-禁用',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_auth_resources_unique` (`frontend_route_id`, `backend_route`),
KEY `idx_auth_resources_frontend_route_id` (`frontend_route_id`),
KEY `idx_auth_resources_backend_route` (`backend_route`),
KEY `idx_auth_resources_module` (`module`),
KEY `idx_auth_resources_component` (`component`),
KEY `idx_auth_resources_status` (`status`),
KEY `idx_auth_resources_sort` (`sort`),
KEY `idx_auth_resources_deleted_at` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='认证资源表(原前后台路由关系表)';

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

@ -1,37 +0,0 @@ @@ -1,37 +0,0 @@
package migration
import (
"gofaster/internal/auth/model"
"go.uber.org/zap"
"gorm.io/gorm"
)
// CreateRouteTables 创建路由相关表
func CreateRouteTables(db *gorm.DB, log *zap.Logger) error {
log.Info("开始创建路由相关表...")
// 创建菜单分组表
if err := db.AutoMigrate(&model.MenuGroup{}); err != nil {
log.Error("创建菜单分组表失败", zap.Error(err))
return err
}
log.Info("✅ 菜单分组表创建完成")
// 创建前台路由表
if err := db.AutoMigrate(&model.FrontendRoute{}); err != nil {
log.Error("创建前台路由表失败", zap.Error(err))
return err
}
log.Info("✅ 前台路由表创建完成")
// 创建前后台路由关系表
if err := db.AutoMigrate(&model.FrontendBackendRoute{}); err != nil {
log.Error("创建前后台路由关系表失败", zap.Error(err))
return err
}
log.Info("✅ 前后台路由关系表创建完成")
log.Info("路由相关表创建完成")
return nil
}

221
gofaster/backend/internal/auth/migration/migration.go

@ -1,221 +0,0 @@ @@ -1,221 +0,0 @@
package migration
import (
"fmt"
"gofaster/internal/auth/model"
"gofaster/internal/auth/repository"
"gofaster/internal/shared/logger"
"gorm.io/gorm"
)
// RunMigrations 运行数据库迁移
func RunMigrations(db *gorm.DB) error {
log := logger.NewLogger("info", "")
defer log.Sync()
// 自动迁移用户表
if err := db.AutoMigrate(&model.User{}); err != nil {
return err
}
// 手动检查并添加可能缺失的字段
if err := ensureUserFields(db); err != nil {
return err
}
// 自动迁移角色表
if err := db.AutoMigrate(&model.Role{}); err != nil {
return err
}
// 自动迁移用户角色关联表
if err := db.AutoMigrate(&model.UserRole{}); err != nil {
return err
}
// 自动迁移验证码表
if err := db.AutoMigrate(&repository.Captcha{}); err != nil {
return err
}
// 自动迁移密码策略相关表
if err := db.AutoMigrate(
&model.PasswordPolicy{},
&model.PasswordHistory{},
&model.PasswordReset{},
); err != nil {
return err
}
// 自动迁移权限相关表
if err := db.AutoMigrate(
&model.Permission{},
&model.RolePermission{},
); err != nil {
return err
}
// 为权限表添加code字段
if err := AddPermissionCodeField(db); err != nil {
return err
}
// 创建路由相关表
if err := CreateRouteTables(db, log); err != nil {
return err
}
// 移除相关表的 delete_at 字段
if err := RemoveDeleteAtFields(db, log); err != nil {
return err
}
// 为 frontend_backend_routes 表添加唯一索引
if err := AddUniqueIndexToFrontendBackendRoutes(db, log); err != nil {
return err
}
// 创建默认角色
if err := createDefaultRoles(db); err != nil {
return err
}
// 创建默认管理员用户
if err := createDefaultAdmin(db); err != nil {
return err
}
// 创建默认密码策略
if err := createDefaultPasswordPolicy(db); err != nil {
return err
}
return nil
}
// ensureUserFields 确保用户表有必要的字段
func ensureUserFields(db *gorm.DB) error {
// 检查PasswordChangedAt字段是否存在
if !db.Migrator().HasColumn(&model.User{}, "password_changed_at") {
fmt.Println("添加 password_changed_at 字段到 users 表")
if err := db.Exec("ALTER TABLE users ADD COLUMN password_changed_at TIMESTAMP NULL").Error; err != nil {
return fmt.Errorf("添加 password_changed_at 字段失败: %w", err)
}
}
// 检查ForceChangePassword字段是否存在
if !db.Migrator().HasColumn(&model.User{}, "force_change_password") {
fmt.Println("添加 force_change_password 字段到 users 表")
if err := db.Exec("ALTER TABLE users ADD COLUMN force_change_password BOOLEAN DEFAULT FALSE").Error; err != nil {
return fmt.Errorf("添加 force_change_password 字段失败: %w", err)
}
}
fmt.Println("用户表字段检查完成")
return nil
}
// createDefaultRoles 创建默认角色
func createDefaultRoles(db *gorm.DB) error {
// 检查是否已存在角色
var count int64
db.Model(&model.Role{}).Count(&count)
if count > 0 {
return nil // 已存在角色,跳过
}
roles := []model.Role{
{
Name: "超级管理员",
Code: "SUPER_ADMIN",
Description: "系统超级管理员,拥有所有权限",
},
{
Name: "管理员",
Code: "ADMIN",
Description: "系统管理员,拥有大部分权限",
},
{
Name: "普通用户",
Code: "USER",
Description: "普通用户,拥有基本权限",
},
}
for _, role := range roles {
if err := db.Create(&role).Error; err != nil {
return err
}
}
return nil
}
// createDefaultAdmin 创建默认管理员用户
func createDefaultAdmin(db *gorm.DB) error {
// 检查是否已存在管理员用户
var count int64
db.Model(&model.User{}).Where("username = ?", "admin").Count(&count)
if count > 0 {
return nil // 已存在管理员用户,跳过
}
// 创建默认管理员用户
adminUser := &model.User{
Username: "admin",
Password: "$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", // "password"
Email: "admin@gofaster.com",
Phone: "13800138000",
Status: 1, // 正常状态
}
if err := db.Create(adminUser).Error; err != nil {
return err
}
// 获取超级管理员角色
var superAdminRole model.Role
if err := db.Where("code = ?", "SUPER_ADMIN").First(&superAdminRole).Error; err != nil {
return err
}
// 关联超级管理员角色
if err := db.Model(adminUser).Association("Roles").Append(&superAdminRole); err != nil {
return err
}
return nil
}
// createDefaultPasswordPolicy 创建默认密码策略
func createDefaultPasswordPolicy(db *gorm.DB) error {
// 检查是否已存在默认策略
var count int64
db.Model(&model.PasswordPolicy{}).Count(&count)
if count > 0 {
return nil // 已存在默认策略,跳过
}
// 创建默认密码策略(1级)
defaultPolicy := &model.PasswordPolicy{
Level: 1,
MinRequiredLevel: 1, // 新增:要求最低1级强度
MinLength: 6,
RequireUppercase: false,
RequireLowercase: false,
RequireNumbers: false,
RequireSpecial: false,
MinCharTypes: 1,
ExpirationDays: 30,
PreventReuse: 3,
IsActive: true,
}
if err := db.Create(defaultPolicy).Error; err != nil {
return err
}
return nil
}

110
gofaster/backend/internal/auth/migration/remove_delete_at_fields.go

@ -1,110 +0,0 @@ @@ -1,110 +0,0 @@
package migration
import (
"fmt"
"go.uber.org/zap"
"gorm.io/gorm"
)
// RemoveDeleteAtFields 移除相关表的 delete_at 字段
func RemoveDeleteAtFields(db *gorm.DB, log *zap.Logger) error {
log.Info("开始移除相关表的 delete_at 字段...")
// 1. 移除 frontend_backend_routes 表的 delete_at 字段
if err := removeDeleteAtFromFrontendBackendRoutes(db, log); err != nil {
return fmt.Errorf("移除 frontend_backend_routes 表 delete_at 字段失败: %w", err)
}
// 2. 移除 frontend_routes 表的 delete_at 字段
if err := removeDeleteAtFromFrontendRoutes(db, log); err != nil {
return fmt.Errorf("移除 frontend_routes 表 delete_at 字段失败: %w", err)
}
log.Info("✅ 所有表的 delete_at 字段移除完成")
return nil
}
// removeDeleteAtFromFrontendBackendRoutes 移除 frontend_backend_routes 表的 delete_at 字段
func removeDeleteAtFromFrontendBackendRoutes(db *gorm.DB, log *zap.Logger) error {
log.Info("移除 frontend_backend_routes 表的 delete_at 字段...")
// 检查表是否存在
if !db.Migrator().HasTable("frontend_backend_routes") {
log.Info("frontend_backend_routes 表不存在,跳过")
return nil
}
// 检查 delete_at 字段是否存在
if !db.Migrator().HasColumn("frontend_backend_routes", "deleted_at") {
log.Info("frontend_backend_routes 表没有 deleted_at 字段,跳过")
return nil
}
// 删除 delete_at 字段
if err := db.Exec("ALTER TABLE frontend_backend_routes DROP COLUMN deleted_at").Error; err != nil {
return fmt.Errorf("删除 deleted_at 字段失败: %w", err)
}
log.Info("✅ frontend_backend_routes 表的 deleted_at 字段移除成功")
return nil
}
// removeDeleteAtFromFrontendRoutes 移除 frontend_routes 表的 delete_at 字段
func removeDeleteAtFromFrontendRoutes(db *gorm.DB, log *zap.Logger) error {
log.Info("移除 frontend_routes 表的 delete_at 字段...")
// 检查表是否存在
if !db.Migrator().HasTable("frontend_routes") {
log.Info("frontend_routes 表不存在,跳过")
return nil
}
// 检查 delete_at 字段是否存在
if !db.Migrator().HasColumn("frontend_routes", "deleted_at") {
log.Info("frontend_routes 表没有 deleted_at 字段,跳过")
return nil
}
// 删除 delete_at 字段
if err := db.Exec("ALTER TABLE frontend_routes DROP COLUMN deleted_at").Error; err != nil {
return fmt.Errorf("删除 deleted_at 字段失败: %w", err)
}
log.Info("✅ frontend_routes 表的 deleted_at 字段移除成功")
return nil
}
// removeDeleteAtFromRouteMappings 移除 route_mappings 表的 delete_at 字段
func removeDeleteAtFromRouteMappings(db *gorm.DB, log *zap.Logger) error {
log.Info("🔧 开始移除 route_mappings 表的 delete_at 字段...")
// 检查表是否存在
tableExists := db.Migrator().HasTable("route_mappings")
log.Info("📊 表存在检查", zap.Bool("route_mappings_exists", tableExists))
if !tableExists {
log.Info("⚠ route_mappings 表不存在,跳过")
return nil
}
// 检查 delete_at 字段是否存在
columnExists := db.Migrator().HasColumn("route_mappings", "deleted_at")
log.Info("📊 字段存在检查", zap.Bool("deleted_at_exists", columnExists))
if !columnExists {
log.Info("ℹ route_mappings 表没有 deleted_at 字段,跳过")
return nil
}
// 删除 delete_at 字段
ddlQuery := "ALTER TABLE route_mappings DROP COLUMN deleted_at"
log.Info("🗑 执行DDL", zap.String("query", ddlQuery))
if err := db.Exec(ddlQuery).Error; err != nil {
log.Error("❌ 删除 deleted_at 字段失败", zap.Error(err))
return fmt.Errorf("删除 deleted_at 字段失败: %w", err)
}
log.Info("✅ route_mappings 表的 deleted_at 字段移除成功")
return nil
}

24
gofaster/backend/internal/auth/model/auth_resources.go

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
package model
import (
"time"
)
// AuthResources 认证资源模型(原前后台路由关系模型)
type AuthResources struct {
ID uint `gorm:"primarykey" json:"id"`
UpdatedAt time.Time `json:"updated_at"`
FrontendRouteID uint `gorm:"uniqueIndex:idx_auth_resources_unique" json:"frontend_route_id"` // 前台路由ID
BackendRoute string `gorm:"uniqueIndex:idx_auth_resources_unique" json:"backend_route"` // 后台API路径
HTTPMethod string `json:"http_method"` // HTTP方法
Component string `json:"component"` // 前端组件名称
Module string `json:"module"` // 所属模块
Description string `json:"description"` // 描述
Sort int `gorm:"default:0" json:"sort"` // 排序
Status int `gorm:"default:1" json:"status"` // 状态:1-启用,0-禁用
}
// TableName 指定表名
func (AuthResources) TableName() string {
return "auth_resources"
}

24
gofaster/backend/internal/auth/model/frontend_backend_route.go

@ -1,24 +0,0 @@ @@ -1,24 +0,0 @@
package model
import (
"time"
)
// FrontendBackendRoute 前后台路由关系模型
type FrontendBackendRoute struct {
ID uint `gorm:"primarykey" json:"id"`
UpdatedAt time.Time `json:"updated_at"`
FrontendRouteID uint `gorm:"uniqueIndex:idx_frontend_backend_routes_unique" json:"frontend_route_id"` // 前台路由ID
BackendRoute string `gorm:"uniqueIndex:idx_frontend_backend_routes_unique" json:"backend_route"` // 后台API路径
HTTPMethod string `json:"http_method"` // HTTP方法
Component string `json:"component"` // 前端组件名称
Module string `json:"module"` // 所属模块
Description string `json:"description"` // 描述
Sort int `gorm:"default:0" json:"sort"` // 排序
Status int `gorm:"default:1" json:"status"` // 状态:1-启用,0-禁用
}
// TableName 指定表名
func (FrontendBackendRoute) TableName() string {
return "frontend_backend_routes"
}

8
gofaster/backend/internal/auth/model/frontend_route.go → gofaster/backend/internal/auth/model/menus.go

@ -4,8 +4,8 @@ import ( @@ -4,8 +4,8 @@ import (
"time"
)
// FrontendRoute 前台路由模型
type FrontendRoute struct {
// Menus 前台路由模型
type Menus struct {
ID uint `gorm:"primarykey" json:"id"`
UpdatedAt time.Time `json:"updated_at"`
Path string `json:"path"` // 前台路由路径
@ -18,6 +18,6 @@ type FrontendRoute struct { @@ -18,6 +18,6 @@ type FrontendRoute struct {
}
// TableName 指定表名
func (FrontendRoute) TableName() string {
return "frontend_routes"
func (Menus) TableName() string {
return "menus"
}

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

@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
package auth
import (
"log"
"gofaster/internal/auth/controller"
"gofaster/internal/auth/model"
"gofaster/internal/auth/repository"
"gofaster/internal/auth/routes"
"gofaster/internal/auth/service"
@ -18,14 +17,14 @@ import ( @@ -18,14 +17,14 @@ import (
// Module 认证模块
type Module struct {
userController *controller.UserController
authController *controller.AuthController
passwordController *controller.PasswordController
frontendRouteController *controller.FrontendRouteController
menuGroupController *controller.MenuGroupController
routeSyncService *service.RouteSyncService
db *gorm.DB
logger *zap.Logger
userController *controller.UserController
authController *controller.AuthController
passwordController *controller.PasswordController
menusController *controller.MenusController
menuGroupController *controller.MenuGroupController
routeSyncService *service.RouteSyncService
db *gorm.DB
logger *zap.Logger
}
// NewModule 创建新的认证模块
@ -43,14 +42,32 @@ func (m *Module) Init(cfg *config.Config, logger *zap.Logger, db *gorm.DB, redis @@ -43,14 +42,32 @@ func (m *Module) Init(cfg *config.Config, logger *zap.Logger, db *gorm.DB, redis
m.db = db
m.logger = logger
// 自动迁移认证相关表
if err := db.AutoMigrate(
&model.User{},
&model.Role{},
&model.Permission{},
&model.UserRole{},
&model.RolePermission{},
&model.MenuGroup{},
&model.Menus{},
&model.AuthResources{},
&model.PasswordPolicy{},
&model.PasswordHistory{},
&model.PasswordReset{},
); err != nil {
logger.Error("认证模块表迁移失败", zap.Error(err))
return err
}
// 初始化仓库
userRepo := repository.NewUserRepository(db)
passwordPolicyRepo := repository.NewPasswordPolicyRepository(db)
passwordHistoryRepo := repository.NewPasswordHistoryRepository(db)
passwordResetRepo := repository.NewPasswordResetRepository(db)
menuGroupRepo := repository.NewMenuGroupRepository(db)
frontendRouteRepo := repository.NewFrontendRouteRepository(db)
frontendBackendRouteRepo := repository.NewFrontendBackendRouteRepository(db)
frontendRouteRepo := repository.NewMenusRepository(db)
authResourcesRepo := repository.NewAuthResourcesRepository(db)
// 初始化服务
userService := service.NewUserService(userRepo, db)
@ -63,17 +80,17 @@ func (m *Module) Init(cfg *config.Config, logger *zap.Logger, db *gorm.DB, redis @@ -63,17 +80,17 @@ func (m *Module) Init(cfg *config.Config, logger *zap.Logger, db *gorm.DB, redis
passwordResetRepo,
)
menuGroupService := service.NewMenuGroupService(menuGroupRepo, logger)
frontendRouteService := service.NewFrontendRouteService(frontendRouteRepo, frontendBackendRouteRepo, logger)
m.routeSyncService = service.NewRouteSyncService(frontendBackendRouteRepo, logger)
menusService := service.NewMenusService(frontendRouteRepo, authResourcesRepo, logger)
m.routeSyncService = service.NewRouteSyncService(authResourcesRepo, logger)
// 初始化控制器
m.userController = controller.NewUserController(userService)
m.authController = controller.NewAuthController(authService)
m.passwordController = controller.NewPasswordController(passwordService, userService)
m.menuGroupController = controller.NewMenuGroupController(menuGroupService, logger)
m.frontendRouteController = controller.NewFrontendRouteController(frontendRouteService, logger)
m.menusController = controller.NewMenusController(menusService, logger)
log.Printf("✅ 认证模块初始化完成")
logger.Info("✅ 认证模块初始化完成")
return nil
}
@ -103,8 +120,8 @@ func (m *Module) RegisterRoutes(router *gin.RouterGroup) { @@ -103,8 +120,8 @@ func (m *Module) RegisterRoutes(router *gin.RouterGroup) {
// 注册路由同步路由
routes.RegisterRouteSyncRoutes(router, m.db, m.logger)
// 注册前台路由路由
routes.RegisterFrontendRouteRoutes(router, m.db, m.logger)
// 注册菜单路由
routes.RegisterMenusRoutes(router, m.db, m.logger)
// 注册菜单分组路由
routes.RegisterMenuGroupRoutes(router, m.db, m.logger)

170
gofaster/backend/internal/auth/repository/auth_resources_repo.go

@ -0,0 +1,170 @@ @@ -0,0 +1,170 @@
package repository
import (
"gofaster/internal/auth/model"
"gorm.io/gorm"
)
// AuthResourcesRepository 认证资源仓库(原前后台路由关系仓库)
type AuthResourcesRepository struct {
db *gorm.DB
}
// NewAuthResourcesRepository 创建认证资源仓库实例
func NewAuthResourcesRepository(db *gorm.DB) *AuthResourcesRepository {
return &AuthResourcesRepository{db: db}
}
// Create 创建认证资源
func (r *AuthResourcesRepository) Create(resource *model.AuthResources) error {
return r.db.Create(resource).Error
}
// FindByID 根据ID查找认证资源
func (r *AuthResourcesRepository) FindByID(id uint) (*model.AuthResources, error) {
var resource model.AuthResources
err := r.db.Where("id = ?", id).First(&resource).Error
if err != nil {
return nil, err
}
return &resource, nil
}
// FindByFrontendRouteID 根据前台路由ID查找资源
func (r *AuthResourcesRepository) FindByFrontendRouteID(frontendRouteID uint) ([]*model.AuthResources, error) {
var resources []*model.AuthResources
err := r.db.Where("frontend_route_id = ?", frontendRouteID).Order("sort ASC").Find(&resources).Error
return resources, err
}
// FindByBackendRoute 根据后台路由查找资源
func (r *AuthResourcesRepository) FindByBackendRoute(backendRoute string) ([]*model.AuthResources, error) {
var resources []*model.AuthResources
err := r.db.Where("backend_route = ?", backendRoute).Find(&resources).Error
return resources, err
}
// FindByModule 根据模块查找资源
func (r *AuthResourcesRepository) FindByModule(module string) ([]*model.AuthResources, error) {
var resources []*model.AuthResources
err := r.db.Where("module = ?", module).Order("sort ASC").Find(&resources).Error
return resources, err
}
// FindByComponent 根据组件名称查找资源
func (r *AuthResourcesRepository) FindByComponent(component string) ([]*model.AuthResources, error) {
var resources []*model.AuthResources
err := r.db.Where("component = ?", component).Order("sort ASC").Find(&resources).Error
return resources, err
}
// List 获取认证资源列表
func (r *AuthResourcesRepository) List() ([]*model.AuthResources, error) {
var resources []*model.AuthResources
err := r.db.Order("sort ASC").Find(&resources).Error
return resources, err
}
// Update 更新认证资源
func (r *AuthResourcesRepository) Update(resource *model.AuthResources) error {
return r.db.Save(resource).Error
}
// Delete 删除认证资源
func (r *AuthResourcesRepository) Delete(id uint) error {
return r.db.Delete(&model.AuthResources{}, id).Error
}
// DeleteByFrontendRouteID 根据前台路由ID删除资源
func (r *AuthResourcesRepository) DeleteByFrontendRouteID(frontendRouteID uint) error {
return r.db.Where("frontend_route_id = ?", frontendRouteID).Delete(&model.AuthResources{}).Error
}
// Upsert 更新或插入认证资源
func (r *AuthResourcesRepository) Upsert(resource *model.AuthResources) error {
// 使用 PostgreSQL 的 ON CONFLICT 语法处理唯一索引冲突
sql := `
INSERT INTO auth_resources
(frontend_route_id, backend_route, http_method, component, module, description, sort, status, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())
ON CONFLICT (frontend_route_id, backend_route)
DO UPDATE SET
http_method = EXCLUDED.http_method,
component = EXCLUDED.component,
module = EXCLUDED.module,
description = EXCLUDED.description,
sort = EXCLUDED.sort,
status = EXCLUDED.status,
updated_at = NOW()
RETURNING id
`
var id uint
err := r.db.Raw(sql,
resource.FrontendRouteID,
resource.BackendRoute,
resource.HTTPMethod,
resource.Component,
resource.Module,
resource.Description,
resource.Sort,
resource.Status,
).Scan(&id).Error
if err != nil {
return err
}
// 设置返回的ID
resource.ID = id
return nil
}
// GetStats 获取认证资源统计信息
func (r *AuthResourcesRepository) GetStats() (map[string]interface{}, error) {
var total int64
var componentStats []struct {
Component string `json:"component"`
Count int64 `json:"count"`
}
var moduleStats []struct {
Module string `json:"module"`
Count int64 `json:"count"`
}
if err := r.db.Model(&model.AuthResources{}).Count(&total).Error; err != nil {
return nil, err
}
if err := r.db.Model(&model.AuthResources{}).
Select("component, count(*) as count").
Group("component").
Scan(&componentStats).Error; err != nil {
return nil, err
}
if err := r.db.Model(&model.AuthResources{}).
Select("module, count(*) as count").
Group("module").
Scan(&moduleStats).Error; err != nil {
return nil, err
}
return map[string]interface{}{
"total": total,
"component_stats": componentStats,
"module_stats": moduleStats,
}, nil
}
// GetWithFrontendRoute 获取资源并包含前台路由信息
func (r *AuthResourcesRepository) GetWithFrontendRoute() ([]map[string]interface{}, error) {
var results []map[string]interface{}
err := r.db.Table("auth_resources").
Select("auth_resources.*, menus.path as frontend_path, menus.name as frontend_name").
Joins("LEFT JOIN menus ON auth_resources.frontend_route_id = menus.id").
Order("auth_resources.sort ASC").
Scan(&results).Error
return results, err
}

170
gofaster/backend/internal/auth/repository/frontend_backend_route_repo.go

@ -1,170 +0,0 @@ @@ -1,170 +0,0 @@
package repository
import (
"gofaster/internal/auth/model"
"gorm.io/gorm"
)
// FrontendBackendRouteRepository 前后台路由关系仓库
type FrontendBackendRouteRepository struct {
db *gorm.DB
}
// NewFrontendBackendRouteRepository 创建前后台路由关系仓库实例
func NewFrontendBackendRouteRepository(db *gorm.DB) *FrontendBackendRouteRepository {
return &FrontendBackendRouteRepository{db: db}
}
// Create 创建前后台路由关系
func (r *FrontendBackendRouteRepository) Create(relation *model.FrontendBackendRoute) error {
return r.db.Create(relation).Error
}
// FindByID 根据ID查找前后台路由关系
func (r *FrontendBackendRouteRepository) FindByID(id uint) (*model.FrontendBackendRoute, error) {
var relation model.FrontendBackendRoute
err := r.db.Where("id = ?", id).First(&relation).Error
if err != nil {
return nil, err
}
return &relation, nil
}
// FindByFrontendRouteID 根据前台路由ID查找关系
func (r *FrontendBackendRouteRepository) FindByFrontendRouteID(frontendRouteID uint) ([]*model.FrontendBackendRoute, error) {
var relations []*model.FrontendBackendRoute
err := r.db.Where("frontend_route_id = ?", frontendRouteID).Order("sort ASC").Find(&relations).Error
return relations, err
}
// FindByBackendRoute 根据后台路由查找关系
func (r *FrontendBackendRouteRepository) FindByBackendRoute(backendRoute string) ([]*model.FrontendBackendRoute, error) {
var relations []*model.FrontendBackendRoute
err := r.db.Where("backend_route = ?", backendRoute).Find(&relations).Error
return relations, err
}
// FindByModule 根据模块查找关系
func (r *FrontendBackendRouteRepository) FindByModule(module string) ([]*model.FrontendBackendRoute, error) {
var relations []*model.FrontendBackendRoute
err := r.db.Where("module = ?", module).Order("sort ASC").Find(&relations).Error
return relations, err
}
// FindByComponent 根据组件名称查找关系
func (r *FrontendBackendRouteRepository) FindByComponent(component string) ([]*model.FrontendBackendRoute, error) {
var relations []*model.FrontendBackendRoute
err := r.db.Where("component = ?", component).Order("sort ASC").Find(&relations).Error
return relations, err
}
// List 获取前后台路由关系列表
func (r *FrontendBackendRouteRepository) List() ([]*model.FrontendBackendRoute, error) {
var relations []*model.FrontendBackendRoute
err := r.db.Order("sort ASC").Find(&relations).Error
return relations, err
}
// Update 更新前后台路由关系
func (r *FrontendBackendRouteRepository) Update(relation *model.FrontendBackendRoute) error {
return r.db.Save(relation).Error
}
// Delete 删除前后台路由关系
func (r *FrontendBackendRouteRepository) Delete(id uint) error {
return r.db.Delete(&model.FrontendBackendRoute{}, id).Error
}
// DeleteByFrontendRouteID 根据前台路由ID删除关系
func (r *FrontendBackendRouteRepository) DeleteByFrontendRouteID(frontendRouteID uint) error {
return r.db.Where("frontend_route_id = ?", frontendRouteID).Delete(&model.FrontendBackendRoute{}).Error
}
// Upsert 更新或插入前后台路由关系
func (r *FrontendBackendRouteRepository) Upsert(relation *model.FrontendBackendRoute) error {
// 使用 PostgreSQL 的 ON CONFLICT 语法处理唯一索引冲突
sql := `
INSERT INTO frontend_backend_routes
(frontend_route_id, backend_route, http_method, component, module, description, sort, status, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())
ON CONFLICT (frontend_route_id, backend_route)
DO UPDATE SET
http_method = EXCLUDED.http_method,
component = EXCLUDED.component,
module = EXCLUDED.module,
description = EXCLUDED.description,
sort = EXCLUDED.sort,
status = EXCLUDED.status,
updated_at = NOW()
RETURNING id
`
var id uint
err := r.db.Raw(sql,
relation.FrontendRouteID,
relation.BackendRoute,
relation.HTTPMethod,
relation.Component,
relation.Module,
relation.Description,
relation.Sort,
relation.Status,
).Scan(&id).Error
if err != nil {
return err
}
// 设置返回的ID
relation.ID = id
return nil
}
// GetStats 获取前后台路由关系统计信息
func (r *FrontendBackendRouteRepository) GetStats() (map[string]interface{}, error) {
var total int64
var componentStats []struct {
Component string `json:"component"`
Count int64 `json:"count"`
}
var moduleStats []struct {
Module string `json:"module"`
Count int64 `json:"count"`
}
if err := r.db.Model(&model.FrontendBackendRoute{}).Count(&total).Error; err != nil {
return nil, err
}
if err := r.db.Model(&model.FrontendBackendRoute{}).
Select("component, count(*) as count").
Group("component").
Scan(&componentStats).Error; err != nil {
return nil, err
}
if err := r.db.Model(&model.FrontendBackendRoute{}).
Select("module, count(*) as count").
Group("module").
Scan(&moduleStats).Error; err != nil {
return nil, err
}
return map[string]interface{}{
"total": total,
"component_stats": componentStats,
"module_stats": moduleStats,
}, nil
}
// GetWithFrontendRoute 获取关系并包含前台路由信息
func (r *FrontendBackendRouteRepository) GetWithFrontendRoute() ([]map[string]interface{}, error) {
var results []map[string]interface{}
err := r.db.Table("frontend_backend_routes").
Select("frontend_backend_routes.*, frontend_routes.path as frontend_path, frontend_routes.name as frontend_name").
Joins("LEFT JOIN frontend_routes ON frontend_backend_routes.frontend_route_id = frontend_routes.id").
Order("frontend_backend_routes.sort ASC").
Scan(&results).Error
return results, err
}

44
gofaster/backend/internal/auth/repository/frontend_route_repo.go → gofaster/backend/internal/auth/repository/menus_repo.go

@ -6,24 +6,24 @@ import ( @@ -6,24 +6,24 @@ import (
"gorm.io/gorm"
)
// FrontendRouteRepository 前台路由仓库
type FrontendRouteRepository struct {
// MenusRepository 前台路由仓库
type MenusRepository struct {
db *gorm.DB
}
// NewFrontendRouteRepository 创建前台路由仓库实例
func NewFrontendRouteRepository(db *gorm.DB) *FrontendRouteRepository {
return &FrontendRouteRepository{db: db}
// NewMenusRepository 创建前台路由仓库实例
func NewMenusRepository(db *gorm.DB) *MenusRepository {
return &MenusRepository{db: db}
}
// Create 创建前台路由
func (r *FrontendRouteRepository) Create(route *model.FrontendRoute) error {
func (r *MenusRepository) Create(route *model.Menus) error {
return r.db.Create(route).Error
}
// FindByID 根据ID查找前台路由
func (r *FrontendRouteRepository) FindByID(id uint) (*model.FrontendRoute, error) {
var route model.FrontendRoute
func (r *MenusRepository) FindByID(id uint) (*model.Menus, error) {
var route model.Menus
err := r.db.Where("id = ?", id).First(&route).Error
if err != nil {
return nil, err
@ -32,8 +32,8 @@ func (r *FrontendRouteRepository) FindByID(id uint) (*model.FrontendRoute, error @@ -32,8 +32,8 @@ func (r *FrontendRouteRepository) FindByID(id uint) (*model.FrontendRoute, error
}
// FindByPath 根据路径查找前台路由
func (r *FrontendRouteRepository) FindByPath(path string) (*model.FrontendRoute, error) {
var route model.FrontendRoute
func (r *MenusRepository) FindByPath(path string) (*model.Menus, error) {
var route model.Menus
err := r.db.Where("path = ?", path).First(&route).Error
if err != nil {
return nil, err
@ -42,32 +42,32 @@ func (r *FrontendRouteRepository) FindByPath(path string) (*model.FrontendRoute, @@ -42,32 +42,32 @@ func (r *FrontendRouteRepository) FindByPath(path string) (*model.FrontendRoute,
}
// FindByModule 根据模块查找前台路由
func (r *FrontendRouteRepository) FindByModule(module string) ([]*model.FrontendRoute, error) {
var routes []*model.FrontendRoute
func (r *MenusRepository) FindByModule(module string) ([]*model.Menus, error) {
var routes []*model.Menus
err := r.db.Where("module = ?", module).Order("sort ASC").Find(&routes).Error
return routes, err
}
// List 获取前台路由列表
func (r *FrontendRouteRepository) List() ([]*model.FrontendRoute, error) {
var routes []*model.FrontendRoute
func (r *MenusRepository) List() ([]*model.Menus, error) {
var routes []*model.Menus
err := r.db.Order("sort ASC").Find(&routes).Error
return routes, err
}
// Update 更新前台路由
func (r *FrontendRouteRepository) Update(route *model.FrontendRoute) error {
func (r *MenusRepository) Update(route *model.Menus) error {
return r.db.Save(route).Error
}
// Delete 删除前台路由
func (r *FrontendRouteRepository) Delete(id uint) error {
return r.db.Delete(&model.FrontendRoute{}, id).Error
func (r *MenusRepository) Delete(id uint) error {
return r.db.Delete(&model.Menus{}, id).Error
}
// UpsertByPath 根据路径更新或插入前台路由
func (r *FrontendRouteRepository) UpsertByPath(route *model.FrontendRoute) error {
var existingRoute model.FrontendRoute
func (r *MenusRepository) UpsertByPath(route *model.Menus) error {
var existingRoute model.Menus
err := r.db.Where("path = ?", route.Path).First(&existingRoute).Error
if err != nil {
@ -84,18 +84,18 @@ func (r *FrontendRouteRepository) UpsertByPath(route *model.FrontendRoute) error @@ -84,18 +84,18 @@ func (r *FrontendRouteRepository) UpsertByPath(route *model.FrontendRoute) error
}
// GetStats 获取前台路由统计信息
func (r *FrontendRouteRepository) GetStats() (map[string]interface{}, error) {
func (r *MenusRepository) GetStats() (map[string]interface{}, error) {
var total int64
var moduleStats []struct {
Module string `json:"module"`
Count int64 `json:"count"`
}
if err := r.db.Model(&model.FrontendRoute{}).Count(&total).Error; err != nil {
if err := r.db.Model(&model.Menus{}).Count(&total).Error; err != nil {
return nil, err
}
if err := r.db.Model(&model.FrontendRoute{}).
if err := r.db.Model(&model.Menus{}).
Select("module, count(*) as count").
Group("module").
Scan(&moduleStats).Error; err != nil {

14
gofaster/backend/internal/auth/repository/permission_repo.go

@ -12,7 +12,7 @@ type PermissionRepository interface { @@ -12,7 +12,7 @@ type PermissionRepository interface {
GetByID(ctx context.Context, id uint) (*model.Permission, error)
List(ctx context.Context, offset, limit int) ([]*model.Permission, int64, error)
GetByBackendRoute(ctx context.Context, backendRoute string) ([]*model.Permission, error)
GetByFrontendRouteID(ctx context.Context, frontendRouteID uint) ([]*model.Permission, error)
GetByMenuID(ctx context.Context, menuID uint) ([]*model.Permission, error)
GetByMenuGroupID(ctx context.Context, menuGroupID uint) ([]*model.Permission, error)
GetByUserID(ctx context.Context, userID uint) ([]*model.Permission, error)
}
@ -65,9 +65,9 @@ func (r *permissionRepository) GetByBackendRoute(ctx context.Context, backendRou @@ -65,9 +65,9 @@ func (r *permissionRepository) GetByBackendRoute(ctx context.Context, backendRou
return permissions, nil
}
func (r *permissionRepository) GetByFrontendRouteID(ctx context.Context, frontendRouteID uint) ([]*model.Permission, error) {
func (r *permissionRepository) GetByMenuID(ctx context.Context, menuID uint) ([]*model.Permission, error) {
var permissions []*model.Permission
err := r.DB().WithContext(ctx).Where("frontend_route_id = ?", frontendRouteID).Find(&permissions).Error
err := r.DB().WithContext(ctx).Where("frontend_route_id = ?", menuID).Find(&permissions).Error
if err != nil {
return nil, err
}
@ -85,7 +85,7 @@ func (r *permissionRepository) GetByMenuGroupID(ctx context.Context, menuGroupID @@ -85,7 +85,7 @@ func (r *permissionRepository) GetByMenuGroupID(ctx context.Context, menuGroupID
func (r *permissionRepository) GetByUserID(ctx context.Context, userID uint) ([]*model.Permission, error) {
var permissions []*model.Permission
// 通过用户角色获取权限
err := r.DB().WithContext(ctx).Table("permissions").
Joins("JOIN role_permissions ON permissions.id = role_permissions.permission_id").
@ -93,10 +93,10 @@ func (r *permissionRepository) GetByUserID(ctx context.Context, userID uint) ([] @@ -93,10 +93,10 @@ func (r *permissionRepository) GetByUserID(ctx context.Context, userID uint) ([]
Where("user_roles.user_id = ?", userID).
Distinct("permissions.*").
Find(&permissions).Error
if err != nil {
return nil, err
}
return permissions, nil
}
}

40
gofaster/backend/internal/auth/routes/frontend_route_routes.go

@ -1,40 +0,0 @@ @@ -1,40 +0,0 @@
package routes
import (
"gofaster/internal/auth/controller"
"gofaster/internal/auth/repository"
"gofaster/internal/auth/service"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"gorm.io/gorm"
)
// RegisterFrontendRouteRoutes 注册前台路由相关路由
func RegisterFrontendRouteRoutes(router *gin.RouterGroup, db *gorm.DB, logger *zap.Logger) {
// 初始化依赖
frontendRouteRepo := repository.NewFrontendRouteRepository(db)
frontendBackendRouteRepo := repository.NewFrontendBackendRouteRepository(db)
frontendRouteService := service.NewFrontendRouteService(frontendRouteRepo, frontendBackendRouteRepo, logger)
frontendRouteController := controller.NewFrontendRouteController(frontendRouteService, logger)
// 前台路由路由组
frontendRouteGroup := router.Group("/frontend-routes")
{
// 前台路由同步(系统初始化操作,不需要认证)
{
frontendRouteGroup.POST("/sync", frontendRouteController.SyncFrontendRoute) // 同步单个前台路由
frontendRouteGroup.POST("/batch-sync", frontendRouteController.BatchSyncFrontendRoutes) // 批量同步前台路由
}
// 前台路由查询(需要认证)
{
frontendRouteGroup.GET("", frontendRouteController.GetFrontendRoutes) // 获取前台路由列表
frontendRouteGroup.GET("/:id", frontendRouteController.GetFrontendRouteByID) // 根据ID获取前台路由
frontendRouteGroup.GET("/by-module", frontendRouteController.GetFrontendRoutesByModule) // 根据模块获取前台路由
frontendRouteGroup.GET("/relations", frontendRouteController.GetRouteRelations) // 获取路由关联关系
frontendRouteGroup.GET("/:id/relations", frontendRouteController.GetRouteRelationsByFrontendRouteID) // 根据前台路由ID获取关联关系
frontendRouteGroup.GET("/stats", frontendRouteController.GetStats) // 获取统计信息
}
}
}

40
gofaster/backend/internal/auth/routes/menus_routes.go

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
package routes
import (
"gofaster/internal/auth/controller"
"gofaster/internal/auth/repository"
"gofaster/internal/auth/service"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"gorm.io/gorm"
)
// RegisterMenusRoutes 注册菜单相关路由
func RegisterMenusRoutes(router *gin.RouterGroup, db *gorm.DB, logger *zap.Logger) {
// 初始化依赖
menusRepo := repository.NewMenusRepository(db)
authResourcesRepo := repository.NewAuthResourcesRepository(db)
menusService := service.NewMenusService(menusRepo, authResourcesRepo, logger)
menusController := controller.NewMenusController(menusService, logger)
// 菜单路由组
menusGroup := router.Group("/menus")
{
// 菜单同步(系统初始化操作,不需要认证)
{
menusGroup.POST("/sync", menusController.SyncMenu) // 同步单个菜单
menusGroup.POST("/batch-sync", menusController.BatchSyncMenus) // 批量同步菜单
}
// 菜单查询(需要认证)
{
menusGroup.GET("", menusController.GetMenus) // 获取菜单列表
menusGroup.GET("/:id", menusController.GetMenuByID) // 根据ID获取菜单
menusGroup.GET("/by-module", menusController.GetMenusByModule) // 根据模块获取菜单
menusGroup.GET("/relations", menusController.GetRouteRelations) // 获取路由关联关系
menusGroup.GET("/:id/relations", menusController.GetRouteRelationsByMenuID) // 根据菜单ID获取关联关系
menusGroup.GET("/stats", menusController.GetStats) // 获取统计信息
}
}
}

10
gofaster/backend/internal/auth/routes/permission_routes.go

@ -23,12 +23,12 @@ func RegisterPermissionRoutes(router *gin.RouterGroup, db *gorm.DB, jwtSecret st @@ -23,12 +23,12 @@ func RegisterPermissionRoutes(router *gin.RouterGroup, db *gorm.DB, jwtSecret st
permissionGroup.Use(middleware.JWTAuth()) // 暂时只检查JWT,不检查权限
{
// 权限查询操作
permissionGroup.GET("", permissionController.ListPermissions) // 获取权限列表
permissionGroup.GET("/:id", permissionController.GetPermission) // 获取权限详情
permissionGroup.GET("/backend-route", permissionController.GetPermissionsByBackendRoute) // 根据后端路由获取权限
permissionGroup.GET("/frontend-route/:frontendRouteId", permissionController.GetPermissionsByFrontendRouteID) // 根据前端路由ID获取权限
permissionGroup.GET("", permissionController.ListPermissions) // 获取权限列表
permissionGroup.GET("/:id", permissionController.GetPermission) // 获取权限详情
permissionGroup.GET("/backend-route", permissionController.GetPermissionsByBackendRoute) // 根据后端路由获取权限
permissionGroup.GET("/menu/:menuId", permissionController.GetPermissionsByMenuID) // 根据菜单ID获取权限
permissionGroup.GET("/menu-group/:menuGroupId", permissionController.GetPermissionsByMenuGroupID) // 根据菜单分组ID获取权限
permissionGroup.GET("/user/:userId", permissionController.GetUserPermissions) // 获取用户权限
permissionGroup.GET("/user/:userId", permissionController.GetUserPermissions) // 获取用户权限
}
}
}

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

@ -13,8 +13,8 @@ import ( @@ -13,8 +13,8 @@ import (
// RegisterRouteSyncRoutes 注册路由同步相关路由
func RegisterRouteSyncRoutes(router *gin.RouterGroup, db *gorm.DB, logger *zap.Logger) {
// 初始化依赖
frontendBackendRouteRepo := repository.NewFrontendBackendRouteRepository(db)
routeSyncService := service.NewRouteSyncService(frontendBackendRouteRepo, logger)
authResourcesRepo := repository.NewAuthResourcesRepository(db)
routeSyncService := service.NewRouteSyncService(authResourcesRepo, logger)
// 路由同步路由组
routeSyncGroup := router.Group("/route-sync")

155
gofaster/backend/internal/auth/service/frontend_route_service.go

@ -1,155 +0,0 @@ @@ -1,155 +0,0 @@
package service
import (
"fmt"
"gofaster/internal/auth/model"
"gofaster/internal/auth/repository"
"go.uber.org/zap"
)
// FrontendRouteService 前台路由服务
type FrontendRouteService struct {
frontendRouteRepo *repository.FrontendRouteRepository
frontendBackendRouteRepo *repository.FrontendBackendRouteRepository
logger *zap.Logger
}
// NewFrontendRouteService 创建前台路由服务实例
func NewFrontendRouteService(
frontendRouteRepo *repository.FrontendRouteRepository,
frontendBackendRouteRepo *repository.FrontendBackendRouteRepository,
logger *zap.Logger,
) *FrontendRouteService {
return &FrontendRouteService{
frontendRouteRepo: frontendRouteRepo,
frontendBackendRouteRepo: frontendBackendRouteRepo,
logger: logger,
}
}
// SyncFrontendRoute 同步单个前台路由
func (s *FrontendRouteService) SyncFrontendRoute(routeData map[string]interface{}) error {
// s.logger.Info("开始同步前台路由", zap.String("path", routeData["path"].(string)))
// 1. 创建或更新前台路由
frontendRoute := &model.FrontendRoute{
Path: routeData["path"].(string),
Name: routeData["name"].(string),
Component: routeData["component"].(string),
Module: routeData["module"].(string),
Description: routeData["description"].(string),
Sort: int(routeData["sort"].(float64)),
Status: 1,
}
if err := s.frontendRouteRepo.UpsertByPath(frontendRoute); err != nil {
s.logger.Error("同步前台路由失败", zap.Error(err))
return fmt.Errorf("同步前台路由失败: %w", err)
}
// 2. 获取前台路由ID
existingRoute, err := s.frontendRouteRepo.FindByPath(frontendRoute.Path)
if err != nil {
s.logger.Error("查找前台路由失败", zap.Error(err))
return fmt.Errorf("查找前台路由失败: %w", err)
}
// 3. 处理后台路由关联
if backendRoutes, ok := routeData["backend_routes"].([]interface{}); ok {
// 先删除现有的关联
if err := s.frontendBackendRouteRepo.DeleteByFrontendRouteID(existingRoute.ID); err != nil {
s.logger.Error("删除现有关联失败", zap.Error(err))
return fmt.Errorf("删除现有关联失败: %w", err)
}
// 创建新的关联
for i, backendRouteData := range backendRoutes {
backendRoute := backendRouteData.(map[string]interface{})
relation := &model.FrontendBackendRoute{
FrontendRouteID: existingRoute.ID,
BackendRoute: backendRoute["backend_route"].(string),
HTTPMethod: backendRoute["http_method"].(string),
Component: routeData["component"].(string), // 使用前台路由的组件名称
Module: backendRoute["module"].(string),
Description: backendRoute["description"].(string),
Sort: i,
Status: 1,
}
if err := s.frontendBackendRouteRepo.Upsert(relation); err != nil {
s.logger.Error("创建前后台路由关联失败", zap.Error(err))
return fmt.Errorf("创建前后台路由关联失败: %w", err)
}
}
}
// s.logger.Info("前台路由同步成功", zap.String("path", frontendRoute.Path))
return nil
}
// BatchSyncFrontendRoutes 批量同步前台路由
func (s *FrontendRouteService) BatchSyncFrontendRoutes(routesData []map[string]interface{}) error {
s.logger.Info("开始批量同步前台路由", zap.Int("count", len(routesData)))
for i, routeData := range routesData {
if err := s.SyncFrontendRoute(routeData); err != nil {
s.logger.Error("批量同步前台路由失败",
zap.Int("index", i),
zap.String("path", routeData["path"].(string)),
zap.Error(err))
return fmt.Errorf("批量同步前台路由失败 [%d]: %w", i, err)
}
}
s.logger.Info("批量同步前台路由完成", zap.Int("count", len(routesData)))
return nil
}
// GetFrontendRoutes 获取前台路由列表
func (s *FrontendRouteService) GetFrontendRoutes() ([]*model.FrontendRoute, error) {
return s.frontendRouteRepo.List()
}
// GetFrontendRouteByID 根据ID获取前台路由
func (s *FrontendRouteService) GetFrontendRouteByID(id uint) (*model.FrontendRoute, error) {
return s.frontendRouteRepo.FindByID(id)
}
// GetFrontendRouteByPath 根据路径获取前台路由
func (s *FrontendRouteService) GetFrontendRouteByPath(path string) (*model.FrontendRoute, error) {
return s.frontendRouteRepo.FindByPath(path)
}
// GetFrontendRoutesByModule 根据模块获取前台路由
func (s *FrontendRouteService) GetFrontendRoutesByModule(module string) ([]*model.FrontendRoute, error) {
return s.frontendRouteRepo.FindByModule(module)
}
// GetRouteRelations 获取路由关联关系
func (s *FrontendRouteService) GetRouteRelations() ([]map[string]interface{}, error) {
return s.frontendBackendRouteRepo.GetWithFrontendRoute()
}
// GetRouteRelationsByFrontendRouteID 根据前台路由ID获取关联关系
func (s *FrontendRouteService) GetRouteRelationsByFrontendRouteID(frontendRouteID uint) ([]*model.FrontendBackendRoute, error) {
return s.frontendBackendRouteRepo.FindByFrontendRouteID(frontendRouteID)
}
// GetStats 获取统计信息
func (s *FrontendRouteService) GetStats() (map[string]interface{}, error) {
frontendStats, err := s.frontendRouteRepo.GetStats()
if err != nil {
return nil, err
}
relationStats, err := s.frontendBackendRouteRepo.GetStats()
if err != nil {
return nil, err
}
return map[string]interface{}{
"frontend_routes": frontendStats,
"route_relations": relationStats,
}, nil
}

155
gofaster/backend/internal/auth/service/menus_service.go

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
package service
import (
"fmt"
"gofaster/internal/auth/model"
"gofaster/internal/auth/repository"
"go.uber.org/zap"
)
// MenusService 菜单服务
type MenusService struct {
menusRepo *repository.MenusRepository
authResourcesRepo *repository.AuthResourcesRepository
logger *zap.Logger
}
// NewMenusService 创建菜单服务实例
func NewMenusService(
menusRepo *repository.MenusRepository,
authResourcesRepo *repository.AuthResourcesRepository,
logger *zap.Logger,
) *MenusService {
return &MenusService{
menusRepo: menusRepo,
authResourcesRepo: authResourcesRepo,
logger: logger,
}
}
// SyncMenu 同步单个菜单
func (s *MenusService) SyncMenu(menuData map[string]interface{}) error {
// s.logger.Info("开始同步菜单", zap.String("path", menuData["path"].(string)))
// 1. 创建或更新菜单
menu := &model.Menus{
Path: menuData["path"].(string),
Name: menuData["name"].(string),
Component: menuData["component"].(string),
Module: menuData["module"].(string),
Description: menuData["description"].(string),
Sort: int(menuData["sort"].(float64)),
Status: 1,
}
if err := s.menusRepo.UpsertByPath(menu); err != nil {
s.logger.Error("同步菜单失败", zap.Error(err))
return fmt.Errorf("同步菜单失败: %w", err)
}
// 2. 获取菜单ID
existingMenu, err := s.menusRepo.FindByPath(menu.Path)
if err != nil {
s.logger.Error("查找菜单失败", zap.Error(err))
return fmt.Errorf("查找菜单失败: %w", err)
}
// 3. 处理后台路由关联
if backendRoutes, ok := menuData["backend_routes"].([]interface{}); ok {
// 先删除现有的关联
if err := s.authResourcesRepo.DeleteByFrontendRouteID(existingMenu.ID); err != nil {
s.logger.Error("删除现有关联失败", zap.Error(err))
return fmt.Errorf("删除现有关联失败: %w", err)
}
// 创建新的关联
for i, backendRouteData := range backendRoutes {
backendRoute := backendRouteData.(map[string]interface{})
relation := &model.AuthResources{
FrontendRouteID: existingMenu.ID,
BackendRoute: backendRoute["backend_route"].(string),
HTTPMethod: backendRoute["http_method"].(string),
Component: menuData["component"].(string), // 使用菜单的组件名称
Module: backendRoute["module"].(string),
Description: backendRoute["description"].(string),
Sort: i,
Status: 1,
}
if err := s.authResourcesRepo.Upsert(relation); err != nil {
s.logger.Error("创建菜单路由关联失败", zap.Error(err))
return fmt.Errorf("创建菜单路由关联失败: %w", err)
}
}
}
// s.logger.Info("菜单同步成功", zap.String("path", menu.Path))
return nil
}
// BatchSyncMenus 批量同步菜单
func (s *MenusService) BatchSyncMenus(menusData []map[string]interface{}) error {
s.logger.Info("开始批量同步菜单", zap.Int("count", len(menusData)))
for i, menuData := range menusData {
if err := s.SyncMenu(menuData); err != nil {
s.logger.Error("批量同步菜单失败",
zap.Int("index", i),
zap.String("path", menuData["path"].(string)),
zap.Error(err))
return fmt.Errorf("批量同步菜单失败 [%d]: %w", i, err)
}
}
s.logger.Info("批量同步菜单完成", zap.Int("count", len(menusData)))
return nil
}
// GetMenus 获取菜单列表
func (s *MenusService) GetMenus() ([]*model.Menus, error) {
return s.menusRepo.List()
}
// GetMenuByID 根据ID获取菜单
func (s *MenusService) GetMenuByID(id uint) (*model.Menus, error) {
return s.menusRepo.FindByID(id)
}
// GetMenuByPath 根据路径获取菜单
func (s *MenusService) GetMenuByPath(path string) (*model.Menus, error) {
return s.menusRepo.FindByPath(path)
}
// GetMenusByModule 根据模块获取菜单
func (s *MenusService) GetMenusByModule(module string) ([]*model.Menus, error) {
return s.menusRepo.FindByModule(module)
}
// GetRouteRelations 获取路由关联关系
func (s *MenusService) GetRouteRelations() ([]map[string]interface{}, error) {
return s.authResourcesRepo.GetWithFrontendRoute()
}
// GetRouteRelationsByMenuID 根据菜单ID获取关联关系
func (s *MenusService) GetRouteRelationsByMenuID(menuID uint) ([]*model.AuthResources, error) {
return s.authResourcesRepo.FindByFrontendRouteID(menuID)
}
// GetStats 获取统计信息
func (s *MenusService) GetStats() (map[string]interface{}, error) {
menusStats, err := s.menusRepo.GetStats()
if err != nil {
return nil, err
}
relationStats, err := s.authResourcesRepo.GetStats()
if err != nil {
return nil, err
}
return map[string]interface{}{
"menus": menusStats,
"route_relations": relationStats,
}, nil
}

8
gofaster/backend/internal/auth/service/permission_service.go

@ -31,9 +31,9 @@ func (s *PermissionService) GetPermissionsByBackendRoute(ctx context.Context, ba @@ -31,9 +31,9 @@ func (s *PermissionService) GetPermissionsByBackendRoute(ctx context.Context, ba
return s.permissionRepo.GetByBackendRoute(ctx, backendRoute)
}
// GetPermissionsByFrontendRouteID 根据前端路由ID获取权限
func (s *PermissionService) GetPermissionsByFrontendRouteID(ctx context.Context, frontendRouteID uint) ([]*model.Permission, error) {
return s.permissionRepo.GetByFrontendRouteID(ctx, frontendRouteID)
// GetPermissionsByMenuID 根据菜单ID获取权限
func (s *PermissionService) GetPermissionsByMenuID(ctx context.Context, menuID uint) ([]*model.Permission, error) {
return s.permissionRepo.GetByMenuID(ctx, menuID)
}
// GetPermissionsByMenuGroupID 根据菜单分组ID获取权限
@ -44,4 +44,4 @@ func (s *PermissionService) GetPermissionsByMenuGroupID(ctx context.Context, men @@ -44,4 +44,4 @@ func (s *PermissionService) GetPermissionsByMenuGroupID(ctx context.Context, men
// GetUserPermissions 获取用户权限
func (s *PermissionService) GetUserPermissions(ctx context.Context, userID uint) ([]*model.Permission, error) {
return s.permissionRepo.GetByUserID(ctx, userID)
}
}

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

@ -8,25 +8,25 @@ import ( @@ -8,25 +8,25 @@ import (
// RouteSyncService 路由同步服务
type RouteSyncService struct {
frontendBackendRouteRepo *repository.FrontendBackendRouteRepository
log *zap.Logger
authResourcesRepo *repository.AuthResourcesRepository
log *zap.Logger
}
// NewRouteSyncService 创建路由同步服务实例
func NewRouteSyncService(
frontendBackendRouteRepo *repository.FrontendBackendRouteRepository,
authResourcesRepo *repository.AuthResourcesRepository,
log *zap.Logger,
) *RouteSyncService {
return &RouteSyncService{
frontendBackendRouteRepo: frontendBackendRouteRepo,
log: log,
authResourcesRepo: authResourcesRepo,
log: log,
}
}
// GetSyncStatus 获取同步状态
func (s *RouteSyncService) GetSyncStatus() (map[string]interface{}, error) {
// 获取数据库中的路由映射总数
totalMappings, err := s.frontendBackendRouteRepo.List()
totalMappings, err := s.authResourcesRepo.List()
if err != nil {
return nil, err
}

10
gofaster/backend/internal/shared/middleware/permission_middleware.go

@ -26,8 +26,8 @@ func PermissionMiddleware(db *gorm.DB, jwtSecret string) gin.HandlerFunc { @@ -26,8 +26,8 @@ func PermissionMiddleware(db *gorm.DB, jwtSecret string) gin.HandlerFunc {
path := c.Request.URL.Path
// 检查路由映射
frontendBackendRouteRepo := repository.NewFrontendBackendRouteRepository(db)
routeMappings, err := frontendBackendRouteRepo.FindByBackendRoute(path)
authResourcesRepo := repository.NewAuthResourcesRepository(db)
routeMappings, err := authResourcesRepo.FindByBackendRoute(path)
if err != nil || len(routeMappings) == 0 {
// 如果找不到路由映射,允许通过(可能是公开接口)
c.Next()
@ -46,7 +46,7 @@ func PermissionMiddleware(db *gorm.DB, jwtSecret string) gin.HandlerFunc { @@ -46,7 +46,7 @@ func PermissionMiddleware(db *gorm.DB, jwtSecret string) gin.HandlerFunc {
}
// checkUserPermission 检查用户权限
func checkUserPermission(db *gorm.DB, userID uint, routeMapping *model.FrontendBackendRoute) error {
func checkUserPermission(db *gorm.DB, userID uint, routeMapping *model.AuthResources) error {
// 这里实现三级权限检查逻辑
// 1. 菜单级别权限
// 2. 权限组级别权限
@ -62,8 +62,8 @@ func GetRouteAuthGroup(c *gin.Context, db *gorm.DB) string { @@ -62,8 +62,8 @@ func GetRouteAuthGroup(c *gin.Context, db *gorm.DB) string {
path := c.Request.URL.Path
method := c.Request.Method
frontendBackendRouteRepo := repository.NewFrontendBackendRouteRepository(db)
routeMappings, err := frontendBackendRouteRepo.FindByBackendRoute(path)
authResourcesRepo := repository.NewAuthResourcesRepository(db)
routeMappings, err := authResourcesRepo.FindByBackendRoute(path)
if err != nil || len(routeMappings) == 0 {
return "Unknown"
}

BIN
gofaster/backend/main.exe

Binary file not shown.

8
gofaster/backend/main.go

@ -33,7 +33,6 @@ import ( @@ -33,7 +33,6 @@ import (
// 导入各模块
_ "gofaster/internal/auth"
"gofaster/internal/auth/migration"
_ "gofaster/internal/workflow"
"github.com/gin-gonic/gin"
@ -75,11 +74,8 @@ func main() { @@ -75,11 +74,8 @@ func main() {
log.Fatal("Failed to connect database", zap.Error(err))
}
// 运行数据库迁移
if err := migration.RunMigrations(db); err != nil {
log.Fatal("Failed to run database migrations", zap.Error(err))
}
fmt.Printf("✅ 数据库迁移完成\n")
// 注意:数据库迁移已移除,因为这是全新的开发环境
fmt.Printf("✅ 数据库连接成功\n")
// 初始化Redis
redisClient := database.NewRedisClient(&cfg.Redis, log)

Loading…
Cancel
Save