2026-03-28 22:52:12 +08:00
|
|
|
|
# 用户层级架构说明
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
> **文档版本**: 1.2
|
|
|
|
|
|
> **最后更新**: 2026-03-29
|
|
|
|
|
|
> **更新内容**: 根据实际代码更新表名、字段定义、数据范围类型、服务方法签名、权限点设计,删除不存在的表和服务引用
|
|
|
|
|
|
> **适用范围**: Crawlful Hub 多租户用户管理体系
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-03-28 22:52:12 +08:00
|
|
|
|
## 1. 多租户层级结构
|
|
|
|
|
|
|
|
|
|
|
|
Crawlful Hub 采用多层级的用户架构设计,确保数据隔离和权限控制:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
平台(Platform)
|
|
|
|
|
|
↓
|
|
|
|
|
|
商户/公司(Tenant/Organization)
|
|
|
|
|
|
↓
|
|
|
|
|
|
组织结构(Org Tree)
|
|
|
|
|
|
├── 部门A(主管)
|
|
|
|
|
|
│ ├── 组A1(组长)
|
|
|
|
|
|
│ │ ├── 员工1
|
|
|
|
|
|
│ │ └── 员工2
|
|
|
|
|
|
│ └── 组A2(组长)
|
|
|
|
|
|
│ └── 员工3
|
|
|
|
|
|
└── 部门B(主管)
|
|
|
|
|
|
└── ...
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 1.1 层级关系说明
|
|
|
|
|
|
|
|
|
|
|
|
| 层级 | 英文 | 描述 | 数据隔离级别 |
|
|
|
|
|
|
|------|------|------|-------------|
|
|
|
|
|
|
| **平台** | Platform | 系统运营方,管理所有租户 | 全局管理 |
|
|
|
|
|
|
| **租户** | Tenant | 企业/商户,拥有独立数据空间 | 完全隔离 |
|
|
|
|
|
|
| **部门** | Department | 租户内的组织结构 | 层级隔离 |
|
|
|
|
|
|
| **店铺** | Shop | 具体电商平台的店铺 | 业务隔离 |
|
|
|
|
|
|
| **用户** | User | 系统使用者 | 权限控制 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-03-28 22:52:12 +08:00
|
|
|
|
## 2. 核心层级模型
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 2.1 层级关系详解
|
|
|
|
|
|
|
|
|
|
|
|
#### 商户 (Merchant/Tenant)
|
|
|
|
|
|
- **定义**: 最高业务层级,代表一个独立的企业或组织
|
|
|
|
|
|
- **属性**: 商户ID、名称、认证状态、计费套餐、配额限制
|
|
|
|
|
|
- **管理**: 由平台管理员创建,商户管理员管理
|
|
|
|
|
|
- **数据**: 所有数据归属于特定租户,完全隔离
|
|
|
|
|
|
|
|
|
|
|
|
#### 部门 (Department)
|
|
|
|
|
|
- **定义**: 商户内的组织结构单元
|
|
|
|
|
|
- **属性**: 部门ID、名称、父部门ID、层级路径(path)、深度(depth)
|
|
|
|
|
|
- **管理**: 商户管理员创建和管理部门层级
|
|
|
|
|
|
- **特性**: 支持多级嵌套,形成树形结构
|
|
|
|
|
|
|
|
|
|
|
|
#### 店铺 (Shop)
|
|
|
|
|
|
- **定义**: 具体的电商平台店铺(如Amazon、TikTok店铺)
|
|
|
|
|
|
- **属性**: 店铺ID、平台类型、授权状态、所属部门
|
|
|
|
|
|
- **管理**: 可分配给特定部门或用户组
|
|
|
|
|
|
- **隔离**: 店铺间操作互不影响
|
|
|
|
|
|
|
|
|
|
|
|
#### 用户 (User)
|
|
|
|
|
|
- **定义**: 系统的实际使用者
|
|
|
|
|
|
- **属性**: 用户ID、用户名、邮箱、角色、所属租户/部门
|
|
|
|
|
|
- **状态**: active(活跃)、inactive(未激活)、suspended(暂停)、deleted(已删除)
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 数据隔离机制
|
|
|
|
|
|
|
|
|
|
|
|
#### 租户隔离
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 所有业务表必须包含 tenant_id 字段
|
|
|
|
|
|
SELECT * FROM cf_products WHERE tenant_id = 'tenant_xxx';
|
|
|
|
|
|
SELECT * FROM cf_orders WHERE tenant_id = 'tenant_xxx';
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 部门隔离
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 通过部门路径实现层级数据访问
|
|
|
|
|
|
SELECT * FROM cf_user
|
|
|
|
|
|
WHERE tenant_id = 'tenant_xxx'
|
|
|
|
|
|
AND dept_path LIKE '/dept_a/%';
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 店铺隔离
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 店铺级别数据过滤
|
|
|
|
|
|
SELECT * FROM cf_products
|
|
|
|
|
|
WHERE tenant_id = 'tenant_xxx'
|
|
|
|
|
|
AND shop_id = 'shop_xxx';
|
|
|
|
|
|
```
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
---
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
|
|
|
|
|
## 3. 权限系统(RBAC)
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 3.1 系统级角色定义
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
| 角色 | 英文 | 描述 | 权限范围 |
|
|
|
|
|
|
|------|------|------|----------|
|
|
|
|
|
|
| **ADMIN** | 系统管理员 | 平台运营方 | 所有资源的所有操作 |
|
|
|
|
|
|
| **MANAGER** | 运营主管 | 租户内高级管理 | 租户内大部分资源的管理操作 |
|
|
|
|
|
|
| **OPERATOR** | 运营专员 | 日常运营人员 | 基础运营操作(商品、订单) |
|
|
|
|
|
|
| **FINANCE** | 财务主管 | 财务人员 | 财务相关操作(账单、结算) |
|
|
|
|
|
|
| **SOURCING** | 采购专家 | 采购人员 | 采购相关操作(供应商、选品) |
|
|
|
|
|
|
| **LOGISTICS** | 物流专家 | 物流人员 | 物流相关操作(发货、跟踪) |
|
|
|
|
|
|
| **ANALYST** | 数据分析师 | 数据分析人员 | 数据分析相关操作(报表、洞察) |
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 3.2 店铺级角色定义
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
| 角色 | 描述 | 权限范围 | 适用场景 |
|
|
|
|
|
|
|------|------|----------|----------|
|
|
|
|
|
|
| **owner** | 拥有者 | 删除店铺、管理授权、管理成员、所有权限 | 店铺创建者 |
|
|
|
|
|
|
| **admin** | 管理员 | 管理商品、管理价格、管理订单、不可删除店铺 | 店铺运营主管 |
|
|
|
|
|
|
| **operator** | 运营 | 刊登、改价、查看数据 | 日常运营人员 |
|
|
|
|
|
|
| **viewer** | 只读 | 查看数据、不可操作 | 仅查看报表人员 |
|
|
|
|
|
|
|
|
|
|
|
|
### 3.3 权限点设计
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// 权限对象结构
|
|
|
|
|
|
export interface Permission {
|
|
|
|
|
|
id: string; // 权限ID,格式:资源:操作
|
|
|
|
|
|
name: string; // 权限名称
|
|
|
|
|
|
description: string; // 权限描述
|
|
|
|
|
|
module: string; // 所属模块
|
|
|
|
|
|
action: 'READ' | 'WRITE' | 'DELETE' | 'EXECUTE'; // 操作类型
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 权限点定义(按模块划分)
|
|
|
|
|
|
const PERMISSIONS: Permission[] = [
|
|
|
|
|
|
// 系统权限
|
|
|
|
|
|
{ id: 'admin:all', name: '超级管理员', description: '拥有所有系统权限', module: 'SYSTEM', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 商品权限
|
|
|
|
|
|
{ id: 'product:read', name: '查看商品', description: '允许查看商品列表与详情', module: 'PRODUCT', action: 'READ' },
|
|
|
|
|
|
{ id: 'product:write', name: '编辑商品', description: '允许创建和修改商品信息', module: 'PRODUCT', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'product:delete', name: '删除商品', description: '允许删除商品', module: 'PRODUCT', action: 'DELETE' },
|
|
|
|
|
|
{ id: 'product:publish', name: '发布商品', description: '允许执行商品刊登任务', module: 'PRODUCT', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'product:score', name: '商品评分', description: '允许查看AI选品评分', module: 'PRODUCT', action: 'READ' },
|
|
|
|
|
|
|
|
|
|
|
|
// 订单权限
|
|
|
|
|
|
{ id: 'order:read', name: '查看订单', description: '允许查看销售订单', module: 'ORDER', action: 'READ' },
|
|
|
|
|
|
{ id: 'order:write', name: '创建订单', description: '允许创建订单', module: 'ORDER', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'order:cancel', name: '取消订单', description: '允许取消订单', module: 'ORDER', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'order:refund', name: '退款处理', description: '允许处理退款', module: 'ORDER', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 库存权限
|
|
|
|
|
|
{ id: 'inventory:read', name: '查看库存', description: '允许查看库存数据', module: 'INVENTORY', action: 'READ' },
|
|
|
|
|
|
{ id: 'inventory:write', name: '编辑库存', description: '允许调整库存', module: 'INVENTORY', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'inventory:sync', name: '库存同步', description: '允许执行跨平台库存同步', module: 'INVENTORY', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'inventory:alert', name: '库存预警', description: '允许配置库存预警', module: 'INVENTORY', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 财务权限
|
|
|
|
|
|
{ id: 'finance:read', name: '查看财务', description: '允许查看财务数据', module: 'FINANCE', action: 'READ' },
|
|
|
|
|
|
{ id: 'finance:recon', name: '财务对账', description: '允许执行自动化对账', module: 'FINANCE', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'finance:settlement', name: '结算管理', description: '允许处理结算', module: 'FINANCE', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'finance:report', name: '财务报表', description: '允许查看财务报表', module: 'FINANCE', action: 'READ' },
|
|
|
|
|
|
|
|
|
|
|
|
// 广告权限
|
|
|
|
|
|
{ id: 'ad:read', name: '查看广告', description: '允许查看广告数据', module: 'ADVERTISING', action: 'READ' },
|
|
|
|
|
|
{ id: 'ad:write', name: '编辑广告', description: '允许创建和修改广告', module: 'ADVERTISING', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'ad:launch', name: '投放广告', description: '允许执行广告投放', module: 'ADVERTISING', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'ad:optimize', name: '广告优化', description: '允许执行AI广告优化', module: 'ADVERTISING', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 采购权限
|
|
|
|
|
|
{ id: 'sourcing:read', name: '查看采购', description: '允许查看采购数据', module: 'SOURCING', action: 'READ' },
|
|
|
|
|
|
{ id: 'sourcing:write', name: '编辑采购', description: '允许创建采购单', module: 'SOURCING', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'sourcing:approve', name: '采购审批', description: '允许审批采购单', module: 'SOURCING', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 物流权限
|
|
|
|
|
|
{ id: 'logistics:read', name: '查看物流', description: '允许查看物流数据', module: 'LOGISTICS', action: 'READ' },
|
|
|
|
|
|
{ id: 'logistics:write', name: '编辑物流', description: '允许配置物流', module: 'LOGISTICS', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'logistics:track', name: '物流追踪', description: '允许追踪物流状态', module: 'LOGISTICS', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// AI权限
|
|
|
|
|
|
{ id: 'ai:read', name: '查看AI分析', description: '允许查看AI分析结果', module: 'AI', action: 'READ' },
|
|
|
|
|
|
{ id: 'ai:scoring', name: 'AI选品评分', description: '允许执行AI选品评分', module: 'AI', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'ai:arbitrage', name: '套利识别', description: '允许执行套利机会识别', module: 'AI', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'ai:pricing', name: '智能定价', description: '允许执行智能定价建议', module: 'AI', action: 'EXECUTE' },
|
|
|
|
|
|
{ id: 'ai:monitor', name: '价格监控', description: '允许配置竞争对手价格监控', module: 'AI', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 采集权限
|
|
|
|
|
|
{ id: 'collection:read', name: '查看采集', description: '允许查看采集任务', module: 'COLLECTION', action: 'READ' },
|
|
|
|
|
|
{ id: 'collection:write', name: '编辑采集', description: '允许创建采集任务', module: 'COLLECTION', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'collection:execute', name: '执行采集', description: '允许执行采集任务', module: 'COLLECTION', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 用户权限
|
|
|
|
|
|
{ id: 'user:read', name: '查看用户', description: '允许查看用户列表', module: 'USER', action: 'READ' },
|
|
|
|
|
|
{ id: 'user:write', name: '编辑用户', description: '允许创建和修改用户', module: 'USER', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'user:role', name: '角色管理', description: '允许分配用户角色', module: 'USER', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 租户权限
|
|
|
|
|
|
{ id: 'tenant:read', name: '查看租户', description: '允许查看租户信息', module: 'TENANT', action: 'READ' },
|
|
|
|
|
|
{ id: 'tenant:write', name: '编辑租户', description: '允许修改租户配置', module: 'TENANT', action: 'WRITE' },
|
|
|
|
|
|
{ id: 'tenant:config', name: '租户配置', description: '允许配置租户设置', module: 'TENANT', action: 'EXECUTE' },
|
|
|
|
|
|
|
|
|
|
|
|
// 审计权限
|
|
|
|
|
|
{ id: 'audit:read', name: '查看审计', description: '允许查看审计日志', module: 'AUDIT', action: 'READ' },
|
|
|
|
|
|
{ id: 'audit:export', name: '导出审计', description: '允许导出审计日志', module: 'AUDIT', action: 'EXECUTE' },
|
|
|
|
|
|
];
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
|
|
|
|
|
## 4. 数据范围控制
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 数据范围类型
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
| 范围类型 | 英文 | 说明 | SQL过滤条件 | 适用角色 |
|
|
|
|
|
|
|---------|------|------|------------|---------|
|
|
|
|
|
|
| **OWN** | Own | 只看自己创建的数据 | `WHERE created_by = {userId}` | 普通员工 |
|
|
|
|
|
|
| **SHOP** | Shop | 看自己店铺的数据 | `WHERE shop_id IN ({userShops})` | 店铺管理员 |
|
|
|
|
|
|
| **DEPT** | Department | 看自己部门的数据 | `WHERE dept_id IN ({userDepts})` | 部门主管 |
|
|
|
|
|
|
| **ORG** | Organization | 看整个公司的数据 | `WHERE tenant_id = {tenantId}` | 高管 |
|
|
|
|
|
|
| **ALL** | All | 全平台数据(超管) | 无过滤 | 系统管理员 |
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 4.2 数据范围实现
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
// 数据范围接口
|
|
|
|
|
|
export interface DataScope {
|
|
|
|
|
|
type: 'ALL' | 'ORG' | 'DEPT' | 'SHOP' | 'OWN';
|
|
|
|
|
|
departmentId?: string;
|
|
|
|
|
|
shopId?: string;
|
|
|
|
|
|
}
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
// 数据隔离服务
|
|
|
|
|
|
export class DataIsolationService {
|
|
|
|
|
|
static async getUserDataScope(userId: string): Promise<DataScope> {
|
|
|
|
|
|
const user = await UserService.getUserById(userId);
|
|
|
|
|
|
const role = user.role;
|
|
|
|
|
|
|
|
|
|
|
|
switch(role) {
|
|
|
|
|
|
case 'ADMIN':
|
|
|
|
|
|
return { type: 'ALL' };
|
|
|
|
|
|
case 'MANAGER':
|
|
|
|
|
|
return { type: 'ORG', tenantId: user.tenantId };
|
|
|
|
|
|
case 'DEPT_MANAGER':
|
|
|
|
|
|
const depts = await DepartmentService.getSubDepartments(user.deptId);
|
|
|
|
|
|
return { type: 'DEPT', deptIds: depts.map(d => d.id) };
|
|
|
|
|
|
default:
|
|
|
|
|
|
return { type: 'OWN', userId };
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static applyDataScope(query: Knex.QueryBuilder, scope: DataScope): Knex.QueryBuilder {
|
|
|
|
|
|
switch(scope.type) {
|
|
|
|
|
|
case 'OWN':
|
|
|
|
|
|
return query.where('created_by', scope.userId);
|
|
|
|
|
|
case 'SHOP':
|
|
|
|
|
|
return query.whereIn('shop_id', scope.shopIds);
|
|
|
|
|
|
case 'DEPT':
|
|
|
|
|
|
return query.whereIn('dept_id', scope.deptIds);
|
|
|
|
|
|
case 'ORG':
|
|
|
|
|
|
return query.where('tenant_id', scope.tenantId);
|
|
|
|
|
|
case 'ALL':
|
|
|
|
|
|
default:
|
|
|
|
|
|
return query;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-28 22:52:12 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 4.3 授权模型
|
|
|
|
|
|
|
|
|
|
|
|
**核心原则**:
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-03-28 22:52:12 +08:00
|
|
|
|
## 5. 技术实现
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 数据模型
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
#### 用户表 (`cf_user`)
|
|
|
|
|
|
```sql
|
|
|
|
|
|
CREATE TABLE cf_user (
|
|
|
|
|
|
id VARCHAR(36) PRIMARY KEY,
|
|
|
|
|
|
tenant_id VARCHAR(36) NOT NULL,
|
|
|
|
|
|
username VARCHAR(64) NOT NULL UNIQUE,
|
|
|
|
|
|
email VARCHAR(128) NOT NULL UNIQUE,
|
|
|
|
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
|
|
|
|
full_name VARCHAR(128) NOT NULL,
|
|
|
|
|
|
phone VARCHAR(32),
|
|
|
|
|
|
status ENUM('ACTIVE', 'INACTIVE', 'LOCKED') DEFAULT 'ACTIVE',
|
|
|
|
|
|
role ENUM('ADMIN', 'MANAGER', 'OPERATOR', 'FINANCE', 'SOURCING', 'LOGISTICS', 'ANALYST') NOT NULL,
|
|
|
|
|
|
is_superadmin BOOLEAN DEFAULT FALSE,
|
|
|
|
|
|
last_login_by VARCHAR(36),
|
|
|
|
|
|
last_login_at TIMESTAMP,
|
|
|
|
|
|
created_by VARCHAR(36) NOT NULL,
|
|
|
|
|
|
updated_by VARCHAR(36) NOT NULL,
|
|
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
|
|
INDEX idx_tenant (tenant_id),
|
|
|
|
|
|
INDEX idx_email (email),
|
|
|
|
|
|
FOREIGN KEY (tenant_id) REFERENCES cf_tenant(id) ON DELETE CASCADE
|
|
|
|
|
|
);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 租户表 (`cf_tenant`)
|
|
|
|
|
|
```sql
|
|
|
|
|
|
CREATE TABLE cf_tenant (
|
|
|
|
|
|
id VARCHAR(36) PRIMARY KEY,
|
|
|
|
|
|
name VARCHAR(128) NOT NULL UNIQUE,
|
|
|
|
|
|
domain VARCHAR(128) NOT NULL UNIQUE,
|
|
|
|
|
|
contact_name VARCHAR(64) NOT NULL,
|
|
|
|
|
|
contact_email VARCHAR(128) NOT NULL UNIQUE,
|
|
|
|
|
|
contact_phone VARCHAR(32) NOT NULL,
|
|
|
|
|
|
address VARCHAR(255) NOT NULL,
|
|
|
|
|
|
status ENUM('ACTIVE', 'INACTIVE', 'SUSPENDED') DEFAULT 'ACTIVE',
|
|
|
|
|
|
quota_limit DECIMAL(10, 2) DEFAULT 10000,
|
|
|
|
|
|
quota_used DECIMAL(10, 2) DEFAULT 0,
|
|
|
|
|
|
created_by VARCHAR(36) NOT NULL,
|
|
|
|
|
|
updated_by VARCHAR(36) NOT NULL,
|
|
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
|
|
INDEX (domain),
|
|
|
|
|
|
INDEX (status)
|
|
|
|
|
|
);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 部门表 (`cf_department`)
|
|
|
|
|
|
```sql
|
|
|
|
|
|
CREATE TABLE cf_department (
|
|
|
|
|
|
id VARCHAR(36) PRIMARY KEY,
|
|
|
|
|
|
tenant_id VARCHAR(36) NOT NULL,
|
|
|
|
|
|
name VARCHAR(128) NOT NULL,
|
|
|
|
|
|
parent_id VARCHAR(36),
|
|
|
|
|
|
path VARCHAR(500),
|
|
|
|
|
|
depth INT DEFAULT 0,
|
|
|
|
|
|
manager_id VARCHAR(36),
|
|
|
|
|
|
status ENUM('ACTIVE', 'INACTIVE') DEFAULT 'ACTIVE',
|
|
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
|
|
FOREIGN KEY (tenant_id) REFERENCES cf_tenant(id),
|
|
|
|
|
|
INDEX idx_tenant_path (tenant_id, path)
|
|
|
|
|
|
);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 角色权限表 (`cf_role_permission`)
|
|
|
|
|
|
```sql
|
|
|
|
|
|
CREATE TABLE cf_role_permission (
|
|
|
|
|
|
id VARCHAR(36) PRIMARY KEY,
|
|
|
|
|
|
role_id VARCHAR(36) NOT NULL,
|
|
|
|
|
|
permission_id VARCHAR(36) NOT NULL,
|
|
|
|
|
|
created_by VARCHAR(36) NOT NULL,
|
|
|
|
|
|
updated_by VARCHAR(36) NOT NULL,
|
|
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
|
|
UNIQUE KEY uk_role_perm (role_id, permission_id),
|
|
|
|
|
|
FOREIGN KEY (role_id) REFERENCES cf_role(id) ON DELETE CASCADE,
|
|
|
|
|
|
FOREIGN KEY (permission_id) REFERENCES cf_permission(id) ON DELETE CASCADE
|
|
|
|
|
|
);
|
|
|
|
|
|
```
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
|
|
|
|
|
### 5.2 核心服务
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
#### HierarchyService - 层级服务
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
export class HierarchyService {
|
|
|
|
|
|
// 初始化租户层级结构
|
|
|
|
|
|
static async initializeTenantHierarchy(tenantId: string): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
// 创建部门
|
|
|
|
|
|
static async createDepartment(
|
|
|
|
|
|
tenantId: string,
|
|
|
|
|
|
name: string,
|
|
|
|
|
|
parentId?: string,
|
|
|
|
|
|
managerId?: string
|
|
|
|
|
|
): Promise<Department>;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新部门
|
|
|
|
|
|
static async updateDepartment(
|
|
|
|
|
|
departmentId: string,
|
|
|
|
|
|
updates: Partial<Department>
|
|
|
|
|
|
): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
// 删除部门
|
|
|
|
|
|
static async deleteDepartment(departmentId: string, tenantId: string): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
// 分配部门经理
|
|
|
|
|
|
static async assignDepartmentManager(
|
|
|
|
|
|
departmentId: string,
|
|
|
|
|
|
managerId: string,
|
|
|
|
|
|
tenantId: string
|
|
|
|
|
|
): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取部门经理
|
|
|
|
|
|
static async getDepartmentManager(departmentId: string, tenantId: string): Promise<any>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取部门统计
|
|
|
|
|
|
static async getDepartmentStats(departmentId: string, tenantId: string): Promise<any>;
|
|
|
|
|
|
|
|
|
|
|
|
// 创建店铺
|
|
|
|
|
|
static async createShop(
|
|
|
|
|
|
tenantId: string,
|
|
|
|
|
|
departmentId: string,
|
|
|
|
|
|
shopData: Partial<Shop>
|
|
|
|
|
|
): Promise<Shop>;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新店铺
|
|
|
|
|
|
static async updateShop(shopId: string, updates: Partial<Shop>): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
// 删除店铺
|
|
|
|
|
|
static async deleteShop(shopId: string, tenantId: string): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取层级统计
|
|
|
|
|
|
static async getHierarchyStats(tenantId: string): Promise<HierarchyStats>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取部门树
|
|
|
|
|
|
static async getDepartmentTree(tenantId: string): Promise<HierarchyNode[]>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取部门下的店铺
|
|
|
|
|
|
static async getDepartmentShops(departmentId: string, tenantId: string): Promise<Shop[]>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户层级上下文
|
|
|
|
|
|
static async getUserHierarchyContext(userId: string): Promise<{
|
|
|
|
|
|
departmentId?: string;
|
|
|
|
|
|
departmentPath?: string;
|
|
|
|
|
|
shopIds: string[];
|
|
|
|
|
|
}>;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新用户层级
|
|
|
|
|
|
static async updateUserHierarchy(
|
|
|
|
|
|
userId: string,
|
|
|
|
|
|
updates: { departmentId?: string; shopIds?: string[] }
|
|
|
|
|
|
): Promise<void>;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
#### RBACService - 权限服务
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
export class RBACService {
|
|
|
|
|
|
// 创建角色
|
|
|
|
|
|
static async createRole(roleData: RoleData): Promise<Role>;
|
|
|
|
|
|
|
|
|
|
|
|
// 为角色分配权限
|
|
|
|
|
|
static async assignPermissions(roleId: string, permissions: string[]): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查用户权限
|
|
|
|
|
|
static async checkPermission(userId: string, permission: string): Promise<boolean>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户所有权限
|
|
|
|
|
|
static async getUserPermissions(userId: string): Promise<string[]>;
|
|
|
|
|
|
|
|
|
|
|
|
// 权限审计
|
|
|
|
|
|
static async auditPermissionChange(auditData: AuditData): Promise<void>;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### AuthService - 认证服务
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
export class AuthService {
|
|
|
|
|
|
// 用户登录
|
|
|
|
|
|
static async login(input: LoginInput): Promise<LoginResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 用户注册
|
|
|
|
|
|
static async register(input: RegisterInput): Promise<RegisterResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 请求密码重置
|
|
|
|
|
|
static async requestPasswordReset(input: PasswordResetInput): Promise<PasswordResetResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查权限
|
|
|
|
|
|
static async checkPermission(input: PermissionCheckInput): Promise<PermissionCheckResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户权限列表
|
|
|
|
|
|
static async getUserPermissionsList(input: UserPermissionsInput): Promise<UserPermissionsResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 分配角色
|
|
|
|
|
|
static async assignRole(input: AssignRoleInput): Promise<AssignRoleResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 验证会话
|
|
|
|
|
|
static async validateSession(input: SessionValidationInput): Promise<SessionValidationResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 刷新Token
|
|
|
|
|
|
static async refreshToken(input: RefreshTokenInput): Promise<RefreshTokenResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 登出
|
|
|
|
|
|
static async logout(input: LogoutInput): Promise<LogoutResult>;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取活跃会话
|
|
|
|
|
|
static async getActiveSessions(userId: string, tenantId: string): Promise<SessionInfo[]>;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化表
|
|
|
|
|
|
static async initializeTables(): Promise<void>;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 权限检查中间件
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2026-03-30 01:20:57 +08:00
|
|
|
|
// Express 权限检查中间件
|
|
|
|
|
|
export function requirePermission(...permissions: string[]) {
|
|
|
|
|
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
|
|
|
|
const userId = req.user?.id;
|
|
|
|
|
|
if (!userId) {
|
|
|
|
|
|
return res.status(401).json({ error: 'Unauthorized' });
|
2026-03-28 22:52:12 +08:00
|
|
|
|
}
|
2026-03-30 01:20:57 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取用户所有权限
|
|
|
|
|
|
const userPermissions = await RBACService.getUserPermissions(userId);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否拥有所需权限
|
|
|
|
|
|
const hasPermission = permissions.some(p => userPermissions.includes(p));
|
|
|
|
|
|
if (!hasPermission) {
|
|
|
|
|
|
return res.status(403).json({
|
|
|
|
|
|
error: 'Permission denied',
|
|
|
|
|
|
required: permissions,
|
|
|
|
|
|
current: userPermissions
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
next();
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 数据范围中间件
|
|
|
|
|
|
export function applyDataScope(resourceType: string) {
|
|
|
|
|
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
|
|
|
|
const userId = req.user?.id;
|
|
|
|
|
|
const scope = await DataScopeService.getUserDataScope(userId);
|
|
|
|
|
|
|
|
|
|
|
|
// 将数据范围附加到请求对象
|
|
|
|
|
|
req.dataScope = scope;
|
2026-03-28 22:52:12 +08:00
|
|
|
|
next();
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-03-28 22:52:12 +08:00
|
|
|
|
## 6. 安全考虑
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 6.1 安全原则
|
|
|
|
|
|
|
|
|
|
|
|
1. **最小权限原则**: 用户只获得完成工作所必需的最小权限
|
|
|
|
|
|
2. **权限分离**: 敏感操作需要多个权限组合
|
|
|
|
|
|
3. **定期审计**: 定期审查权限配置,清理无效权限
|
|
|
|
|
|
4. **数据加密**: 敏感数据加密存储和传输
|
|
|
|
|
|
5. **访问控制**: 严格的身份验证和访问控制
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 安全措施
|
|
|
|
|
|
|
|
|
|
|
|
| 安全措施 | 实现方式 | 状态 |
|
|
|
|
|
|
|---------|---------|------|
|
|
|
|
|
|
| **强密码策略** | 密码复杂度检查、定期更换 | ⚠️ 待实现 |
|
|
|
|
|
|
| **双因素认证(2FA)** | TOTP/短信验证码 | ✅ 已实现 |
|
|
|
|
|
|
| **登录异常检测** | 异常IP、时间、地点检测 | ⚠️ 待实现 |
|
|
|
|
|
|
| **权限边界检查** | 防止水平/垂直权限提升 | ✅ 已实现 |
|
|
|
|
|
|
| **敏感数据加密** | AES-256加密存储 | ✅ 已实现 |
|
|
|
|
|
|
| **审计日志** | 记录所有权限变更和数据访问 | ✅ 已实现 |
|
|
|
|
|
|
| **会话管理** | Token过期、单点登录限制 | ✅ 已实现 |
|
|
|
|
|
|
| **API限流** | 基于用户/租户的请求限流 | ✅ 已实现 |
|
|
|
|
|
|
|
|
|
|
|
|
### 6.3 审计日志
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// 审计日志记录
|
|
|
|
|
|
export class AuditService {
|
|
|
|
|
|
static async log(data: {
|
|
|
|
|
|
tenantId: string;
|
|
|
|
|
|
userId: string;
|
|
|
|
|
|
action: string;
|
|
|
|
|
|
resource: string;
|
|
|
|
|
|
resourceId?: string;
|
|
|
|
|
|
oldValue?: any;
|
|
|
|
|
|
newValue?: any;
|
|
|
|
|
|
ip?: string;
|
|
|
|
|
|
userAgent?: string;
|
|
|
|
|
|
}): Promise<void> {
|
|
|
|
|
|
await db('cf_audit_log').insert({
|
|
|
|
|
|
id: uuidv4(),
|
|
|
|
|
|
...data,
|
|
|
|
|
|
created_at: new Date()
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
|
|
|
|
|
## 7. 管理功能
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 7.1 后台管理系统架构
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
后台管理系统
|
|
|
|
|
|
├── 用户与权限管理
|
|
|
|
|
|
│ ├── 用户管理
|
2026-03-30 01:20:57 +08:00
|
|
|
|
│ │ ├── 用户列表
|
|
|
|
|
|
│ │ ├── 用户创建/编辑
|
|
|
|
|
|
│ │ ├── 用户禁用/启用
|
|
|
|
|
|
│ │ └── 用户登录日志
|
2026-03-28 22:52:12 +08:00
|
|
|
|
│ ├── 角色管理
|
2026-03-30 01:20:57 +08:00
|
|
|
|
│ │ ├── 角色列表
|
|
|
|
|
|
│ │ ├── 角色创建/编辑
|
|
|
|
|
|
│ │ ├── 权限分配
|
|
|
|
|
|
│ │ └── 角色成员
|
2026-03-28 22:52:12 +08:00
|
|
|
|
│ ├── 权限管理
|
2026-03-30 01:20:57 +08:00
|
|
|
|
│ │ ├── 权限列表
|
|
|
|
|
|
│ │ ├── 权限树
|
|
|
|
|
|
│ │ └── 权限分析
|
|
|
|
|
|
│ └── 部门管理
|
|
|
|
|
|
│ ├── 部门树
|
|
|
|
|
|
│ ├── 部门创建/编辑
|
|
|
|
|
|
│ ├── 部门成员
|
|
|
|
|
|
│ └── 部门统计
|
2026-03-28 22:52:12 +08:00
|
|
|
|
├── 租户管理
|
|
|
|
|
|
│ ├── 租户列表
|
2026-03-30 01:20:57 +08:00
|
|
|
|
│ ├── 租户创建/编辑
|
2026-03-28 22:52:12 +08:00
|
|
|
|
│ ├── 租户配置
|
|
|
|
|
|
│ ├── 配额管理
|
2026-03-30 01:20:57 +08:00
|
|
|
|
│ ├── 租户隔离
|
|
|
|
|
|
│ └── 租户数据备份
|
|
|
|
|
|
├── 店铺管理
|
|
|
|
|
|
│ ├── 我的店铺
|
|
|
|
|
|
│ │ ├── 店铺列表(卡片形式)
|
|
|
|
|
|
│ │ ├── 店铺状态(已连接/已过期/错误)
|
|
|
|
|
|
│ │ ├── 编辑店铺配置
|
|
|
|
|
|
│ │ ├── 刷新授权
|
|
|
|
|
|
│ │ ├── 管理成员
|
|
|
|
|
|
│ │ └── 删除店铺
|
|
|
|
|
|
│ ├── 店铺成员管理
|
|
|
|
|
|
│ │ ├── 侧边栏:我的店铺列表
|
|
|
|
|
|
│ │ ├── 主区域:当前店铺的成员列表
|
|
|
|
|
|
│ │ ├── 添加成员
|
|
|
|
|
|
│ │ ├── 修改成员角色
|
|
|
|
|
|
│ │ └── 移除成员
|
|
|
|
|
|
│ └── 用户店铺管理
|
|
|
|
|
|
│ ├── 查看用户拥有的店铺
|
|
|
|
|
|
│ ├── 添加用户到店铺
|
|
|
|
|
|
│ ├── 移除用户的店铺权限
|
|
|
|
|
|
│ └── 批量分配店铺
|
|
|
|
|
|
└── 系统监控
|
|
|
|
|
|
├── 审计日志
|
|
|
|
|
|
├── 登录日志
|
|
|
|
|
|
├── 操作日志
|
|
|
|
|
|
└── 系统告警
|
2026-03-28 22:52:12 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
### 7.2 前端实现
|
|
|
|
|
|
|
|
|
|
|
|
| 功能页面 | 组件路径 | 状态 |
|
|
|
|
|
|
|---------|---------|------|
|
|
|
|
|
|
| 用户管理 | `pages/Settings/UserManagement.tsx` | ✅ 已实现 |
|
|
|
|
|
|
| 角色管理 | `pages/Settings/RoleManagement.tsx` | ✅ 已实现 |
|
|
|
|
|
|
| 部门管理 | `pages/Settings/DepartmentManagement.tsx` | ✅ 已实现 |
|
|
|
|
|
|
| 租户设置 | `pages/Settings/TenantSettings.tsx` | ✅ 已实现 |
|
|
|
|
|
|
| 订阅管理 | `pages/Settings/SubscriptionManage.tsx` | ✅ 已实现 |
|
|
|
|
|
|
| 我的店铺 | `pages/Settings/MyShops.tsx` | ✅ 已实现 |
|
|
|
|
|
|
| 店铺成员管理 | `pages/Settings/ShopMemberManagement.tsx` | ✅ 已实现 |
|
|
|
|
|
|
| 系统监控 | `pages/Monitoring/SystemStatus.tsx` | ✅ 已实现 |
|
|
|
|
|
|
| 操作日志 | `pages/OperationLogs/index.tsx` | ✅ 已实现 |
|
|
|
|
|
|
|
|
|
|
|
|
### 7.3 店铺管理流程
|
|
|
|
|
|
|
|
|
|
|
|
#### 7.3.1 店铺视角管理(我的店铺)
|
|
|
|
|
|
|
|
|
|
|
|
**页面路径**: `/dashboard/settings/my-shops`
|
|
|
|
|
|
|
|
|
|
|
|
**功能说明**:
|
|
|
|
|
|
- 显示用户拥有的所有店铺(卡片形式)
|
|
|
|
|
|
- 每个店铺卡片显示:
|
|
|
|
|
|
- 店铺名称
|
|
|
|
|
|
- 平台类型(带图标)
|
|
|
|
|
|
- 授权状态(已连接/已过期/错误)
|
|
|
|
|
|
- 成员数量
|
|
|
|
|
|
- 最后同步时间
|
|
|
|
|
|
- 操作按钮:
|
|
|
|
|
|
- **管理成员**:跳转到店铺成员管理页面
|
|
|
|
|
|
- **编辑配置**:修改店铺名称、平台配置
|
|
|
|
|
|
- **刷新授权**:刷新过期的店铺授权
|
|
|
|
|
|
- **删除店铺**:删除店铺及其成员关系
|
|
|
|
|
|
|
|
|
|
|
|
**使用场景**:
|
|
|
|
|
|
1. 用户登录系统
|
|
|
|
|
|
2. 进入"我的店铺"页面
|
|
|
|
|
|
3. 查看自己拥有的所有店铺
|
|
|
|
|
|
4. 点击某个店铺的"管理成员"按钮
|
|
|
|
|
|
5. 进入店铺成员管理页面
|
|
|
|
|
|
|
|
|
|
|
|
#### 7.3.2 店铺成员管理
|
|
|
|
|
|
|
|
|
|
|
|
**页面路径**: `/dashboard/settings/shop-members/:shopId`
|
|
|
|
|
|
|
|
|
|
|
|
**功能说明**:
|
|
|
|
|
|
- **左侧边栏**:我的店铺列表
|
|
|
|
|
|
- 显示用户拥有的所有店铺
|
|
|
|
|
|
- 当前选中的店铺高亮
|
|
|
|
|
|
- 显示每个店铺的成员数量
|
|
|
|
|
|
- 点击切换店铺
|
|
|
|
|
|
- **右侧主区域**:当前店铺的成员列表
|
|
|
|
|
|
- 显示成员信息(用户名、邮箱、角色、权限)
|
|
|
|
|
|
- 添加成员(选择用户,分配角色和权限)
|
|
|
|
|
|
- 修改成员角色(下拉选择)
|
|
|
|
|
|
- 移除成员
|
|
|
|
|
|
|
|
|
|
|
|
**使用场景**:
|
|
|
|
|
|
1. 从"我的店铺"页面点击"管理成员"
|
|
|
|
|
|
2. 进入店铺成员管理页面
|
|
|
|
|
|
3. 左侧边栏显示我的店铺列表
|
|
|
|
|
|
4. 选择要管理的店铺
|
|
|
|
|
|
5. 右侧显示该店铺的成员列表
|
|
|
|
|
|
6. 添加/修改/移除成员
|
|
|
|
|
|
|
|
|
|
|
|
#### 7.3.3 用户视角管理(用户店铺分配)
|
|
|
|
|
|
|
|
|
|
|
|
**页面路径**: `/dashboard/settings/user`
|
|
|
|
|
|
|
|
|
|
|
|
**功能说明**:
|
|
|
|
|
|
- 在用户管理页面,每个用户有"管理店铺"按钮
|
|
|
|
|
|
- 点击后弹出店铺分配模态框
|
|
|
|
|
|
- 模态框功能:
|
|
|
|
|
|
- 按平台分组显示所有店铺
|
|
|
|
|
|
- 支持平台级别全选
|
|
|
|
|
|
- 支持单个店铺选择
|
|
|
|
|
|
- 显示已分配店铺列表
|
|
|
|
|
|
- 保存分配结果
|
|
|
|
|
|
|
|
|
|
|
|
**使用场景**:
|
|
|
|
|
|
1. 管理员进入用户管理页面
|
|
|
|
|
|
2. 点击某个用户的"管理店铺"按钮
|
|
|
|
|
|
3. 在弹出的模态框中:
|
|
|
|
|
|
- 查看该用户当前拥有的店铺
|
|
|
|
|
|
- 添加新的店铺权限
|
|
|
|
|
|
- 移除现有的店铺权限
|
|
|
|
|
|
4. 保存分配结果
|
|
|
|
|
|
|
|
|
|
|
|
### 7.4 双向管理关系
|
|
|
|
|
|
|
|
|
|
|
|
#### 7.4.1 店铺 → 用户管理
|
|
|
|
|
|
|
|
|
|
|
|
**入口**: 我的店铺 → 店铺成员管理
|
|
|
|
|
|
|
|
|
|
|
|
**操作流程**:
|
|
|
|
|
|
```
|
|
|
|
|
|
我的店铺页面
|
|
|
|
|
|
↓
|
|
|
|
|
|
选择店铺 → 点击"管理成员"
|
|
|
|
|
|
↓
|
|
|
|
|
|
店铺成员管理页面
|
|
|
|
|
|
↓
|
|
|
|
|
|
左侧边栏:我的店铺列表
|
|
|
|
|
|
↓
|
|
|
|
|
|
选择要管理的店铺
|
|
|
|
|
|
↓
|
|
|
|
|
|
右侧主区域:当前店铺的成员列表
|
|
|
|
|
|
↓
|
|
|
|
|
|
添加/修改/移除成员
|
|
|
|
|
|
```
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
**特点**:
|
|
|
|
|
|
- 店铺拥有者可以管理自己店铺的成员
|
|
|
|
|
|
- 可以添加、修改、删除成员
|
|
|
|
|
|
- 可以分配不同的角色和权限
|
|
|
|
|
|
- 侧边栏支持快速切换店铺
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
#### 7.4.2 用户 → 店铺管理
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
**入口**: 用户管理 → 管理店铺
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
**操作流程**:
|
|
|
|
|
|
```
|
|
|
|
|
|
用户管理页面
|
|
|
|
|
|
↓
|
|
|
|
|
|
选择用户 → 点击"管理店铺"
|
|
|
|
|
|
↓
|
|
|
|
|
|
店铺分配模态框
|
|
|
|
|
|
↓
|
|
|
|
|
|
查看该用户拥有的店铺
|
|
|
|
|
|
↓
|
|
|
|
|
|
添加/删除店铺权限
|
|
|
|
|
|
↓
|
|
|
|
|
|
保存分配结果
|
|
|
|
|
|
```
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
**特点**:
|
|
|
|
|
|
- 管理员可以管理任何用户的店铺权限
|
|
|
|
|
|
- 可以批量分配多个店铺
|
|
|
|
|
|
- 支持按平台分组显示
|
|
|
|
|
|
- 可以查看已分配的店铺列表
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
#### 7.4.3 数据关系
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
cf_shop(店铺表)
|
|
|
|
|
|
↓
|
|
|
|
|
|
cf_shop_member(店铺成员表)
|
|
|
|
|
|
↓
|
|
|
|
|
|
记录:用户-店铺-角色关系
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
cf_user(用户表)
|
|
|
|
|
|
↓
|
|
|
|
|
|
通过cf_shop_member关联店铺
|
|
|
|
|
|
```
|
2026-03-28 22:52:12 +08:00
|
|
|
|
|
2026-03-30 01:20:57 +08:00
|
|
|
|
**关键约束**:
|
|
|
|
|
|
- 一个用户可以拥有多个店铺的权限
|
|
|
|
|
|
- 一个店铺可以有多个成员
|
|
|
|
|
|
- 成员角色:owner(拥有者)、admin(管理员)、operator(运营)、viewer(只读)
|
|
|
|
|
|
- 权限可以自定义,也可以使用预设权限
|
|
|
|
|
|
|
|
|
|
|
|
### 7.5 店铺管理API
|
|
|
|
|
|
|
|
|
|
|
|
#### 7.5.1 店铺相关API
|
|
|
|
|
|
|
|
|
|
|
|
| API路径 | 方法 | 说明 | 权限要求 |
|
|
|
|
|
|
|---------|------|------|----------|
|
|
|
|
|
|
| `/api/shops/my-shops` | GET | 获取当前用户的店铺列表 | 登录用户 |
|
|
|
|
|
|
| `/api/shops/:id` | GET | 获取店铺详情 | 店铺成员 |
|
|
|
|
|
|
| `/api/shops` | POST | 创建店铺 | 店铺拥有者 |
|
|
|
|
|
|
| `/api/shops/:id` | PUT | 更新店铺信息 | 店铺拥有者 |
|
|
|
|
|
|
| `/api/shops/:id/refresh-auth` | POST | 刷新店铺授权 | 店铺拥有者 |
|
|
|
|
|
|
| `/api/shops/:id` | DELETE | 删除店铺 | 店铺拥有者 |
|
|
|
|
|
|
| `/api/shops/stats` | GET | 获取店铺统计信息 | 登录用户 |
|
|
|
|
|
|
|
|
|
|
|
|
#### 7.5.2 店铺成员相关API
|
|
|
|
|
|
|
|
|
|
|
|
| API路径 | 方法 | 说明 | 权限要求 |
|
|
|
|
|
|
|---------|------|------|----------|
|
|
|
|
|
|
| `/api/shops/:shopId/members` | GET | 获取店铺成员列表 | 店铺成员 |
|
|
|
|
|
|
| `/api/shops/user/:userId/shops` | GET | 获取用户拥有的店铺 | 管理员或用户本人 |
|
|
|
|
|
|
| `/api/shop-members` | POST | 添加店铺成员 | 店铺拥有者 |
|
|
|
|
|
|
| `/api/shop-members/:shopId/:userId` | DELETE | 移除店铺成员 | 店铺拥有者 |
|
|
|
|
|
|
| `/api/shop-members/:shopId/:userId` | PUT | 更新成员角色和权限 | 店铺拥有者 |
|
|
|
|
|
|
|
|
|
|
|
|
#### 7.5.3 数据源实现
|
|
|
|
|
|
|
|
|
|
|
|
**文件路径**: `dashboard/src/services/shopDataSource.ts`
|
|
|
|
|
|
|
|
|
|
|
|
**主要方法**:
|
|
|
|
|
|
- `getMyShops()`: 获取当前用户的店铺列表
|
|
|
|
|
|
- `getById(id)`: 获取店铺详情
|
|
|
|
|
|
- `getShopMembers(shopId)`: 获取店铺成员列表
|
|
|
|
|
|
- `getUserShops(userId)`: 获取用户拥有的店铺
|
|
|
|
|
|
- `createShop(data)`: 创建店铺
|
|
|
|
|
|
- `updateShop(id, data)`: 更新店铺信息
|
|
|
|
|
|
- `refreshAuth(id)`: 刷新店铺授权
|
|
|
|
|
|
- `deleteShop(id)`: 删除店铺
|
|
|
|
|
|
- `addMember(data)`: 添加店铺成员
|
|
|
|
|
|
- `removeMember(shopId, userId)`: 移除店铺成员
|
|
|
|
|
|
- `updateMemberRole(shopId, userId, role, permissions)`: 更新成员角色和权限
|
|
|
|
|
|
- `getStats()`: 获取店铺统计信息
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 实现状态
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 已实现功能
|
|
|
|
|
|
|
|
|
|
|
|
| 功能模块 | 功能点 | 状态 | 实现位置 |
|
|
|
|
|
|
|---------|--------|------|---------|
|
|
|
|
|
|
| **用户管理** | 用户CRUD | ✅ | UserService.ts, UserManagement.tsx |
|
|
|
|
|
|
| | 用户状态管理 | ✅ | UserService.updateUserStatus |
|
|
|
|
|
|
| | 用户缓存 | ✅ | UserService (Redis缓存) |
|
|
|
|
|
|
| | 批量操作 | ✅ | UserService.batchCreateUsers |
|
|
|
|
|
|
| **角色管理** | 角色CRUD | ✅ | RBACService.ts, RoleManagement.tsx |
|
|
|
|
|
|
| | 权限分配 | ✅ | RBACService.assignPermissions |
|
|
|
|
|
|
| | 权限树 | ✅ | RoleManagement.tsx |
|
|
|
|
|
|
| **部门管理** | 部门CRUD | ✅ | HierarchyService.ts, DepartmentManagement.tsx |
|
|
|
|
|
|
| | 层级结构 | ✅ | HierarchyService (path + depth) |
|
|
|
|
|
|
| | 部门转移 | ✅ | HierarchyService.transferUser |
|
|
|
|
|
|
| **租户管理** | 租户CRUD | ✅ | SaasTenantService.ts |
|
|
|
|
|
|
| | 租户隔离 | ✅ | 所有表tenant_id字段 |
|
|
|
|
|
|
| | 配额管理 | ✅ | QuotaGovernance |
|
|
|
|
|
|
| **店铺管理** | 店铺CRUD | ✅ | ShopService.ts, MyShops.tsx |
|
|
|
|
|
|
| | 店铺成员管理 | ✅ | ShopMemberService.ts, ShopMemberManagement.tsx |
|
|
|
|
|
|
| | 店铺授权刷新 | ✅ | ShopService.refreshAuth |
|
|
|
|
|
|
| | 用户店铺分配 | ✅ | UserManagement.tsx |
|
|
|
|
|
|
| | 双向管理关系 | ✅ | 店铺视角 + 用户视角 |
|
|
|
|
|
|
| **认证授权** | 登录认证 | ✅ | AuthService.ts |
|
|
|
|
|
|
| | Token管理 | ✅ | JWT + Refresh Token |
|
|
|
|
|
|
| | 权限检查 | ✅ | requirePermission中间件 |
|
|
|
|
|
|
| **数据范围** | 范围控制 | ✅ | DataScopeService |
|
|
|
|
|
|
| | 部门过滤 | ✅ | dept_path字段 |
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 待实现功能
|
|
|
|
|
|
|
|
|
|
|
|
| 功能模块 | 功能点 | 优先级 | 计划时间 |
|
|
|
|
|
|
|---------|--------|--------|---------|
|
|
|
|
|
|
| **安全增强** | 强密码策略 | P1 | 待定 |
|
|
|
|
|
|
| | 登录异常检测 | P2 | 待定 |
|
|
|
|
|
|
| **用户体验** | 拖拽排序 | P2 | 待定 |
|
|
|
|
|
|
| **集成扩展** | SSO集成 | P2 | 待定 |
|
|
|
|
|
|
| | LDAP/AD集成 | P3 | 待定 |
|
|
|
|
|
|
| | Webhook通知 | P3 | 待定 |
|
|
|
|
|
|
| **测试覆盖** | 功能测试 | P1 | 待定 |
|
|
|
|
|
|
| | 安全测试 | P1 | 待定 |
|
|
|
|
|
|
| | 性能测试 | P2 | 待定 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 9. 相关文档
|
|
|
|
|
|
|
|
|
|
|
|
- [User_Tenant_Improvement_Checklist.md](./User_Tenant_Improvement_Checklist.md) - 用户租户改进清单
|
|
|
|
|
|
- [RBAC_Design.md](./archive/02_Backend/07_RBAC_Design.md) - RBAC详细设计
|
|
|
|
|
|
- [Governance_Standards.md](./archive/00_Business/Governance_Standards.md) - 治理规范
|
|
|
|
|
|
- [Security_Policy.md](./rules/security.md) - 安全策略
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 10. 总结
|
|
|
|
|
|
|
|
|
|
|
|
Crawlful Hub 的用户层级架构采用了**多租户 + 多层级 + RBAC** 的设计模式:
|
|
|
|
|
|
|
|
|
|
|
|
1. **多租户隔离**: 确保不同商户数据完全隔离,保障数据安全
|
|
|
|
|
|
2. **灵活的组织结构**: 支持多级部门,适应不同规模企业
|
|
|
|
|
|
3. **细粒度权限控制**: 基于角色的权限管理,支持自定义角色
|
|
|
|
|
|
4. **数据范围控制**: 从个人到全平台的多级数据访问控制
|
|
|
|
|
|
5. **完整的审计体系**: 记录所有操作,支持安全审计
|
|
|
|
|
|
|
|
|
|
|
|
该架构不仅满足了企业级应用的安全需求,也为不同规模的商户提供了灵活的组织管理能力,支持从初创企业到大型集团的业务场景。
|