Files
SBrainCO/docs/租户配置与用户管理指南.md
T
freedakgmail c15ca07b7c docs: 新增租户配置与用户管理指南
涵盖新租户接入全流程(数据库创建、frp隧道配置、租户注册、用户创建)、
角色权限说明、菜单权限对照表、完整API示例
2026-08-02 21:40:40 +08:00

740 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 租户配置与用户管理指南
## 概述
系统采用多租户架构,平台管理员可创建租户(连锁品牌),每个租户有独立的业务数据库。租户管理员(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 会报错