feat(role): 新增平台角色管理功能增强

- 权限表增加 available_for_role_types 字段,支持标记权限可用角色类型
- 权限列表和权限树接口支持按 available_for_role_type 过滤
- 新增角色状态切换接口 PUT /api/admin/roles/:id/status
- 角色分配权限时验证权限的可用角色类型
- 完善数据库迁移脚本和单元测试
- 补充数据库迁移相关开发规范文档
This commit is contained in:
2026-01-14 12:15:57 +08:00
parent 9c399df6bc
commit 5556b1028c
22 changed files with 1474 additions and 87 deletions

View File

@@ -437,3 +437,143 @@ func TestPermissionAPI_GetTree(t *testing.T) {
assert.Equal(t, 0, result.Code)
})
}
// TestPermissionAPI_GetTreeByAvailableForRoleType 测试按角色类型过滤权限树 API
func TestPermissionAPI_GetTreeByRoleType(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
platformPerm := &model.Permission{
PermName: "平台权限",
PermCode: "platform:manage",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "1",
Status: constants.StatusEnabled,
}
env.db.Create(platformPerm)
customerPerm := &model.Permission{
PermName: "客户权限",
PermCode: "customer:manage",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "2",
Status: constants.StatusEnabled,
}
env.db.Create(customerPerm)
commonPerm := &model.Permission{
PermName: "通用权限",
PermCode: "common:view",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "1,2",
Status: constants.StatusEnabled,
}
env.db.Create(commonPerm)
t.Run("按角色类型过滤权限树-平台角色", func(t *testing.T) {
req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions/tree?available_for_role_type=%d", constants.RoleTypePlatform), nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
t.Run("按角色类型过滤权限树-客户角色", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions/tree?available_for_role_type=2", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
t.Run("按平台和角色类型过滤", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions/tree?platform=all&available_for_role_type=1", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
}
// TestPermissionAPI_FilterByAvailableForRoleType 测试按角色类型过滤权限
func TestPermissionAPI_FilterByAvailableForRoleTypes(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
platformPerm := &model.Permission{
PermName: "平台专用权限",
PermCode: "platform:only",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "1",
Status: constants.StatusEnabled,
}
env.db.Create(platformPerm)
customerPerm := &model.Permission{
PermName: "客户专用权限",
PermCode: "customer:only",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "2",
Status: constants.StatusEnabled,
}
env.db.Create(customerPerm)
commonPerm := &model.Permission{
PermName: "通用权限",
PermCode: "common:all",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "1,2",
Status: constants.StatusEnabled,
}
env.db.Create(commonPerm)
t.Run("过滤平台角色可用权限", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions?available_for_role_type=1", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
t.Run("按角色类型过滤权限树", func(t *testing.T) {
req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions/tree?available_for_role_type=%d", constants.RoleTypePlatform), nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
}

View File

@@ -517,3 +517,109 @@ func TestRoleAPI_RemovePermission(t *testing.T) {
assert.NotNil(t, rp.DeletedAt)
})
}
// TestRoleAPI_UpdateStatus 测试角色状态切换 API
func TestRoleAPI_UpdateStatus(t *testing.T) {
env := setupRoleTestEnv(t)
defer env.teardown()
// 添加测试中间件
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
// 创建测试角色
testRole := &model.Role{
RoleName: "状态切换测试角色",
RoleType: constants.RoleTypePlatform,
Status: constants.StatusEnabled,
}
env.db.Create(testRole)
t.Run("成功禁用角色", func(t *testing.T) {
reqBody := model.UpdateRoleStatusRequest{
Status: constants.StatusDisabled,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/admin/roles/%d/status", testRole.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
// 验证数据库中状态已更新
var updated model.Role
env.db.First(&updated, testRole.ID)
assert.Equal(t, constants.StatusDisabled, updated.Status)
})
t.Run("成功启用角色", func(t *testing.T) {
reqBody := model.UpdateRoleStatusRequest{
Status: constants.StatusEnabled,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/admin/roles/%d/status", testRole.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
// 验证数据库中状态已更新
var updated model.Role
env.db.First(&updated, testRole.ID)
assert.Equal(t, constants.StatusEnabled, updated.Status)
})
t.Run("角色不存在返回错误", func(t *testing.T) {
reqBody := model.UpdateRoleStatusRequest{
Status: constants.StatusEnabled,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", "/api/admin/roles/99999/status", bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := env.app.Test(req)
require.NoError(t, err)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, errors.CodeRoleNotFound, result.Code)
})
t.Run("无效状态值返回错误", func(t *testing.T) {
reqBody := map[string]interface{}{
"status": 99, // 无效状态
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/admin/roles/%d/status", testRole.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := env.app.Test(req)
require.NoError(t, err)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.NotEqual(t, 0, result.Code)
})
}