# 05 RLS 权限矩阵（Supabase 行级安全）

本节整理 `complete_mall_database.sql` 中的 RLS（Row Level Security）启用范围与策略意图，并给出“角色 × 表 × 操作”的矩阵化视角，便于前后端对齐。

> 说明：该库采用 Supabase 模式，常用 `auth.uid()` 获取当前登录用户的 auth id，并通过 `ak_users.auth_id` 映射到业务用户 `ak_users.id`。

---

## 1. RLS 设计目标

- **默认拒绝**：启用 RLS 后，如果没有策略，访问会被拒绝。
- **数据隔离优先**：用户私有数据只能访问自己的行。
- **商家/用户双视角**：订单可被“买家”和“卖家”访问。
- **公共可见数据受限**：商品仅公开上架数据。

---

## 2. 启用 RLS 的表（来自 `complete_mall_database.sql`）

脚本显式启用 RLS：

- `ml_user_profiles`
- `ml_user_addresses`
- `ml_shopping_cart`
- `ml_user_favorites`
- `ml_browse_history`
- `ml_user_coupons`
- `ml_orders`
- `ml_products`

> 备注：其他表（如 `ml_categories/ml_brands/ml_shops/ml_order_items` 等）在该脚本片段中未显式启用 RLS。

---

## 3. 核心策略模式（pattern）

### 3.1 “归属自己”的通用模式

对用户私有表（档案、地址、购物车、收藏、浏览、券）使用类似逻辑：

- `SELECT/UPDATE/DELETE`：要求当前 `auth.uid()` 对应到该行的 `user_id`
- `INSERT`：要求插入行的 `user_id` 也属于当前 `auth.uid()`

概念表达（伪 SQL）：

```sql
-- 伪表达：当前登录者只能操作 user_id 属于自己的行
auth.uid() = (select auth_id from ak_users where id = <row.user_id>)
```

价值：

- 前端直连 DB 时，**就算请求参数伪造 user_id**，也无法读写别人的行。

### 3.2 “订单：买家/卖家都可访问”模式

订单 SELECT 策略允许 `auth.uid()` 属于 `user_id` 或 `merchant_id`：

```sql
auth.uid() in (
  select auth_id from ak_users where id in (user_id, merchant_id)
)
```

价值：

- 买家能看自己的订单
- 商家能看自己店铺相关订单（在当前“单商家订单模型”下成立）

### 3.3 “商品：公开上架，商家管理自己的”模式

- `SELECT`：仅 `status = 1` 的商品可见
- `INSERT/UPDATE/DELETE`：要求 `merchant_id` 属于当前登录商家

---

## 4. 权限矩阵（建议口径）

> 说明：此矩阵从业务语义出发描述“期望权限”。实际是否满足，还取决于：
> - 是否启用 RLS
> - 是否存在相应策略
> - `ak_users` 中角色定义与 `auth_id` 映射是否正确

### 4.1 角色定义

- **Customer（消费者）**：普通用户
- **Merchant（商家）**：拥有商品与订单管理权限
- **Admin（管理员）**：平台管理（通常需要 service role 或额外策略）

### 4.2 表级矩阵（读/写）

#### `ml_user_profiles`

- Customer
  - **SELECT**：仅本人
  - **INSERT/UPDATE/DELETE**：仅本人
- Merchant
  - 同 Customer（如果商家也是用户）
- Admin
  - 建议：通过 service role 或单独策略可读全量

#### `ml_user_addresses`

- Customer
  - **SELECT/INSERT/UPDATE/DELETE**：仅本人

#### `ml_shopping_cart`

- Customer
  - **SELECT/INSERT/UPDATE/DELETE**：仅本人

#### `ml_user_favorites` / `ml_browse_history` / `ml_user_coupons`

- Customer
  - **SELECT/INSERT/UPDATE/DELETE**：仅本人

#### `ml_orders`

- Customer
  - **SELECT/INSERT/UPDATE/DELETE**：仅自己的订单
- Merchant
  - **SELECT/INSERT/UPDATE/DELETE**：仅 `merchant_id` 为自己的订单
- Admin
  - 建议：service role 或独立策略全量访问

#### `ml_products`

- Public / Customer
  - **SELECT**：仅上架（`status=1`）
- Merchant
  - **SELECT**：至少能看上架；更合理的做法是：商家能看自己所有状态商品（当前策略是否支持需核对）
  - **INSERT/UPDATE/DELETE**：仅自己的商品

---

## 5. 关键前提与性能建议

### 5.1 `ak_users.auth_id` 的唯一性与索引

由于策略频繁执行子查询：

```sql
select auth_id from ak_users where id = ...
```

建议：

- 确保 `ak_users.id` 为主键（已有）
- 确保 `ak_users.auth_id` 存在且唯一（建议唯一索引）

### 5.2 RLS 子查询的成本

RLS 每次查询都要执行策略表达式。若策略中大量子查询，可能带来性能压力。

可选优化方向：

- 在业务表冗余 `auth_id`（空间换性能）
- 使用 `security definer` 函数封装策略逻辑（需谨慎）
- 确保常用过滤字段（`user_id/merchant_id/status`）有索引
