轻松聘AI开发
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

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. 实现约束

  1. CandidateFeedbackService 的创建、查询、修改方法返回 CandidateFeedbackResponse;
  2. Service 内新增安全的 toResponse 映射方法;
  3. 不再使用包含内部 ID 的 Map<String, Object> 作为接口响应;
  4. Controller 使用 ApiResponse<CandidateFeedbackResponse>;
  5. Entity 继续保留 companyUserId、studentUserId,因为它们是数据库和评分聚合所需字段;
  6. 评分引擎和 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. 完成标准

只有以下条件全部满足,才允许进入隐私设置模块:

  1. 代码编译通过;
  2. CandidateFeedback 专项测试通过;
  3. 全量测试通过;
  4. OpenAPI 静态校验通过;
  5. 响应脱敏断言通过;
  6. 既有权限和错误码回归通过;
  7. 未新增数据库迁移;
  8. 未修改已验收接口语义。