# 我的店铺关注 — 技术方案（C 端）

> **依据：** 《我的店铺关注功能需求.md》v1.0  
> **关联：** 平台《会员管理技术方案》v1.3、《店铺管理技术方案》v1.3.6、《关联需求分析.md》v1.6；C 端《店铺主页技术方案》v1.0、《我的服务技术方案》v1.0、《商品详情内页技术方案》v1.0  
> **范围：** C 端 **`GET /api/member/shop-follows`** 分页列表（须会员 Token）；**新增/取消关注** **复用** 店铺主页 **`/api/shop/{shopId}/follow`**；**无** 新建业务表。  
> **原则：** 复用 `biz_member_shop_follow`、`biz_shop`、`biz_shop_stats`（店铺主页已建）；列表 **过滤已删店**（SF7）；排序 **关注时间倒序**（SF3）。

---

## 1. 技术架构

| 项 | 选型 |
|----|------|
| 基础框架 | RuoYi **v3.9.2**（`springboot2` 分支） |
| 数据库 | **MySQL 5.7.39** |
| ORM / 响应 | MyBatis；列表 **`TableDataInfo`**（`code`、`msg`、`rows`、`total`） |
| 鉴权 | **`MemberAuthInterceptor`**（`/api/member/**`）；**无** `@Anonymous` |
| 分页 | **`PageHelper`** + `startPage()` |
| 缓存 | **本期不做** Redis |

### 1.1 模块落位

```text
baqing-shop/src/main/java/com/ruoyi/web/modules/account/
├── controller/MemberAppController.java              # 追加 GET /shop-follows（或独立 Controller）
├── service/IMemberShopFollowAppService.java
├── service/impl/MemberShopFollowAppServiceImpl.java
├── mapper/MemberShopFollowAppMapper.java
├── vo/MemberShopFollowItemVO.java                   # 列表行
└── constant/MemberShopFollowConstants.java          # 可选 · 文案常量

已有（account · 鉴权）
├── config/MemberWebConfig.java                      # /api/member/** 已拦截
└── support/MemberAuthInterceptor.java、MemberContext.java

已有（home · 店铺主页 · 关注写）
├── controller/ShopAppController.java               # POST/DELETE /api/shop/{shopId}/follow
├── service/IShopAppService.java                     # follow / unfollow
└── mapper/ShopAppMapper.java                        # insertFollow / deleteFollow / fans_count

resources/mapper/account/MemberShopFollowAppMapper.xml
sql/biz_member_shop_follow.sql                       # 权威 DDL（店铺主页已引用）
sql/biz_shop_stats.sql
sql/biz_shop.sql
```

> **说明：** 本模块属 **用户链**，基路径归 **`/api/member`**，与 **我的服务** 资料/地址 **同鉴权**；**不** 放入 `home` 包，避免与 `/api/shop` 浏览接口混淆。

### 1.2 协作链

```text
【写入关注 · 非本模块】
    店铺主页 POST /api/shop/{shopId}/follow
        → biz_member_shop_follow INSERT
        → biz_shop_stats.fans_count + 1

【本模块 · 列表】
    GET /api/member/shop-follows?pageNum=&pageSize=
        → MemberContext.memberId
        → JOIN biz_shop（del_flag=0）+ biz_shop_stats
        → ORDER BY follow.create_time DESC

【取消关注 · 复用店铺主页】
    列表页（可选）或店铺主页
        → DELETE /api/shop/{shopId}/follow
        → 同 ShopAppServiceImpl.unfollow

【前端跳转】
    点击列表项 → 店铺主页路由（shopId）
        → GET /api/shop/{shopId} …（home 模块）
```

| 关联模块 | 协作 |
|----------|------|
| **店铺主页** | **写** 关注/取关；列表字段 **同源** `biz_shop` + `biz_shop_stats` |
| **我的服务** | **并列** 入口；共用 **会员 Token** 与 `MemberWebConfig` |
| **平台 · 会员管理** | 共用 `biz_member` / `sys_user`；平台 **无** 关注列表 API |
| **平台 · 店铺管理** | 维护 `biz_shop`；删店后列表 **JOIN 过滤** |
| **商品详情 / 搜索页** | 间接：详情/搜索 → 店主页关注 → 本列表可见 |

### 1.3 跨模块 Facade / Service

| 组件 | 本模块用法 |
|------|------------|
| **`IShopAppService.unfollow`** | 列表取消关注 **HTTP 层复用** `DELETE /api/shop/{id}/follow`，**不重复** 实现 |
| **`IShopAppService.follow`** | **本模块不暴露**；店主页 POST |
| **`IMemberFacade.isMemberEnabled`** | 登录拦截已保证；列表 **不额外** 校验 |
| **`IShopFacade`** | **不调用** |

---

## 2. 数据库设计

### 2.1 原则

**无新建表。** 表结构由 **《店铺主页技术方案》** 首建；本模块 **只读 JOIN**。

| 表 | 本模块用途 | 权威 DDL |
|----|------------|----------|
| `biz_member_shop_follow` | 当前会员关注关系；**排序键** `create_time` | `sql/biz_member_shop_follow.sql` |
| `biz_shop` | 店名、头像、状态；**过滤** `del_flag='0'` | `sql/biz_shop.sql` |
| `biz_shop_stats` | `rating`、`fans_count` | `sql/biz_shop_stats.sql` |

### 2.2 列表读条件

| 字段 / 条件 | 说明 |
|-------------|------|
| `f.member_id` | **=** `MemberContext.getMemberId()`（SF1） |
| `s.del_flag` | **`'0'`**（SF7 · 已删店 **不返回**） |
| 店态 `shop_status` | **不过滤**（停业 **仍展示**，SF 与 SH8 一致） |
| 排序 | **`f.create_time DESC, f.follow_id DESC`**（SF3） |

**输出字段：**

| 列 | 来源 |
|----|------|
| shopId | `f.shop_id` |
| shopName, shopAvatar, shopStatus | `biz_shop` |
| rating | `biz_shop_stats.rating`（无行 → **null**） |
| fansCount | `IFNULL(st.fans_count, 0)` |
| followTime | `f.create_time`（**可选** 返回；UI 可不展示） |

### 2.3 列表 SQL

```sql
SELECT f.shop_id       AS shopId,
       f.create_time   AS followTime,
       s.shop_name     AS shopName,
       s.shop_avatar   AS shopAvatar,
       s.shop_status   AS shopStatus,
       st.rating       AS rating,
       IFNULL(st.fans_count, 0) AS fansCount
FROM biz_member_shop_follow f
INNER JOIN biz_shop s
        ON f.shop_id = s.shop_id AND s.del_flag = '0'
LEFT JOIN biz_shop_stats st
       ON s.shop_id = st.shop_id
WHERE f.member_id = #{memberId}
ORDER BY f.create_time DESC, f.follow_id DESC
```

### 2.4 索引（已有 + 建议）

| 索引 | 表 | 用途 |
|------|-----|------|
| `uk_member_shop (member_id, shop_id)` | follow | 唯一关注、取关 DELETE |
| `idx_shop_id (shop_id)` | follow | 粉丝统计 |
| **建议** `idx_member_time (member_id, create_time)` | follow | 本模块列表分页（可选） |

> **删店与脏数据：** 关注行 **可保留**；列表 **INNER JOIN** 未删店即可 **对用户不可见**（SF7）。**不强制** 删店时清理 follow 行（与店铺主页一致）。

---

## 3. C 端接口设计

**本模块新增基路径：** `/api/member`（追加在 `MemberAppController` 或 `MemberShopFollowAppController`）  
**鉴权：** 须 **`Authorization: Bearer {token}`**；未登录 → **401**「请先登录」

### 3.1 接口一览

| 方法 | 路径 | 说明 | 归属 |
|------|------|------|------|
| GET | `/shop-follows` | **本模块** · 我的关注店铺分页列表 | account |
| POST | `/api/shop/{shopId}/follow` | **新增关注**（SF6 **不在此模块**） | home · 已有 |
| DELETE | `/api/shop/{shopId}/follow` | **取消关注**（列表/店主页 **共用**） | home · 已有 |

**本期不提供：**

| 项 | 说明 |
|----|------|
| `GET /shop-follows/{shopId}` 详情 | 进店走 **店铺主页** |
| 列表内 **搜索/筛选** | 需求 SF10 |
| 平台端关注列表 | 非本期 |

### 3.2 关注店铺列表 `GET /api/member/shop-follows`

| 项 | 说明 |
|----|------|
| 鉴权 | **必须** 会员 Token（`MemberWebConfig` · `/api/member/**`） |
| 数据范围 | **仅当前会员**；Query **无** `memberId` 参数（防越权） |
| 响应 | **`TableDataInfo`** |

**Query：**

| 参数 | 必填 | 说明 |
|------|:----:|------|
| pageNum | 否 | 页码（RuoYi 默认） |
| pageSize | 否 | 每页条数 |

**`rows` 元素：** `MemberShopFollowItemVO`

| 字段 | 类型 | 说明 |
|------|------|------|
| shopId | long | 进店路由参数 |
| shopName | string | |
| shopAvatar | string | |
| shopStatus | string | `0` 开业 / `1` 停业 |
| rating | decimal | 可 **null** |
| fansCount | int | 无统计行 → **0** |
| followTime | datetime | 关注时间；前端 **可忽略** |

**空结果：** `total=0`，`rows=[]` → 前端「暂无关注的店铺」。

**实现要点：**

1. `Long memberId = MemberContext.getMemberId()`（拦截器已注入）。  
2. `PageHelper.startPage()` → `MemberShopFollowAppMapper.selectFollowShopList(memberId)`。  
3. **不** 返回其它会员数据；**不** 暴露 follow 表主键（除非前端取消关注需 shopId，已含于 `shopId`）。

### 3.3 取消关注（复用 · 非本 Controller）

| 项 | 说明 |
|----|------|
| 方法 | **`DELETE /api/shop/{shopId}/follow`** |
| 鉴权 | Token + `MemberWebConfig` `/api/shop/*/follow` |
| 实现 | `ShopAppServiceImpl.unfollow`（事务：DELETE follow + `fans_count-1`） |
| 列表刷新 | 前端取消成功后 **重调** `GET /shop-follows` 或 **本地移除** 该项 |

> **不在** `/api/member/shop-follows/{shopId}` 重复提供 DELETE，避免双入口逻辑分叉。

### 3.4 新增关注（复用 · 非本 Controller）

| 项 | 说明 |
|----|------|
| 方法 | **`POST /api/shop/{shopId}/follow`** |
| 场景 | **店铺主页** 关注按钮（SF6） |

---

## 4. Service 分层

```text
MemberAppController.listShopFollows()
    → IMemberShopFollowAppService.listFollowShops(memberId)
        → MemberShopFollowAppMapper.selectFollowShopList(memberId)
        → 返回 List<MemberShopFollowItemVO>（PageHelper 分页）

【取消关注 · 不经过本 Service】
ShopAppController.unfollow(shopId)
    → IShopAppService.unfollow(shopId, memberId)
```

| 要点 | 说明 |
|------|------|
| 单一列表职责 | 本 Service **仅** 分页查询 |
| 写操作 | **全部** 在 `IShopAppService` |
| 会员 ID | Controller 从 `MemberContext` 取，**禁止** 客户端传入 |

---

## 5. 与平台后台对照

| 平台/商家操作 | 影响的 C 端本模块 |
|---------------|-------------------|
| 改店名/头像 | 刷新列表即更新 |
| 店铺 **停业** | **仍出现** 于列表 |
| 店铺 **逻辑删除** | 列表 **不再出现**（JOIN 过滤） |
| 平台 **禁用会员** | 无法登录 → **不可调** 本接口 |
| 平台会员详情 | **无** 关注列表；**不** 新增 B 端 API |

---

## 6. 业务规则映射

| 规则 | 实现 |
|------|------|
| SF0 / SF2 须登录 | `/api/member/**` 拦截器 |
| SF1 仅当前会员 | SQL `member_id = #{memberId}` |
| SF3 关注时间倒序 | `ORDER BY create_time DESC` |
| SF4 头像/名称/评分/粉丝 | JOIN shop + stats |
| SF5 进店 | 前端 `shopId` → 店铺主页 |
| SF6 新增关注不在此 | 仅 **POST** `/api/shop/.../follow` |
| SF7 已删店不展示 | `s.del_flag = '0'` |
| SF8 取消后消失 | **DELETE** `/api/shop/.../follow` + 刷新列表 |
| SF10 无搜索 | **无** keyword Query |

---

## 7. 与《店铺主页技术方案》分工

| 能力 | 店铺主页 `/api/shop` | 本模块 `/api/member` |
|------|----------------------|----------------------|
| 店介绍/分类/商品 | ✓ | — |
| POST 关注 | ✓ | — |
| DELETE 取关 | ✓（列表 **复用**） | — |
| 我的关注列表 | — | ✓ **`GET /shop-follows`** |
| 表 `biz_member_shop_follow` | 读写 | **读**（列表） |

---

## 8. 修订记录

| 版本 | 说明 |
|------|------|
| **v1.0** | 首版：`GET /api/member/shop-follows`；复用关注表与店铺主页取关/关注写接口；关联平台会员/店铺管理与店铺主页 |

---

*文档版本：v1.0 · 关联《我的店铺关注功能需求.md》v1.0、《店铺主页技术方案》v1.0、《我的服务技术方案》v1.0、《关联需求分析.md》v1.6 · 技术栈 RuoYi v3.9.2-springboot2 + MySQL 5.7.39*
