# 企业候选人看板 API ## 1. 企业候选人列表 ``` GET /api/v1/company/candidates?[jobId][&status][&keyword][&sort][&order][&page][&size] ``` ### 请求参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | jobId | Long | 否 | 全部 | 按岗位筛选 | | status | String | 否 | 全部 | SUBMITTED/VIEWED/INTERVIEW/HIRED/REJECTED/WITHDRAWN | | keyword | String | 否 | — | 学生姓名/学校/学历关键词搜索 | | sort | String | 否 | createdAt | createdAt/updatedAt/match(match=按岗位匹配度降序) | | order | String | 否 | desc | asc/desc(match 模式下忽略,固定降序) | | page | int | 否 | 0 | 页码 (0-based) | | size | int | 否 | 20 | 每页条数 (1-100) | ### 权限 `COMPANY` 角色,从 JWT 获取用户 → 查 Company → 用 companyId 过滤数据。 ### 响应 ```json { "code": 200, "message": "ok", "data": { "items": [{ "applicationId": 1, "jobId": 10, "jobTitle": "Java 后端工程师", "status": "SUBMITTED", "candidateName": "张三", "candidateSchool": "清华大学", "candidateEducation": "本科", "candidateGraduationYear": 2026, "candidateAvatarUrl": "https://...", "createdAt": "2026-08-15T10:00:00", "updatedAt": "2026-08-16T14:00:00", "matchScore": 82, "matchReasons": ["技能匹配", "学历符合", "城市匹配", "综合评分高"], "matchPercentile": 80 }], "total": 156, "page": 0, "size": 20, "totalPages": 8 }, "requestId": "abc-123", "timestamp": "2026-08-17T15:00:00Z" } ``` > 所有排序下列表项**一致返回** `matchScore`(Integer,0-100,不保留小数)与 `matchReasons` > (简短短语数组,如 `["技能匹配","学历符合","城市匹配","综合评分高"]`,不包含技能明细)。 > `sort=match` 时按 `matchScore` 从高到低排序;`sort=createdAt/updatedAt` 保持按时间排序,但同样携带匹配字段。 > matchScore/matchReasons 属企业内部筛选信息,学生侧不可见。 ### 错误码 | 状态码 | 错误码 | 说明 | |--------|--------|------| | 400 | 1001 | 参数非法 (page<0, size<1或>100, 无效status/sort/order, keyword>100, jobId<=0) | | 401 | — | 未认证 | | 403 | 2002 | 非 COMPANY 角色 | | 404 | 5001 | 企业不存在 | --- ## 2. 企业候选人详情 ``` GET /api/v1/company/candidates/{applicationId} ``` ### 权限 `COMPANY` 角色,且该投递对应的岗位必须属于当前企业。 ### 响应 ```json { "code": 200, "message": "ok", "data": { "applicationId": 1, "jobId": 10, "jobTitle": "Java 后端工程师", "jobCity": "北京", "jobEmploymentType": "全职", "status": "SUBMITTED", "coverLetter": "尊敬的HR...", "createdAt": "2026-08-15T10:00:00", "updatedAt": "2026-08-16T14:00:00", "candidateName": "张三", "candidateAvatarUrl": "https://...", "candidateBio": "热爱编程...", "candidateLocation": "北京", "candidateSchool": "清华大学", "candidateEducation": "本科", "candidateGraduationYear": 2026, "candidateSkills": ["Java", "Spring Boot", "MySQL"], "matchScore": 82, "matchReasons": ["技能匹配", "学历符合", "城市匹配", "综合评分高"], "matchPercentile": 80 }, "requestId": "abc-123", "timestamp": "2026-08-17T15:00:00Z" } ``` > 详情同样**一致返回** `matchScore`(Integer,0-100,不保留小数)、`matchReasons` > (简短短语数组,如 `["技能匹配","学历符合","城市匹配","综合评分高"]`,不包含技能明细)与 `matchPercentile` > (Integer,岗位匹配百分位)。`matchPercentile` 口径与列表一致(按同岗位候选人池,`round(countLower/(cohortSize-1)*100)`,不足 2 人省略)。 > 该字段属企业内部筛选信息,学生侧不可见。 ### 错误码 | 状态码 | 错误码 | 说明 | |--------|--------|------| | 400 | 1001 | 参数非法 | | 401 | — | 未认证 | | 403 | 2002 | 非 COMPANY 角色 | | 403 | 8004 | 投递不属于本企业 | | 404 | 5001 | 企业不存在 | | 404 | 8002 | 投递不存在 | --- ## 安全说明 - companyId 从 JWT 推导,不接受请求参数 - 所有数据限定在 `jobs.company_id = :companyId`,天然隔离 - 不返回 `studentUserId`、`phone`、`passwordHash`、`email`、原始 `resumeUrl` - 列表不返回 `coverLetter`,详情返回 `coverLetter` - 详情使用单次 JOIN 查询 (applications + jobs + student_profiles + users),不产生 N+1