From 0d05986f8c80c5636f1f670605f1469326d86152 Mon Sep 17 00:00:00 2001 From: chenxing Date: Thu, 8 Oct 2026 23:27:33 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(appearance):=20=E5=A4=96=E8=A7=82?= =?UTF-8?q?=E5=81=8F=E5=A5=BD=E5=90=8C=E6=BA=90=E8=AF=BB=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E7=AB=AF=E7=82=B9=20GET=20/api/appearance=EF=BC=88oapi=20?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E8=BD=AE=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - YschOpenCall(target=userhome) 经 open-gateway 签名直调 /oapi/appearance,JsonNode 整体透传(哑管道) - 降级铁律:下游不可达 503(init 侧静默不动本地键),绝不伪装 200+全 null 清键 --- .../api/AppearanceProxyController.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/main/java/com/yunhai/notice/controller/api/AppearanceProxyController.java diff --git a/src/main/java/com/yunhai/notice/controller/api/AppearanceProxyController.java b/src/main/java/com/yunhai/notice/controller/api/AppearanceProxyController.java new file mode 100644 index 0000000..e3302fd --- /dev/null +++ b/src/main/java/com/yunhai/notice/controller/api/AppearanceProxyController.java @@ -0,0 +1,61 @@ +package com.yunhai.notice.controller.api; + +import com.fasterxml.jackson.databind.JsonNode; +import com.yunhai.notice.config.Config; +import com.yunhai.platform.openapi.core.YschOpenCall; +import com.yunhai.platform.utils.model.BoaUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import java.util.HashMap; +import java.util.Map; + +/** + * 外观偏好同源读代理(oapi 代理轮 2026-10-08,全端统一形态)。 + * + *

浏览器(通道 init 异步段)只与本应用【同源】取数——CAS 会话天然有效,无需 iframe 引导; + * 后端经 open-gateway 签名直调 UserHome(target=userhome,寻址走 yh.open.services, + * 勿写死 host)。设置入口收敛在 UserHome 设置中心,本端点【只读不写】。

+ * + *

契约 = UserHome /oapi/appearance 出参【整体透传】(JsonNode 哑管道):字段加减只在 + * UserHome + 通道 init 两端演进,本应用零改动。现行字段 {@code {theme, avatar, logoUrl}}—— + * theme/avatar 为服务端合成后的【生效值】(个人→学校→平台级联,不拆 my/sch); + * null=未配置,init 侧清对应本地键回落默认观感。

+ * + *

降级铁律:UserHome/网关不可达时【必须】以非 2xx 返回(503),绝不能伪装成 200+全 null—— + * init 会把全 null 收敛成清键,等于 UserHome 停服一次就清空全员本地观感 + * (对齐直连时代「fetch 失败静默、不动键」的语义)。

+ */ +@RestController +@RequestMapping(value = Config.API_PATH) +public class AppearanceProxyController { + + private static final Logger log = LoggerFactory.getLogger(AppearanceProxyController.class); + + /** open_api_target 注册表中的 userhome;寻址 yh.call.targets → yh.open.services → 枚举兜底 */ + private static final String TARGET_USERHOME = "userhome"; + + private final YschOpenCall openCall; + + public AppearanceProxyController(YschOpenCall openCall) { + this.openCall = openCall; + } + + @GetMapping("appearance") + public JsonNode appearance(BoaUser authUserWithDept) { + try { + Map q = new HashMap<>(); + q.put("userId", String.valueOf(authUserWithDept.getId())); // 服务端取登录人,不收前端参数 + return openCall.get(TARGET_USERHOME, "/oapi/appearance", q, JsonNode.class); + } catch (Exception e) { + log.warn("[appearance] UserHome 外观偏好不可达(503,init 侧静默不动本地键)userId={}:{}", + authUserWithDept.getId(), e.toString()); + throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "appearance unavailable"); + } + } +} From 09eb27c6cf411fc47132fe3895b5576db7750458 Mon Sep 17 00:00:00 2001 From: chenxing Date: Fri, 9 Oct 2026 00:39:47 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(appearance):=20503=20=E6=94=B9=20sendEr?= =?UTF-8?q?ror=20=E7=9B=B4=E5=86=99=E2=80=94=E2=80=94=E7=BB=95=E8=BF=87=20?= =?UTF-8?q?GlobalExceptionHandler=20=E5=85=9C=E6=88=90=20200+code:6=20?= =?UTF-8?q?=E4=BF=A1=E5=B0=81=EF=BC=88=E7=BA=BF=E4=B8=8A=E5=AE=9E=E6=B5=8B?= =?UTF-8?q?=E5=8F=91=E7=8E=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/api/AppearanceProxyController.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/yunhai/notice/controller/api/AppearanceProxyController.java b/src/main/java/com/yunhai/notice/controller/api/AppearanceProxyController.java index e3302fd..f6fd325 100644 --- a/src/main/java/com/yunhai/notice/controller/api/AppearanceProxyController.java +++ b/src/main/java/com/yunhai/notice/controller/api/AppearanceProxyController.java @@ -6,12 +6,12 @@ import com.yunhai.platform.openapi.core.YschOpenCall; import com.yunhai.platform.utils.model.BoaUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ResponseStatusException; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -47,15 +47,18 @@ public class AppearanceProxyController { } @GetMapping("appearance") - public JsonNode appearance(BoaUser authUserWithDept) { + public JsonNode appearance(BoaUser authUserWithDept, HttpServletResponse response) throws IOException { try { Map q = new HashMap<>(); q.put("userId", String.valueOf(authUserWithDept.getId())); // 服务端取登录人,不收前端参数 return openCall.get(TARGET_USERHOME, "/oapi/appearance", q, JsonNode.class); } catch (Exception e) { + // 降级铁律:sendError 直写 503 绕过 GlobalExceptionHandler(否则被兜成 200+code:6 信封, + // init 侧靠信封防御兜底,但语义上必须让浏览器拿到非 2xx) log.warn("[appearance] UserHome 外观偏好不可达(503,init 侧静默不动本地键)userId={}:{}", authUserWithDept.getId(), e.toString()); - throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "appearance unavailable"); + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "appearance unavailable"); + return null; } } }