轻松聘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.
 
 
 

8.1 KiB

统一求职进度聚合 API(V34)

版本:V34 设计锁定版 状态:已编码实现 适用范围:学生端只读接口,为「求职进度」页聚合投递、面试、结构化面试评分、Offer、同岗位竞争力排名与通知计数。

1. 定位与边界

为 STUDENT 提供「求职进度」页所需的单次聚合数据,避免前端串行调用多个接口。与既有接口边界:

接口 是否改动 关系
GET /jobs/{jobId}/competition-ranking 不改 复用同套候选人池/排序/百分位口径(共享 CompetitionRankingCalculator),但本接口只返回摘要,不返回 dimensions/gaps/topCandidates
GET /notifications 不改 本接口只返回通知计数,通知标题/正文/详情仍由 /notifications 提供
GET /interviews/{id}/score 等 不改 仅读取已提交(SUBMITTED)评分摘要,不返回 DRAFT、不返回 comment
GET /offers/... 不改 仅返回 Offer 摘要,不返回薪资/条款细节
GET /profile/me/...、投递、面试 不改 本接口为其只读聚合,不重写既有语义

2. 接口

GET /api/v1/profile/me/career-progress

Query 参数:

参数 类型 必填 默认 说明
page int 否 0 页码,从 0 开始,>= 0
size int 否 20 每页条数,1–50
status string 否 — 投递状态过滤,仅 SUBMITTED/VIEWED/INTERVIEW/HIRED/REJECTED/WITHDRAWN
  • 角色:仅 STUDENT。
  • 身份从 JWT 获取,不接收 userId/studentUserId/candidateId 等身份注入参数。
  • 新增 1 条 GET 路径,OpenAPI 125 → 126。

3. 权限矩阵

身份/场景 HTTP code 说明
未登录 401 2001 既有安全机制
COMPANY / ADMIN 403 2002 Service 层 assertStudentRole
STUDENT 正常 200 — 正常返回
page < 0 / size < 1 / size > 50 / 非法 status 400 1001 BAD_REQUEST

不新增错误码,复用既有 ErrorCode。

4. 响应结构

ApiResponse<CareerProgressResponse>

CareerProgressResponse {
  ProgressTodoSummary todo;
  PaginatedApplications applications;
  Instant generatedAt;
}

ProgressTodoSummary {
  long pendingInterviewResponseCount;    // 状态 SCHEDULED 的面试数量
  long upcomingConfirmedInterviewCount;  // 状态 CONFIRMED 且 startAt > 当前时间
  long pendingOfferResponseCount;        // 状态 SENT/NEGOTIATING 且 expireAt > 当前时间
  long unreadNotificationCount;          // 未读通知总数
  long unreadReportDecisionCount;        // 未读 REPORT_DECISION 通知数
}

PaginatedApplications {
  List<CareerProgressItem> items;
  long total;
  int  page;
  int  size;
  int  totalPages;
}

CareerProgressItem {
  Long    applicationId;
  String  applicationStatus;
  LocalDateTime appliedAt;
  LocalDateTime updatedAt;
  JobSummary job;
  InterviewSummary nextInterview;                    // 可空
  ScoreSummary latestSubmittedInterviewScore;        // 可空
  OfferSummary offer;                                // 可空
  CompetitionSummary competition;
}

JobSummary { Long jobId; String title; String companyName; String city; String employmentType; String jobStatus; }
InterviewSummary { Long interviewId; Integer roundNo; String title; String interviewType; LocalDateTime startAt; LocalDateTime endAt; String status; }
ScoreSummary { Long interviewId; BigDecimal overallScore; String recommendation; LocalDateTime submittedAt; }
OfferSummary { Long offerId; String status; String title; LocalDateTime expireAt; LocalDate onboardingDate; }
CompetitionSummary {
  boolean eligible;
  Integer rank;                 // 有分才返回;WITHDRAWN/REJECTED 或本人无分 → null
  Integer cohortSize;           // 仅 eligible=true 时返回
  BigDecimal overallScore;      // 本人无分 → null
  BigDecimal percentile;        // 有分者 < 2 → null
  String status;                // OK / PARTIAL / INSUFFICIENT_DATA / PENDING
  String mySnapshotPeriod;      // 本人实际使用快照月;无快照 → null
  String rulesVersion;
}

明确不返回字段

  • 不返回 studentUserId、candidateId、companyUserId、evaluatorUserId、phone、email、resumeUrl、passwordHash、JWT。
  • 不返回通知标题/正文(title/content)、举报详情、面试评分 comment、Offer 薪资/条款细节(salaryMin/salaryMax/baseSalary/housingAllowance/annualBonusMonths/benefits/description/otherAllowances)。
  • 不返回其他候选人的任何身份信息,也不返回 V32 的 dimensions、gaps、topCandidates。

5. 聚合口径

5.1 投递

  • 读取当前学生全部投递,按 createdAt DESC 分页。
  • status 存在时按该状态筛选。
  • 不过滤 WITHDRAWN/REJECTED/HIRED,均属于进度历史。

5.2 Todo

字段 口径
pendingInterviewResponseCount 当前学生 SCHEDULED 面试数量
upcomingConfirmedInterviewCount 当前学生 CONFIRMED 且 startAt > now 的面试数量
pendingOfferResponseCount 当前学生 SENT/NEGOTIATING 且 expireAt > now 的 Offer 数量
unreadNotificationCount 复用 NotificationRepository 未读总数
unreadReportDecisionCount 未读且 type='REPORT_DECISION' 的通知数量

5.3 nextInterview

  • 只看该投递的 SCHEDULED / CONFIRMED 面试,取 startAt 最早一场。
  • RESCHEDULE_REQUESTED 不算已确定;已完成/取消/拒绝不作为 nextInterview。

5.4 latestSubmittedInterviewScore

  • 只读 SUBMITTED 状态的 InterviewScore,每个 applicationId 取 submittedAt 最新一条。
  • 不返回 DRAFT、不返回 comment。

5.5 Offer

  • 仅学生本人 candidateId 的 Offer;DRAFT 永不返回。
  • 优先展示 SENT/NEGOTIATING 且 expireAt > now 的有效 Offer,按 expireAt ASC 取最早到期一条。
  • 无有效 Offer 时,展示该投递 createdAt 最新的一条非 DRAFT Offer。
  • 不返回薪资/条款细节。

5.6 competition

  • 投递状态 SUBMITTED/VIEWED/INTERVIEW/HIRED → eligible=true;WITHDRAWN/REJECTED → eligible=false(不返回其它排名字段)。
  • 复用 V32 候选池口径、最新快照口径、CandidateScoringCalculator#overallScoreFromSnapshots、稳定排序与百分位公式。
  • percentile = 严格低于本人分数的人数 / (有分候选人数 - 1) × 100,scale=2、HALF_UP;有分者 < 2 时 null。
  • 不返回 V32 的 dimensions/gaps/topCandidates。

6. 综合分 / 排序 / 百分位

  • 全部复用 CompetitionRankingCalculator(V32/V34 共享),禁止复制第二套公式、禁止 SQL 重算。
  • status 复用引擎语义:OK / PARTIAL / INSUFFICIENT_DATA / PENDING,不把 PENDING/INSUFFICIENT_DATA 折算为 0。

7. 只读与性能

  • CareerProgressService 标注 @Transactional(readOnly=true),不写业务表/审计表/通知表/缓存。
  • 所有附属数据批量读取(jobs/companies/interviews/scores/offers/cohort/快照/通知计数),查询次数与分页 items 数量无关,目标约 8–10 次,无 N+1。
  • size 上限固定 50。

8. 数据库与 Flyway

  • 不需要新表。
  • 不新增 Flyway V34。
  • 无 DDL、无数据变更。

9. 测试

  • CareerProgressServiceTest:权限/参数、空数据、完整聚合、DRAFT 不泄露、WITHDRAWN/REJECTED、无快照、批量查询无 N+1。
  • CareerProgressControllerTest:STUDENT 正常、COMPANY/ADMIN 403、未登录 401、参数非法 400、脱敏断言。
  • CareerProgressPersistenceIntegrationTest:真实仓库批量读取 + 只读性(关键表计数前后不变)。

10. 禁止事项(实现阶段)

  • 不修改 Candidate Scoring Engine、Competition Ranking 对外接口/权限/状态口径。
  • 不修改 Offer、Interview、Notification、Report/Block 的既有接口语义。
  • 不新增数据库表、不新增 Flyway、不新增 Redis 缓存、不新增异步任务。
  • 不新增错误码。
  • 不做企业回复率、推荐算法、AI 分析、薪资建议、通知内容聚合。
  • 不启停 Docker(黑盒阶段除外)。