From 2914eea299ec5a1d6bfdf40b2743226ef9cbc5af Mon Sep 17 00:00:00 2001
From: 刘帅 <rr.iii@qq.com>
Date: 星期一, 28 九月 2026 11:43:04 +0800
Subject: [PATCH] 脱敏接口调整及返回数据调整

---
 cloud-server-gateway/src/main/java/cn/mb/cloud/gateway/filter/SensitiveDataMaskingGlobalFilter.java |   23 +++++++++++++++++++++--
 cloud-server-other/src/main/java/com/dsh/other/controller/TVipController.java                       |    4 ++++
 cloud-server-account/src/main/java/com/dsh/privacy/SensitiveDataMaskingAdvice.java                  |   18 +++++++++++++++---
 cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java                       |    9 +++++++++
 4 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/cloud-server-account/src/main/java/com/dsh/privacy/SensitiveDataMaskingAdvice.java b/cloud-server-account/src/main/java/com/dsh/privacy/SensitiveDataMaskingAdvice.java
index 0565e3c..a484a09 100644
--- a/cloud-server-account/src/main/java/com/dsh/privacy/SensitiveDataMaskingAdvice.java
+++ b/cloud-server-account/src/main/java/com/dsh/privacy/SensitiveDataMaskingAdvice.java
@@ -7,6 +7,9 @@
 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -22,6 +25,11 @@
 @ControllerAdvice
 public class SensitiveDataMaskingAdvice implements ResponseBodyAdvice<Object> {
     private static final String INTERNAL_SENSITIVE_ACCESS = "X-Internal-Sensitive-Access";
+    /** APP endpoints whose clients receive original values and mask only when displaying them. */
+    private static final Set<String> APP_UNMASKED_PATHS = new HashSet<String>(Arrays.asList(
+            "/base/exploreWP/storeDetail",
+            "/base/exploreWP/customerList"
+    ));
 
     private final HttpServletRequest request;
 
@@ -31,9 +39,13 @@
 
     @Override
     public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
-        // The only unmasked account response is the management-only endpoint.
-        return !(request.getRequestURI().startsWith("/internal/sensitive/")
-                && "true".equalsIgnoreCase(request.getHeader(INTERNAL_SENSITIVE_ACCESS)));
+        String path = request.getRequestURI();
+        // Internal management calls require their sensitive-access header. The
+        // two explicitly listed APP endpoints return original values for
+        // client-side display masking; all other account responses are masked.
+        return !(APP_UNMASKED_PATHS.contains(path)
+                || (path.startsWith("/internal/sensitive/")
+                && "true".equalsIgnoreCase(request.getHeader(INTERNAL_SENSITIVE_ACCESS))));
     }
 
     @Override
diff --git a/cloud-server-gateway/src/main/java/cn/mb/cloud/gateway/filter/SensitiveDataMaskingGlobalFilter.java b/cloud-server-gateway/src/main/java/cn/mb/cloud/gateway/filter/SensitiveDataMaskingGlobalFilter.java
index d1dea58..c5e5003 100644
--- a/cloud-server-gateway/src/main/java/cn/mb/cloud/gateway/filter/SensitiveDataMaskingGlobalFilter.java
+++ b/cloud-server-gateway/src/main/java/cn/mb/cloud/gateway/filter/SensitiveDataMaskingGlobalFilter.java
@@ -15,6 +15,9 @@
 import reactor.core.publisher.Mono;
 
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -23,12 +26,28 @@
 public class SensitiveDataMaskingGlobalFilter implements GlobalFilter, Ordered {
     private static final Pattern FIELD = Pattern.compile("(?i)(\\\"(?:[a-z]*phone|[a-z]*mobile|telephone|tel)\\\"\\s*:\\s*\\\")([^\\\"]*)(\\\")");
     private static final Pattern IDCARD = Pattern.compile("(?i)(\\\"(?:[a-z]*idcard|[a-z]*identitycard|identitynumber|idnumber)\\\"\\s*:\\s*\\\")([^\\\"]*)(\\\")");
+    /**
+     * APP has to receive these values so it can apply its own display masking.
+     * Keep this list exact: all other routes continue to be protected at the
+     * gateway boundary.
+     */
+    private static final Set<String> APP_UNMASKED_PATHS = new HashSet<String>(Arrays.asList(
+            "/other/api/site/queryMySiteById",
+            "/account/base/exploreWP/storeDetail",
+            "/other/base/store/queryStoreFreeBenefit",
+            "/other/base/site/querySiteInfo",
+            "/activity/huimin/getStudentListByCarId",
+            "/account/base/exploreWP/customerList"
+    ));
 
     @Override public int getOrder() { return -2; }
 
     @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
-        // This route itself enforces the admin check and writes an audit record.
-        if (exchange.getRequest().getURI().getPath().startsWith("/system/sensitive/")) return chain.filter(exchange);
+        String path = exchange.getRequest().getURI().getPath();
+        // /system/sensitive enforces its own admin check and audit.  The APP
+        // paths below deliberately return their original fields for client-side
+        // display masking; every other response remains gateway-masked.
+        if (path.startsWith("/system/sensitive/") || APP_UNMASKED_PATHS.contains(path)) return chain.filter(exchange);
         ServerHttpResponseDecorator decorated = new ServerHttpResponseDecorator(exchange.getResponse()) {
             @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                 MediaType type = getHeaders().getContentType();
diff --git a/cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java b/cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java
index ea11d6e..25dc2c6 100644
--- a/cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java
+++ b/cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java
@@ -677,9 +677,18 @@
     })
     public ResultUtil<Map<String, Object>> queryMySiteById(Integer id) {
         try {
+            Integer uid = tokenUtil.getUserIdFormRedis();
+            if (uid == null) {
+                return ResultUtil.tokenErr();
+            }
             HashMap<String, Object> map = new HashMap<>();
             System.err.println("预约id"+id);
             SiteBooking siteBooking = siteBookingService.getById(id);
+            // This endpoint now returns the original contact data to the APP,
+            // so an authenticated user may only read their own booking.
+            if (siteBooking == null || !uid.equals(siteBooking.getAppUserId())) {
+                return ResultUtil.error("预约记录不存在");
+            }
             Integer targetAudience = siteBooking.getTargetAudience();
             System.err.println("预约"+siteBooking);
             Integer storeId = siteBooking.getStoreId();
diff --git a/cloud-server-other/src/main/java/com/dsh/other/controller/TVipController.java b/cloud-server-other/src/main/java/com/dsh/other/controller/TVipController.java
index 14eed78..2a30125 100644
--- a/cloud-server-other/src/main/java/com/dsh/other/controller/TVipController.java
+++ b/cloud-server-other/src/main/java/com/dsh/other/controller/TVipController.java
@@ -60,6 +60,10 @@
         for (Vip vip : list) {
             List<CouponVipResp> couponVipRespList = new ArrayList<>();
             List<TicketVipResp> ticketVipRespList = new ArrayList<>();
+            // The APP contract requires arrays even when this membership card
+            // has no coupon or ticket benefits.
+            vip.setCouponList(couponVipRespList);
+            vip.setTicketList(ticketVipRespList);
             String couponJson = vip.getCouponJson();
             JSONArray couponJsonArray = JSONArray.parseArray(couponJson);
             if(couponJsonArray==null){

--
Gitblit v1.7.1