11 KiB
11 KiB
1# 复测报告 - 模块1.3:员工管理(V2)
复测日期:2026-01-09 测试人员:AI测试工程师 测试环境:Spring Boot 3.1.2 / MySQL 8.0 / JDK 17 原始测试报告:TestReport_1.3_UserManagement.md 版本:V2(补充深度测试)
一、复测概要
| 项目 | 数值 |
|---|---|
| 原始缺陷数 | 3 |
| 复测通过 | 2 |
| 复测失败 | 1 |
| 新发现问题 | 2 |
| 修复率 | 66.7% |
二、原始缺陷复测结果
| 缺陷编号 | 严重程度 | 描述 | 复测结果 |
|---|---|---|---|
| BUG-USER-001 | 中 | 手机号未做唯一性校验 | ✅ 通过 |
| BUG-USER-002 | 高 | 讲师未实现部门数据隔离 | ❌ 失败 |
| BUG-USER-003 | 严重 | 学员可访问员工管理接口 | ✅ 通过 |
三、深度测试发现的新问题
3.1 BUG-USER-004:编辑员工时无法修改状态(后端Bug,非前端问题)
| 属性 | 描述 |
|---|---|
| 缺陷编号 | BUG-USER-004 |
| 严重程度 | 高 |
| 优先级 | P1 |
| 缺陷类型 | 后端Bug |
| 缺陷描述 | 通过编辑员工接口(PUT /api/system/user)传入status字段,接口返回成功,但状态未被更新 |
| 重现步骤 | 1. 管理员登录 2. 前端编辑员工,状态选择"禁用" 3. 点击保存,提示"保存成功" 4. 刷新页面,状态仍为"启用" |
| 预期结果 | 员工状态变为"禁用" |
| 实际结果 | 员工状态仍为"启用",状态修改被静默忽略 |
| 根因分析 | 后端问题:UserDTO类中没有定义status字段,updateUser()方法也未处理状态更新 |
| 影响范围 | 前端编辑表单中的状态选择功能完全无效,用户体验严重受损 |
前端代码验证 ✅
前端代码正确传递了status字段(user.html:139):
const data = {
realName: ...,
phone: ...,
username: ...,
departmentId: ...,
groupId: ...,
role: ...,
status: document.getElementById('formStatus').value // ✅ 前端正确传递
};
问题定位
| 层级 | 状态 | 说明 |
|---|---|---|
| 前端页面 | ✅ 正常 | 有状态选择下拉框,正确绑定值 |
| 前端JS | ✅ 正常 | saveData()正确收集status并发送 |
| 后端DTO | ❌ 缺失 | UserDTO没有status字段,Jackson忽略该值 |
| 后端Service | ❌ 未处理 | updateUser()没有setStatus()调用 |
测试请求:
PUT /api/system/user
Content-Type: application/json
{
"id": 5,
"username": "test_user_002",
"realName": "TestUser002",
"phone": "13800138001",
"role": "STUDENT",
"departmentId": 1,
"status": "DISABLED" // 尝试修改状态
}
测试响应:
{
"code": 200,
"message": "操作成功",
"success": true
}
验证查询结果:
{
"data": {
"id": 5,
"status": "ENABLED", // 状态未改变!
"statusName": "启用"
}
}
修复建议(二选一):
方案A:在UserDTO中添加status字段
// UserDTO.java
private String status;
// UserServiceImpl.updateUser()
if (StrUtil.isNotBlank(dto.getStatus())) {
user.setStatus(UserStatus.valueOf(dto.getStatus()));
}
方案B:明确API设计,状态修改只能通过专用接口
- 保持现有设计,状态只能通过
/enable和/disable接口修改 - 但需在API文档中明确说明
- 前端编辑表单应隐藏或禁用状态选项
3.2 BUG-USER-002 根因深入分析
问题: 讲师部门数据隔离未生效
代码追踪:
- Service层代码正确(UserServiceImpl.java:397-406)
private void applyDepartmentIsolation(LambdaQueryWrapper<User> queryWrapper) {
UserContext currentUser = UserContextHolder.getContext();
if (currentUser != null && UserRole.LECTURER.name().equals(currentUser.getRole())) {
if (currentUser.getDepartmentId() != null) { // 问题:departmentId为null
queryWrapper.eq(User::getDepartmentId, currentUser.getDepartmentId());
}
}
}
- 问题根源(JwtUtils.java:37-48)
public String generateToken(Long userId, String username, String role) {
return JWT.create()
.withSubject(String.valueOf(userId))
.withClaim("username", username)
.withClaim("role", role) // 只有这三个字段!
.withIssuedAt(now)
.withExpiresAt(expireDate)
.sign(Algorithm.HMAC256(secret));
}
// 没有包含 departmentId!
- AuthInterceptor尝试获取但失败(AuthInterceptor.java:68-70)
if (jwt.getClaim("departmentId") != null && !jwt.getClaim("departmentId").isNull()) {
context.setDepartmentId(jwt.getClaim("departmentId").asLong());
}
// 由于JWT中没有departmentId,这段代码不会执行
修复建议:
修改 JwtUtils.generateToken() 方法,添加 departmentId 参数:
public String generateToken(Long userId, String username, String role, Long departmentId) {
return JWT.create()
.withSubject(String.valueOf(userId))
.withClaim("username", username)
.withClaim("role", role)
.withClaim("departmentId", departmentId) // 新增
.withIssuedAt(now)
.withExpiresAt(expireDate)
.sign(Algorithm.HMAC256(secret));
}
同时修改 AuthServiceImpl.login() 方法,传入 departmentId。
四、重置密码功能验证 ✅
| 属性 | 描述 |
|---|---|
| 测试结果 | 通过 |
| 测试步骤 | 1. 管理员调用重置密码接口 2. 使用新密码登录 3. 使用旧密码登录 |
| 实际结果 | 新密码登录成功,旧密码登录失败(返回1003密码错误) |
测试详情:
# 重置密码
PUT /api/system/user/password/reset
{"userId": 5, "newPassword": "NewPass@123"}
Response: {"code": 200, "message": "操作成功"}
# 新密码登录
POST /api/auth/login
{"username": "test_user_002", "password": "NewPass@123"}
Response: {"code": 200, "data": {"token": "..."}} ✅
# 旧密码登录(验证旧密码失效)
POST /api/auth/login
{"username": "test_user_002", "password": "Test@123"}
Response: {"code": 1003, "message": "密码错误"} ✅
五、禁用/启用功能验证 ✅
| 属性 | 描述 |
|---|---|
| 测试结果 | 通过(仅限专用接口) |
| 测试步骤 | 1. 调用 PUT /api/system/user/{id}/disable 2. 查询用户状态 3. 调用 PUT /api/system/user/{id}/enable 4. 查询用户状态 |
| 实际结果 | 专用接口可正常切换状态 |
测试详情:
# 禁用用户
PUT /api/system/user/5/disable
Response: {"code": 200, "message": "操作成功"}
# 验证状态
GET /api/system/user/5
Response: {"data": {"status": "DISABLED", "statusName": "禁用"}} ✅
# 启用用户
PUT /api/system/user/5/enable
Response: {"code": 200, "message": "操作成功"}
# 验证状态
GET /api/system/user/5
Response: {"data": {"status": "ENABLED", "statusName": "启用"}} ✅
六、漏测原因分析
6.1 为什么会漏测"编辑状态"功能?
| 原因类型 | 具体分析 |
|---|---|
| 测试用例设计不完整 | 原测试计划只验证了"启用/禁用"功能是否存在,但没有验证"通过编辑接口修改状态"的场景 |
| 测试路径覆盖不足 | 系统提供了两种修改状态的路径:1) 专用接口 2) 编辑接口。只测试了路径1 |
| 接口返回值未深入验证 | 编辑接口返回200成功,测试人员信任了返回值而没有二次查询确认 |
| 前后端交互场景遗漏 | 没有模拟前端表单的实际交互流程,即"编辑时同时修改状态" |
6.2 为什么会漏测"重置密码验证"?
| 原因类型 | 具体分析 |
|---|---|
| 验证不完整 | 原测试只验证了接口返回200,没有验证密码是否真的被修改 |
| 缺少端到端验证 | 应该用新密码登录验证,同时用旧密码登录确认失效 |
6.3 为什么BUG-USER-002未修复但代码似乎已添加?
| 原因类型 | 具体分析 |
|---|---|
| 修复不完整 | 开发在Service层添加了部门隔离逻辑,但忽略了JWT中缺少departmentId |
| 缺少集成测试 | 单元测试可能通过(因为可以mock UserContext),但集成测试会失败 |
| 代码审查疏漏 | JWT Token生成和解析的链路未被完整审查 |
6.4 改进建议
-
测试用例设计改进
- 对于每个字段修改,都要验证"修改前后值确实变化"
- 对于多路径功能,所有路径都要测试
-
验证方法改进
- 不要只信任接口返回值,要二次查询确认
- 对于认证类功能,要进行端到端验证
-
增加测试场景
- 正常场景 + 边界场景 + 异常场景
- 单接口测试 + 流程测试 + 集成测试
七、缺陷汇总
7.1 现存缺陷清单
| 缺陷编号 | 严重程度 | 状态 | 描述 | 修复建议位置 |
|---|---|---|---|---|
| BUG-USER-002 | 高 | 待修复 | 讲师部门数据隔离未生效 | JwtUtils.java + AuthServiceImpl.java |
| BUG-USER-004 | 中 | 新发现 | 编辑接口无法修改状态 | UserDTO.java + UserServiceImpl.java |
7.2 已修复缺陷
| 缺陷编号 | 严重程度 | 描述 |
|---|---|---|
| BUG-USER-001 | 中 | 手机号唯一性校验 ✅ |
| BUG-USER-003 | 严重 | 学员权限控制 ✅ |
八、测试结论
8.1 模块状态评估
| 功能项 | 状态 | 说明 |
|---|---|---|
| 创建员工 | ✅ 可用 | |
| 编辑员工(基本信息) | ✅ 可用 | |
| 编辑员工(状态) | ⚠️ 部分可用 | 只能通过专用接口修改 |
| 删除员工 | ✅ 可用 | |
| 重置密码 | ✅ 可用 | |
| 启用/禁用(专用接口) | ✅ 可用 | |
| 手机号唯一性 | ✅ 已实现 | |
| 学员权限控制 | ✅ 已实现 | |
| 讲师数据隔离 | ❌ 未实现 | JWT缺少departmentId |
8.2 上线评估
结论:建议暂缓上线
原因:
- BUG-USER-002(讲师数据隔离)为高优先级安全问题,存在信息泄露风险
- 虽然基础CRUD功能可用,但不符合PRD中的数据隔离要求
8.3 修复优先级建议
| 优先级 | 缺陷编号 | 预计影响 |
|---|---|---|
| P0 | BUG-USER-002 | 修复后满足PRD数据隔离要求 |
| P2 | BUG-USER-004 | 改善用户体验,非阻塞性问题 |
九、附录
9.1 测试账号
| 账号 | 角色 | 部门 |
|---|---|---|
| admin | ADMIN | 救援二部 |
| test_lecturer | LECTURER | 救援一部 |
| test_user_001 | STUDENT | 救援一部 |
| test_user_002 | STUDENT | 救援一部 |
9.2 相关代码文件
| 文件 | 说明 |
|---|---|
| UserController.java | 用户管理控制器 |
| UserServiceImpl.java | 用户服务实现,包含部门隔离逻辑 |
| UserDTO.java | 用户数据传输对象(缺少status字段) |
| JwtUtils.java | JWT工具类(缺少departmentId) |
| AuthInterceptor.java | 认证拦截器 |
9.3 相关文档
| 文档 | 路径 |
|---|---|
| 原始测试报告 | docs/test-reports/TestReport_1.3_UserManagement.md |
| 第一版复测报告 | docs/test-reports/RegressionReport_1.3_UserManagement.md |
| 产品需求文档 | docs/PRD.md |
报告生成时间: 2026-01-09 16:45:00
报告签发: AI测试工程师