forked from baskbuddy/zhangshun
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.3 KiB
5.3 KiB
CandidateFeedback 响应脱敏与契约修复设计
状态:已批准实施
目标:修复候选人反馈响应中的内部身份字段泄露,不改变既有接口语义。
1. 背景
candidate_feedback 模块已经提供企业创建/修改/删除反馈、企业和学生查看反馈的能力。当前服务层使用 Map<String, Object> 拼装响应,其中包含 companyUserId 和 studentUserId。这两个字段是内部身份标识,不属于前端页面所需的业务字段,也不应出现在学生或企业响应中。
本模块只做响应层脱敏,不修改数据库结构、请求格式、权限模型或业务流程。
2. 范围
2.1 本次实施
- 新增
CandidateFeedbackResponse白名单 DTO; POST/GET/PUT /applications/{applicationId}/feedback改为返回该 DTO;- 从响应中移除内部用户 ID 和其他敏感字段;
- 更新 Controller 测试、Service 测试和 OpenAPI response schema;
- 更新前端联调文档;
- 执行专项测试、全量测试和 OpenAPI 校验。
2.2 明确不做
- 不修改
candidate_feedback表; - 不新增 Flyway;
- 不修改请求 DTO;
- 不修改 URL、HTTP 方法、权限、错误码和状态机;
- 不把 CandidateFeedback 改造成结构化面试评分;
- 不修改评分引擎对
candidate_feedback的读取; - 不启动或停止 Docker;
- 不删除业务数据。
3. 现有接口契约
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /applications/{applicationId}/feedback |
岗位所属 COMPANY | 创建反馈,同一投递只能创建一次 |
| GET | /applications/{applicationId}/feedback |
岗位所属 COMPANY 或投递本人 STUDENT | 查询反馈 |
| PUT | /applications/{applicationId}/feedback |
岗位所属 COMPANY | 修改反馈 |
| DELETE | /applications/{applicationId}/feedback |
岗位所属 COMPANY | 删除反馈 |
身份始终从 JWT/SecurityContext 获取,不接受 userId、studentUserId 或 companyUserId 请求参数。
4. 新响应 DTO
类名:com.mydreamjob.feedback.dto.CandidateFeedbackResponse
字段:
| 字段 | 类型 | 是否返回 | 说明 |
|---|---|---|---|
id |
Long | 是 | 反馈 ID |
applicationId |
Long | 是 | 投递 ID |
rating |
Integer | 是 | 1–5 分 |
content |
String | 是 | 反馈内容 |
tags |
List<String> | 是 | 标签数组,无标签时返回空数组 |
createdAt |
LocalDateTime | 否 | 创建时间,未生成时省略 |
updatedAt |
LocalDateTime | 否 | 更新时间,未生成时省略 |
响应示例:
{
"code": 200,
"message": "ok",
"data": {
"id": 1,
"applicationId": 31,
"rating": 5,
"content": "沟通良好",
"tags": ["沟通良好"],
"createdAt": "2026-08-25T10:00:00",
"updatedAt": "2026-08-25T10:00:00"
}
}
响应禁止出现:
companyUserId
studentUserId
phone
email
passwordHash
resumeUrl
DTO 使用 @JsonInclude(JsonInclude.Include.NON_NULL);tags 由服务层解析为数组,非法或空 JSON 返回空数组,不向客户端暴露原始存储内容。
5. 权限与错误码
| 场景 | HTTP | 业务码 |
|---|---|---|
| 企业正常创建/修改/删除 | 200 | 200 |
| 企业或本人学生正常查询 | 200 | 200 |
| 未登录 | 401 | 401 |
| 非岗位所属企业写入 | 403 | 2002 |
| 非本人学生查询 | 403 | 2002 |
| COMPANY/ADMIN 无企业归属或跨企业访问 | 403 | 2002 |
| 投递不存在 | 404 | 8002 |
| 反馈不存在 | 404 | 9104 |
| 重复创建 | 409 | 9103 |
| 参数校验失败 | 400 | 1001 |
本次不新增错误码,也不调整既有错误码映射。
6. 实现约束
CandidateFeedbackService的创建、查询、修改方法返回CandidateFeedbackResponse;- Service 内新增安全的
toResponse映射方法; - 不再使用包含内部 ID 的
Map<String, Object>作为接口响应; - Controller 使用
ApiResponse<CandidateFeedbackResponse>; - Entity 继续保留
companyUserId、studentUserId,因为它们是数据库和评分聚合所需字段; - 评分引擎和 Candidate Score Card 只读仓储,不受本次响应 DTO 变化影响。
7. 测试矩阵
Service 测试
- 创建成功时 DTO 含业务字段;
- DTO 不含内部用户 ID;
- 查询成功时 DTO 脱敏;
- 修改成功时 DTO 脱敏;
- 标签 JSON 正常解析;
- 标签为空或非法 JSON 时返回空数组;
- 企业、学生、其他学生、其他企业权限保持原语义。
MockMvc 测试
- POST/GET/PUT 返回 200;
- DELETE 原有行为不变;
- 响应中断言禁止字段不存在;
- 未登录返回 401;
- 参数非法返回 400;
- 越权访问返回 403;
- 反馈不存在返回 404;
- 重复创建返回 409。
回归测试
- Candidate Score Card 读取反馈摘要不受影响;
mvn test全量通过;mvn clean verify全量通过;python docs/verify_openapi.py通过;- OpenAPI 路径数量保持 107。
8. 完成标准
只有以下条件全部满足,才允许进入隐私设置模块:
- 代码编译通过;
- CandidateFeedback 专项测试通过;
- 全量测试通过;
- OpenAPI 静态校验通过;
- 响应脱敏断言通过;
- 既有权限和错误码回归通过;
- 未新增数据库迁移;
- 未修改已验收接口语义。