# 租户配置与用户管理指南

## 概述

系统采用多租户架构，平台管理员可创建租户（连锁品牌），每个租户有独立的业务数据库。租户管理员（hq）可在租户内创建区域经理、店长等角色。

### 角色体系

| 角色 | role 值 | 说明 | 数据范围 |
|------|---------|------|----------|
| 平台管理员 | `platform_admin` | SaaS 平台运维，管理所有租户 | 全平台 |
| 总部管理员 | `hq` | 租户内最高权限，管理本租户用户 | 本租户全部数据 |
| 商品部 | `dept` | 商品/供应链相关功能 | 本租户全部数据 |
| 区域经理 | `regional` | 管辖指定门店，看区域汇总 | `region` 字段指定的门店 |
| 店长 | `store` | 管理指定门店 | `store_code` 字段指定的门店 |

### 数据库结构

- **sbrain_admin**（平台管理库，服务器端口 5432）：存放 `tenant_configs`（租户配置）、`tenant_users`（租户用户）和 `platform_admins`（平台管理员）
- **bill_query**（业务数据库，客户本机端口 5432）：存放各租户的业务数据，通过 FRP 隧道映射到服务器的 16xxx 端口（现有租户用 15432，新租户从 16001 起）

### 架构示意

```
租户A用户 ─┐
租户B用户 ─┤→ Nginx(服务器) → 后端(服务器:9333) → 识别租户 → 连对应frp端口 → 客户本机PostgreSQL
租户C用户 ─┘

浏览器 → https://dm.all8ai.top
       → Nginx 反向代理 /api/ → 127.0.0.1:9333 (服务器后端)
       → 后端解析 JWT 获取 tenant_id
       → 查 tenant_configs 表获取该租户的 frp 端口
       → 连接 127.0.0.1:16xxx (frp 隧道)
       → frp 转发到客户本机 127.0.0.1:5432 (PostgreSQL)
       → 返回数据
```

### 组件部署位置

| 组件 | 位置 | 说明 |
|------|------|------|
| 前端(静态文件) | 服务器 | Nginx 托管，统一部署 |
| 后端(Node.js) | 服务器 | pm2 管理，一套代码服务所有租户 |
| 服务器主库(PostgreSQL) | 服务器 5432 端口 | 仅存 `tenant_configs`、`tenant_users`、`platform_admins`，不存业务数据 |
| 业务数据库(PostgreSQL) | 客户本机 5432 端口 | 每个租户自己的数据库，数据完全在客户侧 |
| frps(frp服务端) | 服务器 7000 端口 | 接收各租户 frp 客户端连接 |
| frpc(frp客户端) | 客户本机 | 每个租户运行自己的 frpc，暴露本机 PostgreSQL |

---

## 〇、新租户接入全流程

接入一个新租户需要完成以下 4 个阶段：

```
阶段1：客户本机准备 PostgreSQL 数据库
阶段2：配置 frp 隧道（客户本机 frpc + 服务器 frps 端口分配）
阶段3：服务器后台创建租户配置并激活
阶段4：创建用户账号（hq → regional → store）
```

### 阶段1：客户本机准备数据库

#### 1.1 安装 PostgreSQL

```bash
# macOS (Homebrew)
brew install postgresql@15
brew services start postgresql@15

# Ubuntu/Debian
sudo apt install postgresql postgresql-contrib
sudo systemctl start postgresql
```

#### 1.2 创建数据库和用户

```bash
# 进入 PostgreSQL
psql postgres

# 创建数据库用户（如不需要密码，使用 trust 认证）
CREATE USER freedak SUPERUSER;

# 创建业务数据库
CREATE DATABASE bill_query OWNER freedak;

# 退出
\q
```

#### 1.3 导入业务数据

```bash
# 方式1：从备份文件恢复
pg_restore -U freedak -d bill_query -1 /path/to/backup.dump

# 方式2：从 SQL 文件导入
psql -U freedak -d bill_query -f /path/to/init_data.sql

# 验证数据
psql -U freedak -d bill_query -c "SELECT count(*) FROM analytics.dim_store;"
```

#### 1.4 配置 PostgreSQL 允许本地连接

确保 `postgresql.conf` 中：

```conf
listen_addresses = 'localhost'
port = 5432
```

确保 `pg_hba.conf` 中允许本地 trust 连接（如使用密码则改为 md5/scram-sha-256）：

```conf
# TYPE  DATABASE     USER      ADDRESS          METHOD
local   all          all                        trust
host    all          all       127.0.0.1/32     trust
```

修改后重启 PostgreSQL：

```bash
# macOS
brew services restart postgresql@15

# Ubuntu
sudo systemctl restart postgresql
```

### 阶段2：配置 frp 隧道

每个租户需要一条独立的 frp 隧道，将客户本机的 PostgreSQL 5432 端口映射到服务器的一个远程端口。

#### 2.1 服务器端 frps 配置

服务器上已运行 frps，配置文件位于 `/etc/frp/frps.toml`：

```toml
bindAddr = "0.0.0.0"
bindPort = 7000
auth.token = "frp_gitea_2026"
```

**无需修改**，frps 会自动接受来自 frpc 的连接。只需为每个租户分配一个不冲突的远程端口。

#### 2.2 分配远程端口

端口分配规则：

| 租户 | tenant_id | 远程端口 | 用途 |
|------|-----------|----------|------|
| 西部马华 | tenant_a | 15432 | 已占用 |
| 新租户B | tenant_b | 16001 | 新分配 |
| 新租户C | tenant_c | 16002 | 新分配 |
| ... | ... | ... | 递增 |

> **注意**：端口 15432 已被现有租户使用。新租户从 16001 开始分配。

#### 2.3 客户本机安装 frpc

```bash
# macOS
brew install frp

# 或手动下载
# https://github.com/fatedier/frp/releases
# 下载对应平台的 frp，解压后取 frpc 可执行文件
```

#### 2.4 客户本机配置 frpc

创建配置文件 `/etc/frp/frpc.toml`（或 `~/.frp/frpc.toml`）：

```toml
serverAddr = "dm.all8ai.top"
serverPort = 7000
auth.token = "frp_gitea_2026"
loginFailExit = false

[[proxies]]
name = "tenant_b-db"
type = "tcp"
localIP = "127.0.0.1"
localPort = 5432
remotePort = 16001
```

> - `name`：必须唯一，建议用 `租户ID-db` 格式
> - `remotePort`：使用分配的远程端口（如 16001）
> - `loginFailExit = false`：断线自动重连

#### 2.5 启动 frpc

```bash
# 前台运行（测试用）
frpc -c /etc/frp/frpc.toml

# 后台运行（生产用）
nohup frpc -c /etc/frp/frpc.toml > /var/log/frpc.log 2>&1 &

# macOS 可用 brew services
brew services start frp
```

#### 2.6 验证隧道连通

在服务器上测试远程端口是否可达：

```bash
# 在服务器上执行
ssh ubuntu@152.136.182.184 "ss -tlnp | grep 16001"
# 应看到 LISTEN 状态

# 测试数据库连接
ssh ubuntu@152.136.182.184 "psql -h 127.0.0.1 -p 16001 -U freedak -d bill_query -c 'SELECT 1;'"
# 应返回 1
```

### 阶段3：服务器后台创建租户配置

隧道连通后，在系统中注册租户。

#### 3.1 平台管理员登录

- **入口**：https://dm.all8ai.top/login
- **用户名**：`平台管理员`
- **密码**：初始密码见部署配置
- **登录后**：自动跳转到「租户管理」页面

#### 3.2 创建租户

在「租户管理」页面点击「新建租户」，填写以下信息：

| 字段 | 说明 | 示例 |
|------|------|------|
| tenant_id | 租户唯一标识（英文，不可改） | `tenant_b` |
| tenant_name | 租户显示名称 | `B品牌连锁` |
| db_host | 业务数据库地址（固定） | `127.0.0.1` |
| db_port | frp 远程端口 | `16001` |
| db_name | 业务数据库名（固定） | `bill_query` |
| db_user | 数据库用户 | `freedak` |
| db_password | 数据库密码（空则不传） | （留空） |
| frp_port | FRP 隧道端口（同 db_port） | `16001` |

> **关键**：`db_port` 和 `frp_port` 必须填 frp 分配的远程端口（如 16001），不是客户本机的 5432。

#### 3.3 激活租户

创建后默认状态为 `inactive`，需手动改为 `active`。

#### API 方式（阶段3完整示例）

```bash
# 平台管理员登录获取 token
ADMIN_TOKEN=$(curl -s -X POST http://localhost:9333/api/admin/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"平台管理员","password":"xxx"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['token'])")

# 创建租户
curl -s -X POST http://localhost:9333/api/admin/tenants \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "tenant_id": "tenant_b",
    "tenant_name": "B品牌连锁",
    "db_host": "127.0.0.1",
    "db_port": 16001,
    "db_name": "bill_query",
    "db_user": "freedak",
    "db_password": "",
    "frp_port": 16001
  }'

# 激活租户
curl -s -X PUT http://localhost:9333/api/admin/tenants/tenant_b \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"status": "active"}'

# 验证数据库连接
curl -s http://localhost:9333/api/admin/tenants/tenant_b/status \
  -H "Authorization: Bearer $ADMIN_TOKEN"
# 应返回 {"success":true,"data":{"connected":true}}
```

### 阶段4：创建用户账号

租户激活后，创建用户账号。详见下方「一、平台管理员操作」和「二、租户管理员操作」。

---

## 一、平台管理员操作

### 1.1 登录

- **入口**：https://dm.all8ai.top/login
- **用户名**：`平台管理员`
- **密码**：初始密码见部署配置
- **登录后**：自动跳转到「租户管理」页面

### 1.2 创建新租户

> **前置条件**：客户本机已完成数据库准备和 frp 隧道配置（见「〇、新租户接入全流程」阶段1和阶段2），服务器上对应端口已可连通。

在「租户管理」页面点击「新建租户」，填写以下信息：

| 字段 | 说明 | 示例 |
|------|------|------|
| tenant_id | 租户唯一标识（英文，不可改） | `tenant_b` |
| tenant_name | 租户显示名称 | `B品牌连锁` |
| db_host | 业务数据库地址（固定） | `127.0.0.1` |
| db_port | frp 远程端口（见阶段2分配） | `16001` |
| db_name | 业务数据库名（固定） | `bill_query` |
| db_user | 数据库用户 | `freedak` |
| db_password | 数据库密码（空则不传） | （留空） |
| frp_port | FRP 隧道端口（同 db_port） | `16001` |

> **关键**：`db_port` 和 `frp_port` 必须填 frp 分配的远程端口（如 16001），不是客户本机的 5432。

**创建后默认状态为 `inactive`（未激活），需手动激活为 `active`。**

#### API 方式

```bash
# 平台管理员登录获取 token
ADMIN_TOKEN=$(curl -s -X POST http://localhost:9333/api/admin/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"平台管理员","password":"xxx"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['token'])")

# 创建租户
curl -s -X POST http://localhost:9333/api/admin/tenants \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "tenant_id": "tenant_b",
    "tenant_name": "B品牌连锁",
    "db_host": "127.0.0.1",
    "db_port": 16001,
    "db_name": "bill_query",
    "db_user": "freedak",
    "db_password": "",
    "frp_port": 16001
  }'

# 激活租户
curl -s -X PUT http://localhost:9333/api/admin/tenants/tenant_b \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"status": "active"}'

# 验证数据库连接
curl -s http://localhost:9333/api/admin/tenants/tenant_b/status \
  -H "Authorization: Bearer $ADMIN_TOKEN"
# 应返回 {"success":true,"data":{"connected":true}}
```

### 1.3 为租户创建总部管理员（hq）

平台管理员为租户创建第一个 hq 用户，后续用户由 hq 自行管理：

#### 界面操作

在「租户管理」页面 → 点击租户 → 「用户」标签 → 「添加用户」

#### API 方式

```bash
curl -s -X POST http://localhost:9333/api/admin/tenants/tenant_b/users \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "brand_b_admin",
    "password": "123",
    "role": "hq",
    "name": "B品牌管理员"
  }'
```

> **注意**：平台管理员创建用户时不包含 `store_code`/`region`/`dept` 字段。如需设置，由租户管理员（hq）登录后通过用户管理页面编辑。

---

## 二、租户管理员（hq）操作

### 2.1 登录

- **入口**：https://dm.all8ai.top/login
- **用户名**：由平台管理员创建的 hq 账号
- **登录后**：自动跳转到「总部驾驶舱」

### 2.2 用户管理

进入「系统管理」→「用户管理」页面，可执行以下操作：

- **添加用户**：创建区域经理、店长、商品部账号
- **编辑关联**：修改用户关联的门店/区域
- **修改密码**：重置用户密码
- **删除用户**：移除用户账号

### 2.3 创建区域经理（regional）

1. 点击「添加用户」
2. 填写信息：
   - **用户名**：如 `haidian_manager`
   - **密码**：如 `123`
   - **角色**：选择「区域经理」
   - **姓名**：如 `海淀区区域经理`
3. **管辖门店**：在门店列表中勾选该区域经理管辖的门店（多选）
   - `region` 字段存储的是**逗号分隔的门店编码**，如 `0016,0018,0020,...`
   - 可通过搜索框按门店名称或区域筛选
4. 点击「添加」

#### API 方式

```bash
# hq 登录获取 token
HQ_TOKEN=$(curl -s -X POST http://localhost:9333/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"brand_b_admin","password":"123"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['token'])")

# 创建区域经理（region 字段填逗号分隔的门店编码）
curl -s -X POST http://localhost:9333/api/admin/tenant/users \
  -H "Authorization: Bearer $HQ_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "haidian_manager",
    "password": "123",
    "role": "regional",
    "name": "海淀区区域经理",
    "region": "0016,0018,0020,0022,0024,0025,0028,0029,0032,0049,0054,0059,..."
  }'
```

### 2.4 创建店长（store）

1. 点击「添加用户」
2. 填写信息：
   - **用户名**：如 `wanquanhe_manager`
   - **密码**：如 `123`
   - **角色**：选择「店长」
   - **姓名**：如 `万泉河店长`
3. **所属门店**：在门店列表中勾选该店长管理的门店（可多选）
   - `store_code` 字段存储逗号分隔的门店编码，如 `0016`
4. 点击「添加」

#### API 方式

```bash
curl -s -X POST http://localhost:9333/api/admin/tenant/users \
  -H "Authorization: Bearer $HQ_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "wanquanhe_manager",
    "password": "123",
    "role": "store",
    "name": "万泉河店长",
    "store_code": "0016"
  }'
```

### 2.5 创建商品部（dept）

1. 点击「添加用户」
2. 填写信息：
   - **用户名**：如 `product_dept`
   - **密码**：如 `123`
   - **角色**：选择「商品部」
   - **姓名**：如 `商品部`
3. **部门**：填写部门名称，如 `商品部`
4. 点击「添加」

#### API 方式

```bash
curl -s -X POST http://localhost:9333/api/admin/tenant/users \
  -H "Authorization: Bearer $HQ_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "product_dept",
    "password": "123",
    "role": "dept",
    "name": "商品部",
    "dept": "商品部"
  }'
```

### 2.6 编辑用户关联门店

创建用户后如需修改关联门店：

1. 在用户列表中点击「编辑」图标
2. 重新勾选/取消勾选门店
3. 点击「保存」

#### API 方式

```bash
# 修改用户关联的门店编码
curl -s -X PUT http://localhost:9333/api/admin/tenant/users/5 \
  -H "Authorization: Bearer $HQ_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "store_code": "0016,0018",
    "region": "",
    "dept": ""
  }'
```

### 2.7 修改用户密码

1. 在用户列表中点击「钥匙」图标
2. 输入新密码
3. 点击「确认修改」

#### API 方式

```bash
curl -s -X PUT http://localhost:9333/api/admin/tenant/users/5/password \
  -H "Authorization: Bearer $HQ_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"password": "newpass"}'
```

---

## 三、数据范围机制

### 3.1 字段说明

| 角色 | 字段 | 存储内容 | 示例 |
|------|------|----------|------|
| hq / dept | （无） | 不做数据过滤 | - |
| regional | `region` | 逗号分隔的门店编码 | `0016,0018,0020,...` |
| store | `store_code` | 逗号分隔的门店编码 | `0016` |

> **重要**：`region` 字段存储的是**门店编码（store_code）**，不是区域名称。系统通过 `store_code = ANY(...)` 进行 SQL 过滤。

### 3.2 过滤逻辑

- **hq / dept**：无过滤，可查看本租户全部数据
- **regional**：所有 API 查询自动注入 `store_code = ANY(门店编码列表)`
- **store**：所有 API 查询自动注入 `store_code = ANY(门店编码列表)`

### 3.3 菜单权限

| 菜单 | hq | dept | regional | store |
|------|----|------|----------|-------|
| 老板驾驶舱 | ✅ | ✅ | - | - |
| 总部驾驶舱 | ✅ | ✅ | - | - |
| 态势感知 | ✅ | ✅ | ✅ | ✅ |
| 银行授信 | ✅ | - | - | - |
| 区域经理 | ✅ | ✅ | ✅ | - |
| 区域对比 | ✅ | ✅ | ✅ | - |
| 店长工作台 | ✅ | ✅ | - | ✅ |
| 任务管理 | ✅ | ✅ | ✅ | ✅ |
| 月度验收 | ✅ | ✅ | ✅ | - |
| 营收分析 | ✅ | ✅ | - | - |
| 平台优惠 | ✅ | ✅ | - | - |
| 会员复购 | ✅ | ✅ | - | - |
| 会员LTV | ✅ | ✅ | - | - |
| 菜品成本 | ✅ | ✅ | - | - |
| 成本库存 | ✅ | ✅ | - | - |
| 库存周转 | ✅ | ✅ | - | - |
| 中央厨房 | ✅ | ✅ | - | - |
| 配送对账 | ✅ | ✅ | - | - |
| BOM穿透 | ✅ | ✅ | - | - |
| 生产要货 | ✅ | ✅ | - | - |
| 门店费用 | ✅ | ✅ | - | - |
| 商品SKU | ✅ | ✅ | - | - |
| 菜单工程 | ✅ | ✅ | - | - |
| 时间分析 | ✅ | ✅ | - | - |
| 智能排班 | ✅ | ✅ | ✅ | ✅ |
| 员工绩效 | ✅ | ✅ | - | - |
| 风险内控 | ✅ | ✅ | - | - |
| 门店选址 | ✅ | ✅ | - | - |
| 用户管理 | ✅ | - | - | - |
| 数据质量 | ✅ | ✅ | - | - |
| 指标字典 | ✅ | ✅ | ✅ | ✅ |
| 本体标准 | ✅ | ✅ | - | - |

前端路由守卫（RoleRoute）确保用户无法通过直接输入 URL 访问无权限的页面。

---

## 四、完整操作示例

### 场景：为新连锁品牌「B品牌」配置全套账号

#### 步骤 0：客户本机准备数据库 + frp 隧道

```bash
# === 在客户本机执行 ===

# 1. 创建数据库
psql postgres -c "CREATE USER freedak SUPERUSER;"
psql postgres -c "CREATE DATABASE bill_query OWNER freedak;"

# 2. 导入业务数据
pg_restore -U freedak -d bill_query -1 /path/to/backup.dump

# 3. 配置 frpc
cat > /etc/frp/frpc.toml << 'EOF'
serverAddr = "dm.all8ai.top"
serverPort = 7000
auth.token = "frp_gitea_2026"
loginFailExit = false

[[proxies]]
name = "tenant_b-db"
type = "tcp"
localIP = "127.0.0.1"
localPort = 5432
remotePort = 16001
EOF

# 4. 启动 frpc
nohup frpc -c /etc/frp/frpc.toml > /var/log/frpc.log 2>&1 &

# === 在服务器上验证 ===
ssh ubuntu@152.136.182.184 "ss -tlnp | grep 16001"
# 应看到 LISTEN 状态
```

#### 步骤 1：平台管理员创建租户

```bash
ADMIN_TOKEN=...  # 平台管理员 token

# 创建租户（db_port 和 frp_port 使用 frp 远程端口 16001）
curl -X POST http://localhost:9333/api/admin/tenants \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "tenant_id": "tenant_b",
    "tenant_name": "B品牌连锁",
    "db_host": "127.0.0.1",
    "db_port": 16001,
    "db_name": "bill_query",
    "db_user": "freedak",
    "db_password": "",
    "frp_port": 16001
  }'

# 激活租户
curl -X PUT http://localhost:9333/api/admin/tenants/tenant_b \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"status": "active"}'

# 验证数据库连接
curl -s http://localhost:9333/api/admin/tenants/tenant_b/status \
  -H "Authorization: Bearer $ADMIN_TOKEN"
# 应返回 {"success":true,"data":{"connected":true}}

# 创建总部管理员
curl -X POST http://localhost:9333/api/admin/tenants/tenant_b/users \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "brand_b_admin",
    "password": "123",
    "role": "hq",
    "name": "B品牌管理员"
  }'
```

#### 步骤 2：总部管理员创建区域经理和店长

```bash
HQ_TOKEN=...  # brand_b_admin 的 token

# 查询可用门店列表
curl http://localhost:9333/api/admin/tenant/stores \
  -H "Authorization: Bearer $HQ_TOKEN"

# 创建朝阳区区域经理（管辖门店 0001~0010）
curl -X POST http://localhost:9333/api/admin/tenant/users \
  -H "Authorization: Bearer $HQ_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "chaoyang_manager",
    "password": "123",
    "role": "regional",
    "name": "朝阳区区域经理",
    "region": "0001,0002,0003,0004,0005,0006,0007,0008,0009,0010"
  }'

# 创建店长（管理 0001 号门店）
curl -X POST http://localhost:9333/api/admin/tenant/users \
  -H "Authorization: Bearer $HQ_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "store_0001",
    "password": "123",
    "role": "store",
    "name": "001号店长",
    "store_code": "0001"
  }'

# 创建商品部
curl -X POST http://localhost:9333/api/admin/tenant/users \
  -H "Authorization: Bearer $HQ_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "product_dept",
    "password": "123",
    "role": "dept",
    "name": "商品部",
    "dept": "商品部"
  }'
```

#### 步骤 3：验证登录

| 用户名 | 密码 | 角色 | 预期首页 | 数据范围 |
|--------|------|------|----------|----------|
| brand_b_admin | 123 | hq | 总部驾驶舱 | 全部门店 |
| chaoyang_manager | 123 | regional | 区域经理工作台 | 10 家朝阳区门店 |
| store_0001 | 123 | store | 店长工作台 | 仅 0001 号门店 |
| product_dept | 123 | dept | 总部驾驶舱 | 全部门店 |

---

## 五、注意事项

1. **租户状态**：创建后默认 `inactive`，必须激活为 `active` 后用户才能登录
2. **门店编码**：`region` 和 `store_code` 字段存储的是 `dim_store` 表中的 `store_code` 值，不是门店名称
3. **多选门店**：店长和区域经理都可以关联多个门店，用逗号分隔
4. **用户名唯一**：同一租户内用户名不能重复
5. **密码安全**：密码使用 bcrypt 加密存储，无法逆向查看
6. **路由守卫**：前端通过 RoleRoute 组件阻止越权访问，后端通过 data-scope 中间件过滤数据
7. **删除用户**：删除用户不会影响已产生的业务数据
8. **frp 隧道**：客户本机必须保持 frpc 运行，否则该租户所有 API 不可用。建议配置 `loginFailExit = false` 自动重连
9. **frp 端口**：每个租户必须使用不同的远程端口，端口 15432 已被现有租户占用，新租户从 16001 开始递增
10. **数据库密码**：如客户本机 PostgreSQL 使用密码认证（非 trust），需在创建租户时填写 `db_password`
11. **数据库离线**：客户本机关机/休眠会导致该租户 API 不可用，前端会显示错误提示
12. **数据导入**：新租户的业务数据库必须导入完整的表结构和数据（analytics schema、dim_store 等），否则 API 会报错
