20 KiB
20 KiB
权限体系升级测试方案
文档版本: v1.0
创建日期: 2026-02-27
一、测试目标
验证权限体系升级后的功能正确性、兼容性和性能,确保:
- 新增的13个角色体系正常工作
- 按钮级权限控制正确
- 状态驱动权限正确
- 数据范围权限正确
- 三端(管理后台、员工端、业主端)权限检查正常
- 现有业务功能不受影响
二、测试范围
2.1 测试端覆盖
| 端 | 端口 | 测试重点 |
|---|---|---|
| 管理后台 | 5175 | 完整权限管理、角色分配、权限检查 |
| 员工端 | 5174 | 执行层权限、移动端适配 |
| 业主端 | 5176 | 业主权限、房屋绑定权限 |
2.2 测试模块覆盖
| 模块 | 测试内容 |
|---|---|
| 用户认证 | 登录、登出、Token刷新 |
| 角色管理 | 角色创建、角色分配、角色权限 |
| 权限检查 | 菜单权限、按钮权限、API权限 |
| 数据权限 | ALL/PROJECT/DEPARTMENT/SELF |
| 状态驱动 | 工单状态-操作映射 |
三、测试策略
3.1 测试层次
┌─────────────────────────────────────────────────────────────────┐
│ E2E 测试 │
│ (Puppeteer 自动化测试,覆盖三端核心业务流程) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 集成测试 │
│ (Spring Boot Test,验证服务间协作和数据库交互) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 单元测试 │
│ (JUnit 5 + Vitest,验证单个方法和组件的正确性) │
└─────────────────────────────────────────────────────────────────┘
3.2 测试数据准备
3.2.1 测试账号
| 角色 | 用户名 | 密码 | 数据范围 | 用途 |
|---|---|---|---|---|
| 超级管理员 | super_admin | Test@123 | ALL | 全权限测试 |
| 系统管理员 | sys_admin | Test@123 | ALL | 系统配置测试 |
| 物业经理 | property_manager | Test@123 | PROJECT | 多项目管理测试 |
| 项目经理 | project_manager | Test@123 | PROJECT | 单项目管理测试 |
| 工程主管 | engineering_lead | Test@123 | DEPARTMENT | 工单管理测试 |
| 安保主管 | security_lead | Test@123 | DEPARTMENT | 安保管理测试 |
| 保洁主管 | cleaning_lead | Test@123 | DEPARTMENT | 保洁管理测试 |
| 财务主管 | finance_lead | Test@123 | DEPARTMENT | 财务管理测试 |
| 维修人员 | maintenance_staff | Test@123 | SELF | 工单执行测试 |
| 安保人员 | security_staff | Test@123 | SELF | 巡检验证测试 |
| 保洁人员 | cleaning_staff | Test@123 | SELF | 保洁任务测试 |
| 客服人员 | cs_staff | Test@123 | PROJECT | 服务支持测试 |
| 业主 | owner | Test@123 | SELF | 业主功能测试 |
3.2.2 测试项目数据
-- 测试项目
INSERT INTO mdm_project (id, name, code, status) VALUES
('test-project-001', '测试小区A', 'TEST-A', 'ACTIVE'),
('test-project-002', '测试小区B', 'TEST-B', 'ACTIVE');
-- 测试楼栋
INSERT INTO mdm_space_node (id, project_id, name, node_type, parent_id) VALUES
('building-001', 'test-project-001', '1号楼', 'BUILDING', NULL),
('building-002', 'test-project-001', '2号楼', 'BUILDING', NULL);
-- 测试房间
INSERT INTO mdm_space_node (id, project_id, name, node_type, parent_id) VALUES
('room-101', 'test-project-001', '101', 'ROOM', 'building-001'),
('room-102', 'test-project-001', '102', 'ROOM', 'building-001');
四、单元测试方案
4.1 后端单元测试
4.1.1 测试文件
| 测试类 | 测试内容 |
|---|---|
| PermissionServiceV3Test | 权限服务核心方法 |
| RoleServiceTest | 角色管理方法 |
| DataScopeHelperTest | 数据权限工具类 |
| WorkOrderStatusTest | 工单状态-操作映射 |
4.1.2 测试用例
PermissionServiceV3Test:
@Test
@DisplayName("测试数据范围枚举包含PROJECT")
void testDataScopeContainsProject() {
Role.DataScope[] scopes = Role.DataScope.values();
assertTrue(List.of(scopes).contains(Role.DataScope.PROJECT));
}
@Test
@DisplayName("测试新角色模板存在")
void testNewRoleTemplatesExist() {
List<String> expectedRoles = List.of(
"SYS_ADMIN", "ENGINEERING_LEAD", "SECURITY_LEAD",
"CLEANING_LEAD", "FINANCE_LEAD", "CLEANING_STAFF"
);
for (String roleCode : expectedRoles) {
assertTrue(roleRepository.findByRoleCode(roleCode).isPresent());
}
}
@Test
@DisplayName("测试按钮级权限编码存在")
void testButtonPermissionsExist() {
List<String> expectedPermissions = List.of(
"ops:work_order:assign",
"ops:work_order:accept",
"ops:work_order:complete"
);
for (String permCode : expectedPermissions) {
assertTrue(permissionRepository.findByPermissionCode(permCode).isPresent());
}
}
@Test
@DisplayName("测试状态驱动权限-CREATED状态")
void testStateDrivenPermission_Created() {
WorkOrderStatus status = WorkOrderStatus.CREATED;
assertTrue(status.isActionAllowed(WorkOrderAction.ASSIGN));
assertTrue(status.isActionAllowed(WorkOrderAction.EDIT));
assertTrue(status.isActionAllowed(WorkOrderAction.DELETE));
assertFalse(status.isActionAllowed(WorkOrderAction.ACCEPT));
}
@Test
@DisplayName("测试状态驱动权限-CLOSED状态")
void testStateDrivenPermission_Closed() {
WorkOrderStatus status = WorkOrderStatus.CLOSED;
assertTrue(status.getAllowedActions().isEmpty());
}
DataScopeHelperTest:
@Test
@DisplayName("测试ALL数据范围")
void testDataScope_All() {
DataScopeContext.setDataScopeInfo(new DataScopeInfo("ALL", userId, projectId, deptId));
assertTrue(DataScopeHelper.canAccess(anyProjectId, anyDeptId, anyCreatorId));
}
@Test
@DisplayName("测试PROJECT数据范围")
void testDataScope_Project() {
DataScopeContext.setDataScopeInfo(new DataScopeInfo("PROJECT", userId, projectId, deptId));
assertTrue(DataScopeHelper.canAccess(projectId, anyDeptId, anyCreatorId));
assertFalse(DataScopeHelper.canAccess(otherProjectId, anyDeptId, anyCreatorId));
}
@Test
@DisplayName("测试DEPARTMENT数据范围")
void testDataScope_Department() {
DataScopeContext.setDataScopeInfo(new DataScopeInfo("DEPARTMENT", userId, projectId, deptId));
assertTrue(DataScopeHelper.canAccess(projectId, deptId, anyCreatorId));
assertFalse(DataScopeHelper.canAccess(projectId, otherDeptId, anyCreatorId));
}
@Test
@DisplayName("测试SELF数据范围")
void testDataScope_Self() {
DataScopeContext.setDataScopeInfo(new DataScopeInfo("SELF", userId, projectId, deptId));
assertTrue(DataScopeHelper.canAccess(projectId, deptId, userId));
assertFalse(DataScopeHelper.canAccess(projectId, deptId, otherUserId));
}
4.2 前端单元测试
4.2.1 测试文件
| 测试文件 | 测试内容 |
|---|---|
| permissionStore.test.ts | 权限Store方法 |
| permissionDirective.test.ts | 权限指令 |
4.2.2 测试用例
permissionStore.test.ts:
describe('Permission Store V3', () => {
describe('hasPermissionCode', () => {
it('should return true when permission exists', () => {
const store = usePermissionStore()
store.permissions = [
{ code: 'ops:work_order:view', name: '查看工单', type: 'BUTTON' }
]
expect(store.hasPermissionCode('ops:work_order:view')).toBe(true)
})
it('should return false when permission not exists', () => {
const store = usePermissionStore()
store.permissions = []
expect(store.hasPermissionCode('ops:work_order:view')).toBe(false)
})
})
describe('hasAnyPermission', () => {
it('should return true when any permission exists', () => {
const store = usePermissionStore()
store.permissions = [
{ code: 'ops:work_order:view', name: '查看工单', type: 'BUTTON' }
]
expect(store.hasAnyPermission(['ops:work_order:view', 'ops:work_order:edit'])).toBe(true)
})
it('should return false when no permission exists', () => {
const store = usePermissionStore()
store.permissions = []
expect(store.hasAnyPermission(['ops:work_order:view', 'ops:work_order:edit'])).toBe(false)
})
})
describe('checkActionPermission', () => {
it('should call API with correct parameters', async () => {
const store = usePermissionStore()
const result = await store.checkActionPermission({
permission: 'ops:work_order:assign',
status: 'CREATED',
action: 'ASSIGN',
entityType: 'work_order'
})
expect(typeof result).toBe('boolean')
})
})
})
五、集成测试方案
5.1 测试场景
| 场景ID | 测试场景 | 测试步骤 | 预期结果 |
|---|---|---|---|
| INT-001 | 用户登录获取权限 | 1. 用户登录 2. 获取用户权限 3. 验证权限列表 |
返回正确的权限列表 |
| INT-002 | 角色权限分配 | 1. 创建角色 2. 分配权限 3. 验证角色权限 |
权限正确关联 |
| INT-003 | 数据权限过滤 | 1. 设置用户数据范围 2. 查询数据列表 3. 验证过滤结果 |
数据正确过滤 |
| INT-004 | 状态驱动权限 | 1. 创建工单 2. 检查可执行操作 3. 执行操作 |
操作正确执行 |
5.2 测试代码示例
@SpringBootTest
@Transactional
class PermissionIntegrationTest {
@Autowired
private PermissionService permissionService;
@Autowired
private WorkOrderService workOrderService;
@Test
@DisplayName("集成测试:用户登录获取权限")
void testUserLoginAndGetPermissions() {
// 1. 用户登录
LoginResponse response = authService.login("project_manager", "Test@123");
assertNotNull(response.getAccessToken());
// 2. 获取用户权限
UserPermissionsResponse permissions = permissionService.getUserPermissions(response.getUserId());
assertNotNull(permissions);
assertFalse(permissions.getPermissions().isEmpty());
// 3. 验证权限列表包含预期权限
assertTrue(permissions.getPermissions().stream()
.anyMatch(p -> p.getPermissionCode().startsWith("ops:work_order")));
}
@Test
@DisplayName("集成测试:状态驱动权限")
void testStateDrivenPermission() {
// 1. 创建工单
WorkOrder workOrder = workOrderService.create(createRequest);
assertEquals(WorkOrderStatus.CREATED, workOrder.getStatus());
// 2. 检查可执行操作
Set<String> actions = permissionService.getAllowedActions(
userId, projectId, "work_order", "CREATED");
assertTrue(actions.contains("ASSIGN"));
assertFalse(actions.contains("ACCEPT"));
// 3. 分配工单
workOrderService.assign(workOrder.getId(), assigneeId);
assertEquals(WorkOrderStatus.ASSIGNED, workOrder.getStatus());
}
}
六、E2E测试方案
6.1 测试用例清单
6.1.1 管理后台测试用例
| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|---|---|---|---|---|
| E2E-ADMIN-001 | 超级管理员登录 | 1. 打开登录页 2. 输入超级管理员账号 3. 点击登录 |
登录成功,显示所有菜单 | P0 |
| E2E-ADMIN-002 | 项目经理登录 | 1. 打开登录页 2. 输入项目经理账号 3. 点击登录 |
登录成功,显示项目管理菜单 | P0 |
| E2E-ADMIN-003 | 工程主管工单管理 | 1. 登录工程主管账号 2. 进入工单管理 3. 检查可用按钮 |
显示分配、审核按钮 | P0 |
| E2E-ADMIN-004 | 维修人员工单执行 | 1. 登录维修人员账号 2. 进入工单列表 3. 检查可用按钮 |
显示接单、开始、完成按钮 | P0 |
| E2E-ADMIN-005 | 工单状态驱动按钮 | 1. 创建工单 2. 检查按钮状态 3. 分配工单 4. 再次检查按钮 |
按钮根据状态变化 | P0 |
| E2E-ADMIN-006 | 数据权限过滤 | 1. 登录部门主管账号 2. 查看工单列表 3. 验证数据范围 |
仅显示本部门数据 | P1 |
6.1.2 员工端测试用例
| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|---|---|---|---|---|
| E2E-EMP-001 | 维修人员移动端登录 | 1. 打开员工端 2. 输入维修人员账号 3. 点击登录 |
登录成功,显示工单列表 | P0 |
| E2E-EMP-002 | 工单接单流程 | 1. 查看待接单工单 2. 点击接单 3. 验证状态变化 |
工单状态变为已接单 | P0 |
| E2E-EMP-003 | 工单执行流程 | 1. 点击开始处理 2. 填写处理结果 3. 点击完成 |
工单状态变为已完成 | P0 |
| E2E-EMP-004 | 安保人员巡检 | 1. 登录安保人员账号 2. 进入巡检任务 3. 执行巡检 |
可扫码签到、上报异常 | P1 |
| E2E-EMP-005 | 保洁人员任务 | 1. 登录保洁人员账号 2. 查看任务列表 3. 执行任务 |
显示分配给自己的任务 | P1 |
6.1.3 业主端测试用例
| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|---|---|---|---|---|
| E2E-OWNER-001 | 业主登录 | 1. 打开业主端 2. 输入业主账号 3. 点击登录 |
登录成功,显示首页 | P0 |
| E2E-OWNER-002 | 在线报修 | 1. 点击报修 2. 填写报修信息 3. 提交 |
报修工单创建成功 | P0 |
| E2E-OWNER-003 | 查看账单 | 1. 点击账单 2. 查看账单列表 3. 验证数据 |
显示本人房屋账单 | P1 |
| E2E-OWNER-004 | 在线缴费 | 1. 选择账单 2. 点击缴费 3. 完成支付 |
缴费成功 | P1 |
| E2E-OWNER-005 | 访客邀请 | 1. 点击访客邀请 2. 填写访客信息 3. 提交 |
访客凭证生成成功 | P2 |
6.2 E2E测试脚本示例
// e2e/permission-workflow.spec.ts
import { test, expect } from '@playwright/test'
test.describe('权限工作流测试', () => {
test('E2E-ADMIN-005: 工单状态驱动按钮', async ({ page }) => {
// 1. 登录工程主管账号
await page.goto('http://localhost:5175/login')
await page.fill('[name="username"]', 'engineering_lead')
await page.fill('[name="password"]', 'Test@123')
await page.click('button[type="submit"]')
await page.waitForURL('**/dashboard')
// 2. 进入工单管理
await page.click('text=工单管理')
await page.click('text=工单列表')
// 3. 创建新工单
await page.click('button:has-text("新增工单")')
await page.fill('[name="title"]', '测试工单')
await page.fill('[name="description"]', '测试描述')
await page.click('button:has-text("提交")')
// 4. 验证CREATED状态下的按钮
const assignButton = page.locator('button:has-text("分配")')
const acceptButton = page.locator('button:has-text("接单")')
await expect(assignButton).toBeVisible()
await expect(acceptButton).not.toBeVisible()
// 5. 分配工单
await assignButton.click()
await page.selectOption('[name="assignee"]', 'maintenance_staff')
await page.click('button:has-text("确认")')
// 6. 验证ASSIGNED状态下的按钮
await expect(assignButton).not.toBeVisible()
await expect(acceptButton).toBeVisible()
})
test('E2E-EMP-002: 维修人员工单接单流程', async ({ page }) => {
// 1. 登录维修人员账号
await page.goto('http://localhost:5174/login')
await page.fill('[name="username"]', 'maintenance_staff')
await page.fill('[name="password"]', 'Test@123')
await page.click('button[type="submit"]')
await page.waitForURL('**/home')
// 2. 查看待接单工单
await page.click('text=待接单')
// 3. 接单
await page.click('button:has-text("接单")')
// 4. 验证状态变化
await expect(page.locator('text=已接单')).toBeVisible()
// 5. 开始处理
await page.click('button:has-text("开始处理")')
await expect(page.locator('text=处理中')).toBeVisible()
})
})
七、测试执行计划
7.1 执行顺序
Day 1: 单元测试
├── 后端单元测试 (2小时)
│ ├── PermissionServiceV3Test
│ ├── RoleServiceTest
│ ├── DataScopeHelperTest
│ └── WorkOrderStatusTest
└── 前端单元测试 (2小时)
├── permissionStore.test.ts
└── permissionDirective.test.ts
Day 2: 集成测试
├── 用户认证集成测试 (1小时)
├── 角色权限集成测试 (1小时)
├── 数据权限集成测试 (1小时)
└── 状态驱动权限集成测试 (1小时)
Day 3: E2E测试
├── 管理后台E2E测试 (2小时)
├── 员工端E2E测试 (1小时)
└── 业主端E2E测试 (1小时)
Day 4: 回归测试
├── 全量回归测试 (3小时)
└── 缺陷修复验证 (1小时)
7.2 测试环境
| 环境 | 用途 | 配置 |
|---|---|---|
| 开发环境 | 单元测试、集成测试 | 本地开发机器 |
| 测试环境 | E2E测试 | 独立测试服务器 |
| 预发布环境 | 回归测试 | 类生产环境 |
八、缺陷管理
8.1 缺陷等级定义
| 等级 | 定义 | 处理时限 |
|---|---|---|
| P0 | 核心功能不可用,阻塞测试 | 立即修复 |
| P1 | 主要功能异常,影响业务流程 | 24小时内 |
| P2 | 次要功能异常,不影响主流程 | 48小时内 |
| P3 | UI/体验问题 | 下版本修复 |
8.2 缺陷报告模板
## 缺陷标题
[模块] 简短描述
## 缺陷详情
- **发现时间**: YYYY-MM-DD HH:mm
- **发现人**:
- **测试环境**:
- **测试端**: 管理后台/员工端/业主端
## 复现步骤
1. 步骤1
2. 步骤2
3. 步骤3
## 预期结果
描述预期行为
## 实际结果
描述实际行为
## 附件
- 截图
- 日志
- 其他
## 影响范围
描述影响的功能和用户
九、测试报告
9.1 测试报告模板
# 权限体系升级测试报告
## 一、测试概述
- **测试时间**: YYYY-MM-DD ~ YYYY-MM-DD
- **测试人员**:
- **测试环境**:
- **测试版本**: v3.0
## 二、测试执行情况
### 2.1 测试用例执行统计
| 测试类型 | 用例总数 | 执行数 | 通过数 | 失败数 | 通过率 |
|---------|---------|--------|--------|--------|--------|
| 单元测试 | | | | | |
| 集成测试 | | | | | |
| E2E测试 | | | | | |
| **总计** | | | | | |
### 2.2 缺陷统计
| 等级 | 发现数 | 已修复 | 待修复 | 遗留 |
|------|--------|--------|--------|------|
| P0 | | | | |
| P1 | | | | |
| P2 | | | | |
| P3 | | | | |
| **总计** | | | | |
## 三、测试结论
- **是否满足上线条件**: 是/否
- **遗留问题**:
- **建议**:
## 四、附录
- 测试用例详情
- 缺陷列表
- 测试截图
十、验收标准
10.1 功能验收标准
- 所有13个角色可正常创建和使用
- 所有40+按钮权限可正常检查
- 状态驱动权限正确工作
- 数据范围权限正确过滤
- 三端权限检查正常工作
10.2 兼容性验收标准
- 现有用户可正常登录
- 现有功能不受影响
- 现有数据完整保留
10.3 性能验收标准
- 权限检查响应时间 < 100ms
- 页面加载时间无明显增加
- 数据库查询性能正常
10.4 测试验收标准
- 单元测试覆盖率 > 80%
- 集成测试全部通过
- E2E测试全部通过
- 无P0/P1级别缺陷