# 移动端登录鉴权白名单变更说明

> 版本：v1.2  发布日期：2026-06-29  适用模块：jeesharp-business / business-mobile-gateway

---

## 变更记录（2026-06-29）— 微信支付成功回调统一入口

杏花村 C 端各业态支付成功回调收敛为单一路径，yaml 白名单同步调整：

| 变更 | 说明 |
|------|------|
| **新增** | `/mobile/pay/notify/**`（`TravelWechatPayNotifyMobileController`） |
| **移除** | 各业态独立路径（如 `/mobile/hotel/booking/pay/notify/**`、`/mobile/ticket/booking/pay/notify/**` 等） |
| **保留** | `/mobile/order/pay/notify/wechat/refund`（商城退款）、`/mobile/food/meal/order/refund/notify/**` |

---

## 一、本次变更目标（v1.1 / 2026-05-30）

实现「**默认免登录浏览、按需登录提示**」的鉴权策略，让未登录用户也能完整浏览首页、详情页等公开内容，仅在触发收藏、下单、个人中心等私有操作时才提示登录。

落地手段是「**双侧同步**」：

- **前端**：所有公开接口的请求函数显式声明 `skipAuth: true`，避免本地过期 Token 被带入触发 starter 抛 `code:201`。
- **后端**：在 `application.yaml` 的 `jeesharp.member-auth.sa-interceptor.exclude-path-patterns` 配置中加入对应路径，使 Sa-Token 拦截器放行匿名访问。

⚠️ 前提条件：平台核心 starter `jeesharp-spring-boot-starter-member-auth` 必须 ≥ `3.0.1-Beta-22`（已具备 `SaInterceptorProperties` 合并默认值能力），否则 yaml 配置不生效。

---

## 二、变更全景一览

### 2.1 前端 `skipAuth: true` 改动统计

> 仓库：`xinghuacun_travel_mini_app`  路径：`src/api/modules/*.ts`

本轮（含历次累计）总共 **20 个 api 模块 / 60+ 处 GET 调用** 已加 `skipAuth: true`。完整清单见 §三。

### 2.2 后端 yaml 白名单累计 28 行

> 仓库：`xinghuacun_travel_manager`  路径：`web-starter/src/main/resources/application.yaml`

`jeesharp.member-auth.sa-interceptor.exclude-path-patterns` 的全量白名单见 §四。

---

## 三、前端 `skipAuth: true` 变更明细


### 3.1 历史已加 skipAuth 的模块（v1.0 基础白名单）

| api 模块 | 关键函数 | 用途 |
| --- | --- | --- |
| splashAd.ts | getSplashAdCurrent | 开机动画 |
| homeShell.ts | fetchHomeShell | 首页壳层（含天气/8 宫格） |
| homeBanners.ts | fetchHomeBanners | 首页轮播 |
| homeGridIntro.ts | list / detail | 首页 8 宫格图文介绍 |
| homeEntryPopup.ts | fetchHomeEntryPopup | 首页运营弹窗 |
| notice.ts | getNoticeList / getNoticeDetail | 公告 |
| privacyPolicy.ts | fetchPrivacyPolicyByCode | 协议政策 |
| aboutUs.ts | fetchAboutUsInfo | 关于我们 |
| marketing.ts | fetchMarketingActivities / Detail | 营销活动列表 / 详情 |
| cultureActivity.ts | activities / activity detail | 文旅活动 |
| heritage.ts | culture-products / projects（列表 + 详情）| 文化遗产 |
| village.ts | destinations 列表 + 详情 | 美丽乡村 |
| hotel.ts | stays 列表 + 详情 | 酒店民宿 |
| food.ts | restaurants 列表 + 详情 | 美食 |
| leisure.ts | venues 列表 + 详情 | 休闲玩乐 |
| yearcard.ts | products 列表 + 详情 | 年卡商品 |
| study.ts | programs 列表 + 详情 | 研学课程 |
| explain.ts | spots 列表 + 详情 + byGuidePoi | 解说点 |
| travelStrategy.ts | hotList / page / detail | 旅游攻略 |
| guidePois.ts | fetchGuidePois / fetchGuidePoiById | 导览 POI |

### 3.2 v1.1 本次新增 / 补全的模块（17 处）

| 模块 | 函数 | URL | 备注 |
| --- | --- | --- | --- |
| coupons.ts | fetchDemoCoupons | `/coupon/templates` | 首页推荐券模板 |
| coupons.ts | fetchCouponById | `/coupon/template/{id}` | 券模板详情 |
| routePlan.ts | fetchRoutePlans | `/route/plans` | 旅游路线 tab 入口 |
| routePlan.ts | fetchRoutePlanById | `/route/plan/{id}` | 线路详情 |
| product.ts | getProductDetail | `/product/{id}` | 商品详情 |
| product.ts | getMallBanners | `/mall/banners` | 商城轮播 |
| product.ts | getMallCategories | `/mall/categories` | 商城分类 |
| product.ts | getMallFlashList | `/mall/flash` | 限时秒杀 |
| product.ts | getMallProductList | `/mall/products` | 商品列表 |
| product.ts | getMallRecommendList | `/mall/recommend` | 猜你喜欢 |
| product.ts | getProductReviews | `/product/{id}/reviews` | 商品评价 |
| product.ts | getProductSpecParams | `/product/{id}/specs` | 商品规格 |
| marketing.ts | fetchMarketingShareConfig | `/marketing/activity/share-config` | 分享配置 |
| marketing.ts | fetchPromotionGiftDetail | `/marketing/promotion-gift/detail` | 推广赠礼 |
| marketing.ts | fetchShareCommissionDetail | `/marketing/share-commission/detail` | 分销佣金 |
| marketing.ts | fetchMarketingPosterTemplate | `/marketing/poster/template` | 海报模板 |
| review.ts | fetchReviewDimensionTemplates | `/review/review/dimensionTemplate` | 评价维度模板 |
| review.ts | fetchReviewPublicPage | `/review/review/publicList` | 公开评价列表 |
| guideSync.ts | fetchGuideSyncFromRoute | `/guide/sync-from-route` | 路线同步导览 |

### 3.3 故意保留鉴权的「凭单查询私有接口」（不要放行）

| 函数 | URL | 原因 |
| --- | --- | --- |
| fetchCultureEnrollResult | `/culture/activity/enroll/result/{id}` | 凭报名单 ID 查结果 |
| fetchYearCardPayResult | `/yearcard/pay-result/{orderId}` | 凭订单 ID 查支付结果 |
| fetchRouteTripResult | `/route/trip/result/{orderId}` | 凭订单 ID 查行程结果 |

### 3.4 仍需登录的接口（绝对不要放行）

| 业务 | 路径 | 原因 |
| --- | --- | --- |
| 个人中心 | `/mobile/member/userCenter/**`、`/mobile/member/member/**`、`/mobile/member/coupon/**`（我的券）、`/mobile/member/favorite/**`、`/mobile/member/vipIap/**`、`/mobile/member/message/**` | 与当前登录会员强绑定 |
| 订单 / 交易 | `/mobile/mallOrder/**`、`/mobile/tradeOrder/**`、`/mobile/order/**`（除支付回调）、`/mobile/address/**` | 写操作必须实名 |
| 评价 / 反馈写入 | `/mobile/review/review/submit`、`/mobile/feedback/**` | 防垃圾 |
| 各业务下单 | `*/booking/create`、`*/enroll`、`*/receive` 等 | 业务侧 `@SaCheckLogin` 控制 |


---

## 四、后端 yaml 白名单（全量当前值）

> 文件：`web-starter/src/main/resources/application.yaml`  
> 节点：`jeesharp.member-auth.sa-interceptor.exclude-path-patterns`

```yaml
jeesharp:
  member-auth:
    sa-interceptor:
      include-path-patterns:
        - /mobile/**
        - /api/app/**
      exclude-path-patterns:
        # —— 登录与注册 ——
        - /mobile/member/auth/**
        # —— 通用免登录通道（推荐新建免登录接口走此前缀，无需改 yaml）——
        - /mobile/noAuth/**
        # —— 支付回调 ——
        - /mobile/order/pay/notify/wechat
        - /mobile/ticket/booking/pay/notify/**
        - /mobile/hotel/booking/pay/notify/**
        # —— 首页基础 ——
        - /mobile/home/**
        - /mobile/setting/homeBanner/**
        - /mobile/setting/homeGridIntro/**
        # —— 公开内容 / 资讯 ——
        - /mobile/setting/travelNews/**           # 旅游资讯
        - /mobile/setting/travelStrategy/**       # 旅游攻略
        - /mobile/setting/aboutUs/**              # 关于我们
        - /mobile/setting/helpCenter/**           # 帮助中心
        - /mobile/setting/privacyPolicy/**        # 用户协议 / 隐私政策
        - /mobile/notice/**                       # 通知公告
        - /mobile/marketing/**                    # 活动营销 / 推广列表
        # —— 公开浏览类业务（仅展示，下单/收藏走前端登录拦截）——
        - /mobile/village/**                      # 村落 / 景区介绍
        - /mobile/heritage/**                     # 非遗 / 文创
        - /mobile/culture/**                      # 文旅活动
        - /mobile/hotel/**                        # 酒店民宿
        - /mobile/food/**                         # 美食
        - /mobile/leisure/**                      # 休闲玩乐
        - /mobile/study/**                        # 研学
        - /mobile/yearcard/**                     # 年卡商品（仅商品浏览）
        - /mobile/explain/**                      # 解说点
        - /mobile/guide/**                        # 导览 POI 浏览
        - /mobile/scenic/**                       # 景区 / 景点
        - /mobile/coupon/templates                # 优惠券模板列表（首页展示用）
        - /mobile/coupon/template/**              # 优惠券模板详情
        # —— v1.1 新增 ——
        - /mobile/mall/**                         # 商城首页轮播 / 分类 / 推荐 / 秒杀 / 商品列表
        - /mobile/product/*                       # 商品详情（GET /product/{id}，仅 1 段）
        - /mobile/product/*/reviews               # 商品评价（公开浏览）
        - /mobile/product/*/specs                 # 商品规格参数
        - /mobile/route/plans                     # 线路规划列表（旅游路线 tab 入口）
        - /mobile/route/plan/**                   # 线路规划详情
        - /mobile/marketing/activity/share-config # 活动分享配置（公开）
        - /mobile/marketing/promotion-gift/detail # 推广赠礼配置（公开）
        - /mobile/marketing/share-commission/detail # 分销佣金配置（公开）
        - /mobile/marketing/poster/template       # 营销海报模板（公开）
        - /mobile/review/review/dimensionTemplate # 评价维度模板（公开展示）
        - /mobile/review/review/publicList        # 公开评价列表（详情页评论区）
        - /mobile/guide/sync-from-route           # 路线同步导览（公开查询）
```

### 4.1 v1.1 新增白名单（共 13 行）

| 路径 | 用途 | 关联前端 |
| --- | --- | --- |
| `/mobile/mall/**` | 商城首页轮播 / 分类 / 推荐 / 秒杀 / 商品列表 | product.ts `getMall*` |
| `/mobile/product/*` | 商品详情（单段通配，避免误放行写操作）| product.ts `getProductDetail` |
| `/mobile/product/*/reviews` | 商品评价（公开浏览）| product.ts `getProductReviews` |
| `/mobile/product/*/specs` | 商品规格参数 | product.ts `getProductSpecParams` |
| `/mobile/route/plans` | 旅游路线 tab 入口列表 | routePlan.ts `fetchRoutePlans` |
| `/mobile/route/plan/**` | 线路详情 | routePlan.ts `fetchRoutePlanById` |
| `/mobile/marketing/activity/share-config` | 活动分享配置 | marketing.ts `fetchMarketingShareConfig` |
| `/mobile/marketing/promotion-gift/detail` | 推广赠礼 | marketing.ts `fetchPromotionGiftDetail` |
| `/mobile/marketing/share-commission/detail` | 分销佣金 | marketing.ts `fetchShareCommissionDetail` |
| `/mobile/marketing/poster/template` | 营销海报模板 | marketing.ts `fetchMarketingPosterTemplate` |
| `/mobile/review/review/dimensionTemplate` | 评价维度模板 | review.ts `fetchReviewDimensionTemplates` |
| `/mobile/review/review/publicList` | 公开评价列表 | review.ts `fetchReviewPublicPage` |
| `/mobile/guide/sync-from-route` | 路线同步导览 | guideSync.ts `fetchGuideSyncFromRoute` |
| `/mobile/coupon/templates`、`/mobile/coupon/template/**` | 优惠券模板（首页/详情）| coupons.ts `fetchDemoCoupons` / `fetchCouponById` |

⚠️ **路径设计要点**：

- `/mobile/product/*` 用单段通配 `*` 而非 `**`，避免把 `/product/{id}/comment/create` 等写操作误放行。
- `/mobile/mall/**` 业务侧设计为「**只读浏览**」，所以可以用双星通配。后续若 mall 模块要新增写接口，必须改用单段通配并显式列出。


---

## 五、前端配套基础设施变更

### 5.1 request.ts 响应拦截器去掉强制 reLaunch（v1.1）

> 仓库：`xinghuacun_travel_mini_app`  文件：`src/utils/request.ts`

**改前**：任何接口收到 `code:201` → `uni.reLaunch('/package-views/pages/login/index')`，用户被踢出当前公开页。

**改后**：
- 默认接口收到 `201` → 静默 `clearAuthState()`（清本地 token / satoken / userInfo）+ 3 秒节流 toast「登录已失效，请重新登录」；
- `skipAuth: true` 接口收到 `201` → 仅抛错由调用方静默兜底，不打扰用户；
- **完全不再主动跳登录页**，用户继续留在当前公开页面浏览。

```ts
if (res.code === 201) {
  if (!skipAuth) {
    clearAuthState()
    notifyTokenExpiredOnce(res.msg)
  }
  throw new Error(res.msg || '请重新登录')
}
```

### 5.2 tokenStorage.ts 新增 clearAuthState（v1.1）

> 文件：`src/utils/tokenStorage.ts`

把「清本地登录态」函数下沉到 `tokenStorage.ts`，避免与 `auth.ts` 形成循环依赖。`auth.ts` 改为 re-export 保持原对外 API 不变。

> 解决了 v1.1 前期出现的 `/auth.js".then is not a function` 报错（小程序不支持动态 `import()`，会被打包为同步 `require`）。

### 5.3 全局守卫工具（v1.1）

> 文件：`src/utils/auth.ts`

| API | 用途 | 典型调用 |
| --- | --- | --- |
| `requireLogin(options?)` | 仅检查登录态，未登录弹确认窗口 → 「去登录」跳登录页 | `if (!requireLogin()) return` |
| `requireLoginNavigate(url, options?)` | 已登录直接 `navigateTo`，未登录走确认窗口 | `requireLoginNavigate('/pkg/.../order/create')` |
| `clearAuthState()` | 仅清本地态（不调远程 logout、不跳页）| 供 request 拦截器内部使用 |

`options` 字段：

- `silent`：true 跳过 modal 直接跳登录（自动续期失败兜底）
- `prompt`：自定义弹窗标题/正文/按钮文案
- `redirect`：登录成功后希望回跳的 path
- `onLogin`：「去登录」前的钩子（埋点用）

### 5.4 profile 页 onShow 增加未登录拦截（v1.1）

> 文件：`src/pages/profile/index.vue`

未登录用户点「我的」tab → 直接 `reLaunch` 登录页，避免进入「我的」后看到大片空白和接口 `201` 提示。

```ts
onShow(() => {
  tabBarStore.setTabRoute('pages/profile/index')
  syncWeixinTabBarSelected(2)
  if (!userStore.isLoggedIn) {
    uni.reLaunch({ url: ROUTE_LOGIN_INDEX, fail: () => uni.navigateTo({ url: ROUTE_LOGIN_INDEX }) })
    return
  }
  void refreshProfileData()
})
```

### 5.5 dev-only 灰度告警（v1.1）

> 文件：`src/utils/request.ts`

```ts
} else if (!config.skipAuth) {
  // #ifdef DEBUG
  console.warn(
    `[auth] 未登录态请求未声明 skipAuth，可能触发 code:201：${config.method || 'GET'} ${config.url}`
  )
  // #endif
}
```

用 `#ifdef DEBUG` 包裹，**只在开发版本输出**，生产构建会被 uniapp 条件编译完全剥离，零运行时开销。一旦控制台出现该警告，请：

1. 到对应 `api/modules/*.ts` 给该函数补 `{ skipAuth: true }`；或
2. 在调用前 `requireLogin()` 做登录守卫。

### 5.6 详情页「提交评价」改为直接跳登录（v1.1）

> 影响文件：`src/pages/strategy/detail.vue`、`src/package-views/pages/food/detail.vue`

旧逻辑：`showModal('登录后即可评价...')` → 用户点「去登录」才跳。  
新逻辑：直接 `navigateTo` 登录页，更符合用户期望「点了就跳，无需二次确认」。


---

## 六、平台核心 starter 配套变更

> 仓库：`spring-boot-platform-core_3.0.1-Beta`  
> 模块：`jeesharp-framework / jeesharp-spring-boot-starter-member-auth`

### 6.1 新增 SaInterceptorProperties

```java
@ConfigurationProperties(prefix = "jeesharp.member-auth.sa-interceptor")
public class SaInterceptorProperties {
    private List<String> includePathPatterns = new ArrayList<>();
    private List<String> excludePathPatterns = new ArrayList<>();
}
```

### 6.2 改造 SaInterceptorConfiguration

把原硬编码白名单改为「默认值（框架契约） + 业务扩展（yaml 追加）」的合并模式：

```java
static final String[] DEFAULT_EXCLUDE_PATH_PATTERNS = { "/mobile/member/auth/**" };

private List<String> mergePatterns(List<String> defaults, List<String> extras) {
    LinkedHashSet<String> set = new LinkedHashSet<>();
    set.addAll(defaults);
    if (extras != null) set.addAll(extras);
    return new ArrayList<>(set);
}
```

`LinkedHashSet` 去重 + 保留插入顺序，确保业务 yaml 配置真正生效又不被覆盖。

### 6.3 starter 最低版本

`jeesharp-spring-boot-starter-member-auth >= 3.0.1-Beta-22`

---

## 七、验收测试用例

请按以下场景手工验证（前端冷启动并清缓存后逐步执行）：

| # | 场景 | 期望结果 |
| --- | --- | --- |
| 1 | 冷启动 → splash → 首页 | 无任何 Token Toast，正常显示首页内容 |
| 2 | 未登录 → 商城首页 / 旅游路线 tab | 正常加载列表，无 Toast |
| 3 | 未登录 → 进入「热门攻略」攻略详情 | 详情正常显示，评论区正常加载，无 Toast |
| 4 | 未登录 → 进入「推荐榜」酒店/民宿/美食/景点详情 | 同上 |
| 5 | 未登录 → 点击「我的」tab | 直接跳到登录页 |
| 6 | 未登录 → 在攻略详情点「提交评价」| 直接跳到登录页（无二次确认 modal）|
| 7 | 已登录 → 切换 tab、查看个人中心 | 正常，无登录提示 |
| 8 | 登录态过期（手动清 token）→ 浏览公开内容 | 仅一次 toast「登录已失效」，不被踢到登录页 |
| 9 | dev 模式 → 控制台 | 无 `[auth] 未登录态请求未声明 skipAuth ...` 警告 |

---

## 八、后续维护规范（强制）

### 8.1 新增免登录接口约定

凡新建移动端「未登录也能访问」的接口，**必须**遵循 [.cursor/rules/api/mobile-api-rules.mdc §13](../../.cursor/rules/api/mobile-api-rules.mdc) 中的命名规范：

1. **后端路径**以 `/mobile/noAuth/` 开头，例如：
   ```java
   @RequestMapping("${jeesharp.web.mobilePath}/noAuth/operation/popup")
   ```
2. **前端请求** `skipAuth: true`：
   ```ts
   get('/noAuth/operation/popup/home', undefined, { skipAuth: true })
   ```
3. `/mobile/noAuth/**` 已永久预放行，**无需修改 yaml**。

### 8.2 新增业务路径下的免登录接口

如果接口路径不能走 `/mobile/noAuth/**`（比如需要复用已有 controller 路径），则：

1. 前端 `skipAuth: true`
2. **必须**在 yaml `exclude-path-patterns` 显式追加
3. 路径设计优先用单段通配 `*`，避免误放行写操作
4. 改完 yaml 后**必须重启服务**（不会热加载）

### 8.3 反例（禁止）

- ❌ 不要给个人中心、订单、收藏、评价、地址、消息等"写操作"或"用户私有数据"接口加 `@SaIgnore` 或写入 yaml 白名单
- ❌ 不要在前端公开接口忘记 `skipAuth: true`
- ❌ 不要直接修改 starter 的硬编码默认 `DEFAULT_EXCLUDE_PATH_PATTERNS`

### 8.4 排查 code:201 的 5 步定位法

按顺序检查：

1. 后端是否重启？（yaml 修改不会热加载）
2. starter 版本是否 ≥ `3.0.1-Beta-22`？
3. 接口路径是否真的命中 `exclude-path-patterns`？（大小写敏感、路径前缀完整）
4. 前端调用是否加了 `skipAuth: true`？
5. 微信开发者工具 Network 面板看请求头：仍带 `Authorization` 说明 `skipAuth` 没生效；不带则是后端拦截。

---

## 九、变更版本历史

| 版本 | 日期 | 主要内容 |
| --- | --- | --- |
| v1.0 | 2026-05-29 | 初始版本：前端 7 个首页基础模块 + 13 个公开浏览模块加 skipAuth；后端 yaml 落地 28 行基础白名单；starter 改造支持 yaml 合并；mobile-api-rules.mdc §13 写入项目规范 |
| v1.1 | 2026-05-30 | 灰度扫描补全 17 处遗漏（含 coupons / routePlan / product / marketing / review / guideSync）；后端 yaml 追加 13 行；request.ts 去掉强制 reLaunch；新增 `requireLogin` / `clearAuthState` 工具；profile 页 onShow 守卫；dev 警告机制；详情页评价改为直接跳登录 |

---

## 十、关联文档

- 项目规范：[.cursor/rules/api/mobile-api-rules.mdc §13](../../.cursor/rules/api/mobile-api-rules.mdc) — 「移动端免登录接口约定（强制）」
- 模块说明：[business-mobile-gateway/README.md](./README.md) — 「移动端登录鉴权白名单（v1.0）」章节
- 后端配置：[web-starter/src/main/resources/application.yaml](../../web-starter/src/main/resources/application.yaml) — `jeesharp.member-auth.sa-interceptor`
- 前端守卫：`src/utils/auth.ts`、`src/utils/request.ts`、`src/utils/tokenStorage.ts`
