# 权限体系升级测试方案

**文档版本**: 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<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:**

```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. 用户登录<br>2. 获取用户权限<br>3. 验证权限列表 | 返回正确的权限列表 |
| INT-002 | 角色权限分配 | 1. 创建角色<br>2. 分配权限<br>3. 验证角色权限 | 权限正确关联 |
| INT-003 | 数据权限过滤 | 1. 设置用户数据范围<br>2. 查询数据列表<br>3. 验证过滤结果 | 数据正确过滤 |
| INT-004 | 状态驱动权限 | 1. 创建工单<br>2. 检查可执行操作<br>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<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. 打开登录页<br>2. 输入超级管理员账号<br>3. 点击登录 | 登录成功，显示所有菜单 | P0 |
| E2E-ADMIN-002 | 项目经理登录 | 1. 打开登录页<br>2. 输入项目经理账号<br>3. 点击登录 | 登录成功，显示项目管理菜单 | P0 |
| E2E-ADMIN-003 | 工程主管工单管理 | 1. 登录工程主管账号<br>2. 进入工单管理<br>3. 检查可用按钮 | 显示分配、审核按钮 | P0 |
| E2E-ADMIN-004 | 维修人员工单执行 | 1. 登录维修人员账号<br>2. 进入工单列表<br>3. 检查可用按钮 | 显示接单、开始、完成按钮 | P0 |
| E2E-ADMIN-005 | 工单状态驱动按钮 | 1. 创建工单<br>2. 检查按钮状态<br>3. 分配工单<br>4. 再次检查按钮 | 按钮根据状态变化 | P0 |
| E2E-ADMIN-006 | 数据权限过滤 | 1. 登录部门主管账号<br>2. 查看工单列表<br>3. 验证数据范围 | 仅显示本部门数据 | P1 |

#### 6.1.2 员工端测试用例

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| E2E-EMP-001 | 维修人员移动端登录 | 1. 打开员工端<br>2. 输入维修人员账号<br>3. 点击登录 | 登录成功，显示工单列表 | P0 |
| E2E-EMP-002 | 工单接单流程 | 1. 查看待接单工单<br>2. 点击接单<br>3. 验证状态变化 | 工单状态变为已接单 | P0 |
| E2E-EMP-003 | 工单执行流程 | 1. 点击开始处理<br>2. 填写处理结果<br>3. 点击完成 | 工单状态变为已完成 | P0 |
| E2E-EMP-004 | 安保人员巡检 | 1. 登录安保人员账号<br>2. 进入巡检任务<br>3. 执行巡检 | 可扫码签到、上报异常 | P1 |
| E2E-EMP-005 | 保洁人员任务 | 1. 登录保洁人员账号<br>2. 查看任务列表<br>3. 执行任务 | 显示分配给自己的任务 | P1 |

#### 6.1.3 业主端测试用例

| 用例ID | 测试场景 | 测试步骤 | 预期结果 | 优先级 |
|--------|---------|---------|---------|--------|
| E2E-OWNER-001 | 业主登录 | 1. 打开业主端<br>2. 输入业主账号<br>3. 点击登录 | 登录成功，显示首页 | P0 |
| E2E-OWNER-002 | 在线报修 | 1. 点击报修<br>2. 填写报修信息<br>3. 提交 | 报修工单创建成功 | P0 |
| E2E-OWNER-003 | 查看账单 | 1. 点击账单<br>2. 查看账单列表<br>3. 验证数据 | 显示本人房屋账单 | P1 |
| E2E-OWNER-004 | 在线缴费 | 1. 选择账单<br>2. 点击缴费<br>3. 完成支付 | 缴费成功 | P1 |
| E2E-OWNER-005 | 访客邀请 | 1. 点击访客邀请<br>2. 填写访客信息<br>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级别缺陷
