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

7.7 KiB

/applications 接口 500 问题诊断报告

前端联调:2026-09-29
测试账号:19900009021 / ZhilaiTest!2026#A
后端地址:https://job.youthome.com.cn/api/v1


问题复现

请求 HTTP code message
GET /applications(带 Bearer token) 500 500 internal server error
GET /applications?page=0&size=20(带 token) 500 500 internal server error
GET /applications(不带 token) 401 401 not authenticated
GET /applications/me(带 token) 200 200 ok ✓
POST /applications(带 token,投递) 404 7001 job not found ✓(正确路由,只是 jobId 不存在)
GET /applications/me/timeline(带 token) 404 8002 application not found ✓(正确路由)

结论:GET /applications 带 token 必现 HTTP 500,是后端 bug。


根因分析

1. OpenAPI 文档中根本没有定义 GET /applications

后端公开的 28 个端点里,applications 模块只有 5 个:

方法 路径 用途 状态
POST /applications 提交投递 ✅ 正常(jobId 不存在时返回 404)
GET /applications/me 我的投递列表(分页) ✅ 正常(返回 200 + 分页结构)
GET /applications/{id} 投递详情 ✅ 正常(id 不存在返回 404)
PATCH /applications/{id}/status 企业改投递状态 — 未测
POST /applications/{id}/withdraw 撤回投递 — 未测
GET /applications/{id}/timeline 状态时间线 ✅ 正常(id 不存在返回 404)

没有 GET /applications 这条路由。

2. 为什么不是 404 而是 500?

这是 Spring Boot Controller 路由冲突 导致的。

后端 Controller 大概率写了:

@RestController
@RequestMapping("/api/v1/applications")
public class ApplicationController {

    @GetMapping("/me")                      // GET /applications/me  → 200 ✓
    public Page<Application> listMine() { ... }

    @GetMapping("/{id}")                    // GET /applications/{id} → 404(id 不存在)✓
    public Application getById(@PathVariable Long id) { ... }

    @PostMapping("")                        // POST /applications → 404(jobId 不存在)✓
    public Application create(@RequestBody ...) { ... }

    // 但没有写 @GetMapping("")!
}

当请求 GET /applications 进来时:

  • Spring 路由解析器在候选路由里找匹配
  • /applications/me 需要路径是 /me,不匹配
  • /{id} 匹配成功 —— id 被解析为字符串 "applications"
  • 但方法签名上 @PathVariable Long id 期望 Long 类型
  • Spring 尝试把 "applications" 转成 Long → 类型转换异常
  • 全局异常处理器没兜住这个异常 → 返回 HTTP 500 internal server error

这是 Spring Boot Controller 常见的路由设计陷阱:一个 Controller 既有 /{id} 又有 /me,但忘了写根路径 @GetMapping("") 或显式拒绝根路径的 GET 请求。


修复方案(后端二选一)

方案 A:显式拦截并返回 404(推荐,防御性编程)

@GetMapping("")
public ResponseEntity<Void> listAll() {
    // applications 没有"列出所有投递"这个语义
    return ResponseEntity.notFound().build();
}

方案 B:确认不需要这个路由,忽略

如果后端根本不打算提供 GET /applications(全量投递列表——企业端才有意义,而且需要按企业 ID 过滤),那 Spring Boot 默认会 404。但 当前 500 是路由冲突导致的,需要清理路由或者统一异常处理。

方案 C:按角色分发(如果后端真打算提供)

@GetMapping("")
public ResponseEntity<?> listAll(Authentication auth) {
    // STUDENT 应该只查自己的 → 404 引导去 /applications/me
    // COMPANY 可以查自己企业下的投递 → 需要 companyId 过滤
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of(
        "code", 404, "message", "这个路由不存在,请使用 /applications/me 或 /applications/{id}"
    ));
}

其他建议

1. Spring Boot 的 /{id} 路由建议加类型约束

// 只匹配数字 ID,排除 "/me" 等子路径
@GetMapping("/{id:\\d+}")
public Application getById(@PathVariable Long id) { ... }

这样 GET /applications 和 GET /applications/me 不会被 /{id} 吃掉。

2. 建议加 Controller 测试

@WebMvcTest(ApplicationController.class)
class ApplicationControllerTest {
    @Test
    void get_root_shouldReturn404() throws Exception {
        mockMvc.perform(get("/api/v1/applications"))
            .andExpect(status().isNotFound());
    }
}

3. 统一异常处理加类型转换兜底

@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<Map<String, Object>> handleTypeMismatch(MethodArgumentTypeMismatchException e) {
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of(
        "code", 404,
        "message", "资源不存在"
    ));
}

给后端同事的话

XX,GET /applications(不带 /me)带 token 返回 500 我测出来了,OpenAPI 文档里也没定义这个路由。

根因是 Spring 路由冲突:Controller 里有 GET /applications/{id} 但没有 GET /applications,所以 "applications" 这个字符串被当成了 @PathVariable Long id 传给方法,类型转换失败抛异常,全局异常处理器没兜住就 500 了。

前端这边不需要这个路由——学生端调的是 /applications/me(正常),投递调的是 POST /applications(正常),详情调的是 GET /applications/{id}(正常)。所以最简单的修复就是给 Controller 加个 @GetMapping("") 返回 404,或者 /{id} 加类型约束 @GetMapping("/{id:\\d+}") 排除子路径。

具体怎么修看你那边代码风格,上面三个方案选一个就行。


附:其他需要后端补的端点清单

(来自全量联调扫描,前端有调用但后端没端点)

🔴 高优先级

前端路径 用途 前端函数
GET /jobs/{id}/score 岗位综合评分(8 维度雷达图) fetchJobScore
GET /jobs/{id}/salary 薪资分布数据 fetchJobSalary
GET /jobs/{id}/reviews 面经/岗位评价列表 fetchJobReviews
GET /jobs/{id}/competition-ranking 同岗位竞争力排名 fetchRankData
GET /profile/me/career-progress 求职进度时间轴 fetchJobProgress
GET /jobs/favorites 我的收藏岗位 fetchFavorites
GET /conversations 沟通会话列表 fetchConversations
GET /notifications 系统通知 fetchNotifications

🟡 中优先级

前端路径 用途 前端函数
GET /jobs/compare 岗位对比 fetchJobCompare
GET /offers/compare Offer 对比 fetchOfferCompare
GET /profile/me/preferences 求职偏好 fetchJobPreference
PUT /profile/me/preferences 保存求职偏好 更新
POST /jobs/{id}/reviews 发布评价 submitReview
GET /interviews/me 面试列表 fetchMyInterviews
GET /interviews/{id}/score 面试评分 fetchInterviewScore
GET /company/candidates/{id} 候选人详情 fetchCandidateDetail

🔴 已对齐但后端仍有问题

端点 问题
POST /auth/sms/send sms sender unavailable(短信网关未通,无 mock code 旁路)
GET /jobs/search / GET /jobs / GET /companies/{id}/jobs items: [](后端无 job seed 数据)
GET /profile/me 返回空对象 {}(后端未定义 STUDENT profile 字段)
GET /companies/{id} 只有 id=1 有数据(企业 seed 不足)