From a3bfafde009c644c21ee8c9971b436f4cd7481f1 Mon Sep 17 00:00:00 2001
From: 刘帅 <rr.iii@qq.com>
Date: 星期日, 20 九月 2026 11:59:29 +0800
Subject: [PATCH] feat:手机号隐私问题

---
 cloud-server-gateway/src/main/java/cn/mb/cloud/gateway/filter/SensitiveDataMaskingGlobalFilter.java |   10 +---------
 cloud-server-account/src/main/java/com/dsh/account/model/vo/userBenefitDetail/AppUserDetailsVo.java |    2 --
 cloud-server-account/src/main/java/com/dsh/account/controller/AppUserController.java                |   18 ++++++++++++++++++
 cloud-server-other/src/main/java/com/dsh/other/feignclient/account/AppUserClient.java               |    9 +++++++++
 cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java                       |   18 ++++++++++++++++++
 5 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/cloud-server-account/src/main/java/com/dsh/account/controller/AppUserController.java b/cloud-server-account/src/main/java/com/dsh/account/controller/AppUserController.java
index 279e705..d8b3fbe 100644
--- a/cloud-server-account/src/main/java/com/dsh/account/controller/AppUserController.java
+++ b/cloud-server-account/src/main/java/com/dsh/account/controller/AppUserController.java
@@ -269,6 +269,24 @@
     }
 
     /**
+     * Internal data source used when an APP booking submits its displayed,
+     * masked phone number.  Limit the response to the single field needed for
+     * that booking; callers must use the internal sensitive-data header.
+     */
+    @PostMapping("/internal/sensitive/site-booking/app-user/{id}/phone")
+    public Map<String, Object> siteBookingPhone(@PathVariable("id") Integer id,
+                                                @RequestHeader(value = "X-Internal-Sensitive-Access", required = false) String access) {
+        if (!"true".equalsIgnoreCase(access)) {
+            throw new IllegalStateException("Sensitive data access is restricted to site booking");
+        }
+        TAppUser user = appUserService.getById(id);
+        if (user == null) return null;
+        Map<String, Object> result = new java.util.HashMap<String, Object>();
+        result.put("phone", user.getPhone());
+        return result;
+    }
+
+    /**
      * 冻结/解冻 1=解冻 2=冻结
      */
     @RequestMapping("/base/appUser/changeState")
diff --git a/cloud-server-account/src/main/java/com/dsh/account/model/vo/userBenefitDetail/AppUserDetailsVo.java b/cloud-server-account/src/main/java/com/dsh/account/model/vo/userBenefitDetail/AppUserDetailsVo.java
index 50b4f95..25047d6 100644
--- a/cloud-server-account/src/main/java/com/dsh/account/model/vo/userBenefitDetail/AppUserDetailsVo.java
+++ b/cloud-server-account/src/main/java/com/dsh/account/model/vo/userBenefitDetail/AppUserDetailsVo.java
@@ -1,7 +1,6 @@
 package com.dsh.account.model.vo.userBenefitDetail;
 
 
-import com.dsh.privacy.UnmaskedSensitiveData;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
@@ -17,7 +16,6 @@
     private String userName;
 
     @ApiModelProperty(value = "用户手机号")
-    @UnmaskedSensitiveData
     private String userPhone;
 
     @ApiModelProperty(value = "用户性别")
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 b150ae6..d1dea58 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
@@ -21,13 +21,6 @@
 /** Final API boundary for APP traffic, including responses returned by every service. */
 @Component
 public class SensitiveDataMaskingGlobalFilter implements GlobalFilter, Ordered {
-    /**
-     * This response contains the current user's own phone number, which the APP
-     * needs in its original form.  The account service marks the field with
-     * {@code @UnmaskedSensitiveData}; the gateway works on JSON text, so it
-     * must mirror that narrow exception here.
-     */
-    private static final String UNMASKED_USER_DETAILS_PATH = "/account/api/useBenefit/userDetails";
     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*\\\")([^\\\"]*)(\\\")");
 
@@ -35,8 +28,7 @@
 
     @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
         // This route itself enforces the admin check and writes an audit record.
-        String path = exchange.getRequest().getURI().getPath();
-        if (path.startsWith("/system/sensitive/") || UNMASKED_USER_DETAILS_PATH.equals(path)) return chain.filter(exchange);
+        if (exchange.getRequest().getURI().getPath().startsWith("/system/sensitive/")) 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 06074d1..0019170 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
@@ -14,6 +14,7 @@
 import com.dsh.other.enums.SiteBookingStatusEnum;
 import com.dsh.other.mapper.QrLinkMapper;
 import com.dsh.other.feignclient.CourseRecordClient;
+import com.dsh.other.feignclient.account.AppUserClient;
 import com.dsh.other.feignclient.account.CityManagerClient;
 import com.dsh.other.feignclient.activity.UserCouponClient;
 import com.dsh.other.feignclient.activity.model.QueryUserCouponByIdAndUserId;
@@ -53,6 +54,7 @@
 import java.util.*;
 import java.security.SecureRandom;
 import java.util.stream.Collectors;
+import java.util.regex.Pattern;
 
 /**
  * @author zhibing.pu
@@ -64,6 +66,7 @@
 public class SiteController {
 
     private static final char[] ENTRY_CODE_CHARS = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
+    private static final Pattern MAINLAND_MOBILE = Pattern.compile("^1[3-9]\\d{9}$");
     private final SecureRandom entryCodeRandom = new SecureRandom();
 
     @Autowired
@@ -89,6 +92,8 @@
 
     @Resource
     private CityManagerClient cityManagerClient;
+    @Resource
+    private AppUserClient appUserClient;
     @Autowired
     private IOperatorUserService operatorUserService;
 
@@ -390,6 +395,19 @@
             if (null == uid) {
                 return ResultUtil.tokenErr();
             }
+            String phone = reservationSite.getPhone();
+            if (phone != null && phone.contains("*")) {
+                Map<String, Object> userPhone = appUserClient.siteBookingPhone(uid, "true");
+                Object originalPhone = userPhone == null ? null : userPhone.get("phone");
+                if (!(originalPhone instanceof String) || ((String) originalPhone).trim().isEmpty()) {
+                    return ResultUtil.error("未获取到用户原始手机号");
+                }
+                reservationSite.setPhone((String) originalPhone);
+            } else if (phone == null || !MAINLAND_MOBILE.matcher(phone.trim()).matches()) {
+                return ResultUtil.error("请输入正确的手机号");
+            } else {
+                reservationSite.setPhone(phone.trim());
+            }
 //            Integer uid = 201;
             return siteService.reservationSite(uid, reservationSite);
         } catch (Exception e) {
diff --git a/cloud-server-other/src/main/java/com/dsh/other/feignclient/account/AppUserClient.java b/cloud-server-other/src/main/java/com/dsh/other/feignclient/account/AppUserClient.java
index 06a0fd4..1e2b234 100644
--- a/cloud-server-other/src/main/java/com/dsh/other/feignclient/account/AppUserClient.java
+++ b/cloud-server-other/src/main/java/com/dsh/other/feignclient/account/AppUserClient.java
@@ -2,7 +2,11 @@
 
 import com.dsh.other.feignclient.account.model.AppUser;
 import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+
+import java.util.Map;
 
 /**
  * @author zhibing.pu
@@ -28,4 +32,9 @@
      */
     @PostMapping("/base/appUser/updateAppUser")
     void updateAppUser(AppUser appUser);
+
+    /** Gets only the original phone for the authenticated user's site booking. */
+    @PostMapping("/internal/sensitive/site-booking/app-user/{id}/phone")
+    Map<String, Object> siteBookingPhone(@PathVariable("id") Integer appUserId,
+                                         @RequestHeader("X-Internal-Sensitive-Access") String sensitiveAccess);
 }

--
Gitblit v1.7.1