# 权限体系升级测试方案 **文档版本**: v1.0 **创建日期**: 2026-02-27 --- ## 一、测试目标 验证权限体系升级后的功能正确性、兼容性和性能,确保: 1. 新增的13个角色体系正常工作 2. 按钮级权限控制正确 3. 状态驱动权限正确 4. 数据范围权限正确 5. 三端(管理后台、员工端、业主端)权限检查正常 6. 现有业务功能不受影响 --- ## 二、测试范围 ### 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 测试项目数据 ```sql -- 测试项目 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:** ```java @Test @DisplayName("测试数据范围枚举包含PROJECT") void testDataScopeContainsProject() { Role.DataScope[] scopes = Role.DataScope.values(); assertTrue(List.of(scopes).contains(Role.DataScope.PROJECT)); } @Test @DisplayName("测试新角色模板存在") void testNewRoleTemplatesExist() { List 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 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:** ```java @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:** ```typescript 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 测试代码示例 ```java @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 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测试脚本示例 ```typescript // 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 缺陷报告模板 ```markdown ## 缺陷标题 [模块] 简短描述 ## 缺陷详情 - **发现时间**: YYYY-MM-DD HH:mm - **发现人**: - **测试环境**: - **测试端**: 管理后台/员工端/业主端 ## 复现步骤 1. 步骤1 2. 步骤2 3. 步骤3 ## 预期结果 描述预期行为 ## 实际结果 描述实际行为 ## 附件 - 截图 - 日志 - 其他 ## 影响范围 描述影响的功能和用户 ``` --- ## 九、测试报告 ### 9.1 测试报告模板 ```markdown # 权限体系升级测试报告 ## 一、测试概述 - **测试时间**: 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级别缺陷