Pārlūkot izejas kodu

fix(订单实时): SSE 端点类级映射去掉重复的 /api/v1 前缀

server.servlet.context-path 已经是 /api/v1,控制器再写一次 ⇒ 真实 URL 变成
/api/v1/api/v1/payment/stream/orders,按正常路径访问 404(后端日志「路径不存在
[/api/v1/payment/stream/orders]」)。既有 4 条单测都直接调控制器方法、不经
DispatcherServlet,所以这条缺陷一路全绿到联调才暴露。

新增用例经 DispatcherServlet + contextPath("/api/v1") 走真实前缀供给方式:
RED(No mapping for GET /api/v1/payment/stream/orders → 404)→ GREEN 4/4。
alphaH 1 nedēļu atpakaļ
vecāks
revīzija
037f3811a3

+ 6 - 1
java/src/main/java/com/payment/platform/module/payment/stream/OrderStreamController.java

@@ -19,10 +19,15 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  * <p>身份取自 Security 上下文(与全仓 6 个控制器同一惯用法)。EventSource 不能自定义请求头,
  * 所以浏览器那条腿靠 {@code JwtAuthFilter} 对 {@code ?token=} 的现成支持;服务端不额外做令牌轮询
  * (滑动过期下 JWT 的 exp 不是权威源)。
+ *
+ * <p>★ 类级映射**不带** {@code /api/v1}:那一层由 {@code server.servlet.context-path} 供给。
+ * 2026-10-05 实测:写成 {@code /api/v1/payment/stream} 时真实 URL 变成
+ * {@code /api/v1/api/v1/payment/stream/orders},按正常路径访问得到后端日志
+ * {@code 路径不存在 [/api/v1/payment/stream/orders]}(单测直接调方法,看不见这一层)。
  */
 @Slf4j
 @RestController
-@RequestMapping("/api/v1/payment/stream")
+@RequestMapping("/payment/stream")
 @RequiredArgsConstructor
 public class OrderStreamController {
 

+ 28 - 0
java/src/test/java/com/payment/platform/module/payment/stream/OrderStreamControllerTest.java

@@ -7,6 +7,11 @@ import org.junit.jupiter.api.Test;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 
 import java.util.List;
@@ -63,4 +68,27 @@ class OrderStreamControllerTest {
         assertTrue(ex.getMessage().contains("未登录"), "实际文案=" + ex.getMessage());
         assertEquals(0, hub.connectionCount(), "匿名不许往桶里挂空连接");
     }
+
+    /**
+     * 前面三条用例都**直接调方法**,从不经 DispatcherServlet ⇒ 类级映射写错它们照样绿
+     * (2026-10-05 实测:后端日志 `路径不存在 [/api/v1/payment/stream/orders]`,而这条流量的
+     * 真实 URL 得是 `/api/v1/api/v1/...` 才通)。这一条补上那段路:
+     * 前缀由 `server.servlet.context-path=/api/v1` 供给(见 application.yml),
+     * 所以类级 {@code @RequestMapping} 只能是 `/payment/stream`,多写一次就双双落空。
+     */
+    @Test
+    void orders_urlUnderServletContextPath_reachesHandler() throws Exception {
+        login(2L, false);
+        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
+
+        MvcResult result = mockMvc.perform(MockMvcRequestBuilders
+                        .get("/api/v1/payment/stream/orders")
+                        .contextPath("/api/v1"))
+                .andReturn();
+
+        assertEquals(200, result.getResponse().getStatus(),
+                "context-path 之外的路径必须就是控制器映射;实际状态=" + result.getResponse().getStatus()
+                        + ",响应体=" + result.getResponse().getContentAsString());
+        MockMvcResultMatchers.request().asyncStarted().match(result);
+    }
 }