feat(types): 添加express.d.ts类型引用 style: 格式化express.d.ts中的接口定义 refactor: 移除未使用的AntFC类型导入 chore: 删除自动生成的.umi-production文件 feat: 添加店铺管理相关表和初始化脚本 docs: 更新安全规则和交互指南文档 refactor: 统一使用FC类型替代React.FC perf: 优化图表组件导入方式 style: 添加.prettierrc配置文件 refactor: 调整组件导入顺序和结构 feat: 添加平台库存管理路由 fix: 修复订单同步时的库存检查逻辑 docs: 更新RBAC设计和租户管理文档 refactor: 优化部门控制器代码
384 lines
15 KiB
Markdown
384 lines
15 KiB
Markdown
# 用户层级架构文档与实际代码差异分析
|
||
|
||
> **分析日期**: 2026-03-29
|
||
> **文档版本**: 用户层级架构说明.md v1.1
|
||
> **代码版本**: server/src
|
||
|
||
---
|
||
|
||
## 1. 表名差异
|
||
|
||
| 文档中的表名 | 实际代码中的表名 | 说明 |
|
||
|-------------|----------------|------|
|
||
| `cf_users` | `cf_user` | 复数/单数差异 |
|
||
| `cf_tenants` | `cf_tenant` | 复数/单数差异 |
|
||
| `cf_departments` | `cf_department` | 复数/单数差异 |
|
||
| `cf_role_permissions` | `cf_role_permission` | 复数/单数差异 |
|
||
| `cf_shop_auth` | **不存在** | 文档中提到但代码中未实现 |
|
||
|
||
---
|
||
|
||
## 2. 用户表 (`cf_user`) 差异
|
||
|
||
### 文档中的字段
|
||
```sql
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
username VARCHAR(50) NOT NULL,
|
||
email VARCHAR(100) NOT NULL UNIQUE,
|
||
password_hash VARCHAR(255) NOT NULL,
|
||
role VARCHAR(20) DEFAULT 'OPERATOR',
|
||
tenant_id VARCHAR(36) NOT NULL,
|
||
dept_id VARCHAR(36),
|
||
status ENUM('active', 'inactive', 'suspended') DEFAULT 'active',
|
||
last_login_at TIMESTAMP,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
```
|
||
|
||
### 实际代码中的字段
|
||
```sql
|
||
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
|
||
```
|
||
|
||
### 差异点
|
||
| 字段 | 文档 | 实际代码 | 差异说明 |
|
||
|------|------|---------|---------|
|
||
| `username` | VARCHAR(50) | VARCHAR(64) | 长度不同 |
|
||
| `email` | VARCHAR(100) | VARCHAR(128) | 长度不同 |
|
||
| `dept_id` | ✅ 存在 | ❌ 不存在 | 实际代码中没有部门ID字段 |
|
||
| `full_name` | ❌ 不存在 | ✅ 存在 | 实际代码中有全名字段 |
|
||
| `phone` | ❌ 不存在 | ✅ 存在 | 实际代码中有电话字段 |
|
||
| `is_superadmin` | ❌ 不存在 | ✅ 存在 | 实际代码中有超级管理员标识 |
|
||
| `created_by` | ❌ 不存在 | ✅ 存在 | 实际代码中有创建人字段 |
|
||
| `updated_by` | ❌ 不存在 | ✅ 存在 | 实际代码中有更新人字段 |
|
||
| `status` | 小写枚举 | 大写枚举 | 命名风格不同 |
|
||
| `role` | 字符串 | 枚举类型 | 类型定义不同 |
|
||
|
||
---
|
||
|
||
## 3. 租户表 (`cf_tenant`) 差异
|
||
|
||
### 文档中的字段
|
||
```sql
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
type ENUM('SAAS', 'ENTERPRISE', 'TRIAL') DEFAULT 'SAAS',
|
||
status ENUM('ACTIVE', 'SUSPENDED', 'CANCELLED') DEFAULT 'ACTIVE',
|
||
plan_id VARCHAR(36),
|
||
quota_config JSON,
|
||
settings JSON,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
```
|
||
|
||
### 实际代码中的字段
|
||
```sql
|
||
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
|
||
```
|
||
|
||
### 差异点
|
||
| 字段 | 文档 | 实际代码 | 差异说明 |
|
||
|------|------|---------|---------|
|
||
| `name` | VARCHAR(100) | VARCHAR(128) | 长度不同 |
|
||
| `type` | ✅ 存在 | ❌ 不存在 | 实际代码中没有租户类型字段 |
|
||
| `plan_id` | ✅ 存在 | ❌ 不存在 | 实际代码中没有套餐ID字段 |
|
||
| `quota_config` | ✅ 存在 | ❌ 不存在 | 实际代码中没有JSON配置字段 |
|
||
| `settings` | ✅ 存在 | ❌ 不存在 | 实际代码中没有JSON设置字段 |
|
||
| `domain` | ❌ 不存在 | ✅ 存在 | 实际代码中有域名字段 |
|
||
| `contact_name` | ❌ 不存在 | ✅ 存在 | 实际代码中有联系人姓名字段 |
|
||
| `contact_email` | ❌ 不存在 | ✅ 存在 | 实际代码中有联系人邮箱字段 |
|
||
| `contact_phone` | ❌ 不存在 | ✅ 存在 | 实际代码中有联系人电话字段 |
|
||
| `address` | ❌ 不存在 | ✅ 存在 | 实际代码中有地址字段 |
|
||
| `quota_limit` | ❌ 不存在 | ✅ 存在 | 实际代码中有配额限制字段 |
|
||
| `quota_used` | ❌ 不存在 | ✅ 存在 | 实际代码中有已用配额字段 |
|
||
| `created_by` | ❌ 不存在 | ✅ 存在 | 实际代码中有创建人字段 |
|
||
| `updated_by` | ❌ 不存在 | ✅ 存在 | 实际代码中有更新人字段 |
|
||
|
||
---
|
||
|
||
## 4. 部门表 (`cf_department`) 差异
|
||
|
||
### 文档中的字段
|
||
```sql
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
tenant_id VARCHAR(36) NOT NULL,
|
||
parent_id VARCHAR(36),
|
||
name VARCHAR(100) NOT NULL,
|
||
manager_id VARCHAR(36),
|
||
path VARCHAR(500),
|
||
depth INT DEFAULT 0,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (tenant_id) REFERENCES cf_tenants(id),
|
||
INDEX idx_tenant_path (tenant_id, path)
|
||
```
|
||
|
||
### 实际代码中的字段(HierarchyService.ts中定义)
|
||
```sql
|
||
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
|
||
```
|
||
|
||
### 差异点
|
||
| 字段 | 文档 | 实际代码 | 差异说明 |
|
||
|------|------|---------|---------|
|
||
| `name` | VARCHAR(100) | VARCHAR(128) | 长度不同 |
|
||
| `status` | ❌ 不存在 | ✅ 存在 | 实际代码中有状态字段 |
|
||
| `updated_at` | ❌ 不存在 | ✅ 存在 | 实际代码中有更新时间字段 |
|
||
|
||
---
|
||
|
||
## 5. 角色权限表 (`cf_role_permission`) 差异
|
||
|
||
### 文档中的字段
|
||
```sql
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
role_key VARCHAR(50) NOT NULL,
|
||
permission_key VARCHAR(100) NOT NULL,
|
||
tenant_id VARCHAR(36),
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
UNIQUE KEY uk_role_perm (role_key, permission_key, tenant_id)
|
||
```
|
||
|
||
### 实际代码中的字段
|
||
```sql
|
||
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
|
||
```
|
||
|
||
### 差异点
|
||
| 字段 | 文档 | 实际代码 | 差异说明 |
|
||
|------|------|---------|---------|
|
||
| `role_key` | ✅ 存在 | ❌ 不存在 | 实际代码中是`role_id` |
|
||
| `permission_key` | ✅ 存在 | ❌ 不存在 | 实际代码中是`permission_id` |
|
||
| `tenant_id` | ✅ 存在 | ❌ 不存在 | 实际代码中没有租户ID字段 |
|
||
| `created_by` | ❌ 不存在 | ✅ 存在 | 实际代码中有创建人字段 |
|
||
| `updated_by` | ❌ 不存在 | ✅ 存在 | 实际代码中有更新人字段 |
|
||
| `updated_at` | ❌ 不存在 | ✅ 存在 | 实际代码中有更新时间字段 |
|
||
|
||
---
|
||
|
||
## 6. 数据范围类型差异
|
||
|
||
### 文档中的数据范围类型
|
||
| 类型 | 说明 | SQL过滤条件 | 适用角色 |
|
||
|------|------|------------|---------|
|
||
| **SELF** | 只看自己创建的数据 | `WHERE created_by = {userId}` | 普通员工 |
|
||
| **TEAM** | 看自己组的数据 | `WHERE team_id IN ({userTeams})` | 组长 |
|
||
| **DEPT** | 看自己部门的数据 | `WHERE dept_id IN ({userDepts})` | 部门主管 |
|
||
| **ORG** | 看整个公司的数据 | `WHERE tenant_id = {tenantId}` | 高管 |
|
||
| **ALL** | 全平台数据(超管) | 无过滤 | 系统管理员 |
|
||
|
||
### 实际代码中的数据范围类型(RBACEngine.ts)
|
||
```typescript
|
||
export interface DataScope {
|
||
type: 'ALL' | 'ORG' | 'DEPT' | 'SHOP' | 'OWN';
|
||
departmentId?: string;
|
||
shopId?: string;
|
||
}
|
||
```
|
||
|
||
### 差异点
|
||
| 文档类型 | 实际代码类型 | 差异说明 |
|
||
|----------|-------------|---------|
|
||
| **SELF** | **OWN** | 实际代码中是`OWN`而不是`SELF` |
|
||
| **TEAM** | **SHOP** | 实际代码中是`SHOP`而不是`TEAM` |
|
||
| **DEPT** | **DEPT** | 一致 |
|
||
| **ORG** | **ORG** | 一致 |
|
||
| **ALL** | **ALL** | 一致 |
|
||
|
||
---
|
||
|
||
## 7. 服务实现差异
|
||
|
||
### DataScopeService
|
||
- **文档中**: 提到`DataScopeService`类,包含`getUserDataScope`和`applyDataScope`方法
|
||
- **实际代码**: 不存在`DataScopeService`类
|
||
- **实际实现**: 数据隔离功能通过`DataIsolationService`实现
|
||
|
||
### HierarchyService
|
||
- **文档中的方法签名**:
|
||
```typescript
|
||
static async getUserHierarchyPath(userId: string): Promise<string[]>;
|
||
static async getSubDepartments(deptId: string): Promise<Department[]>;
|
||
static async getDepartmentUsers(deptId: string): Promise<User[]>;
|
||
static async transferUser(userId: string, targetDeptId: string): Promise<void>;
|
||
static async getDepartmentStats(deptId: string): Promise<DeptStats>;
|
||
```
|
||
- **实际代码中的方法**:
|
||
```typescript
|
||
static async initializeTenantHierarchy(tenantId: string): Promise<void>;
|
||
static async createDepartment(tenantId: string, name: string, parentId?: string): Promise<Department>;
|
||
static async updateDepartment(deptId: string, updates: Partial<Department>): Promise<void>;
|
||
static async deleteDepartment(deptId: string): Promise<void>;
|
||
static async getDepartmentTree(tenantId: string): Promise<HierarchyNode[]>;
|
||
static async getDepartmentStats(deptId: string): Promise<HierarchyStats>;
|
||
static async transferUser(userId: string, targetDeptId: string): Promise<void>;
|
||
static async createShop(tenantId: string, deptId: string, shopData: Partial<Shop>): Promise<Shop>;
|
||
static async updateShop(shopId: string, updates: Partial<Shop>): Promise<void>;
|
||
static async deleteShop(shopId: string): Promise<void>;
|
||
static async getShopTree(tenantId: string): Promise<HierarchyNode[]>;
|
||
static async assignUserToShop(userId: string, shopId: string): Promise<void>;
|
||
static async removeUserFromShop(userId: string): Promise<void>;
|
||
```
|
||
|
||
### AuthService
|
||
- **文档中的方法签名**:
|
||
```typescript
|
||
static async login(credentials: LoginCredentials): Promise<AuthResult>;
|
||
static async verifyToken(token: string): Promise<TokenPayload>;
|
||
static async refreshToken(refreshToken: string): Promise<AuthResult>;
|
||
static async logout(userId: string): Promise<void>;
|
||
```
|
||
- **实际代码中的方法**:
|
||
```typescript
|
||
static async login(input: LoginInput): Promise<LoginResult>;
|
||
static async register(input: RegisterInput): Promise<RegisterResult>;
|
||
static async passwordReset(input: PasswordResetInput): Promise<PasswordResetResult>;
|
||
static async checkPermission(input: PermissionCheckInput): Promise<PermissionCheckResult>;
|
||
static async getUserPermissions(input: UserPermissionsInput): Promise<UserPermissionsResult>;
|
||
static async assignRole(input: AssignRoleInput): Promise<boolean>;
|
||
static async removeRole(input: RemoveRoleInput): Promise<boolean>;
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 权限点设计差异
|
||
|
||
### 文档中的权限点命名规范
|
||
```typescript
|
||
const PERMISSIONS = {
|
||
'product:read': '查看商品',
|
||
'product:create': '创建商品',
|
||
'product:update': '更新商品',
|
||
'product:delete': '删除商品',
|
||
'product:export': '导出商品',
|
||
'product:import': '导入商品',
|
||
// ... 其他权限
|
||
};
|
||
```
|
||
|
||
### 实际代码中的权限点(RBACEngine.ts)
|
||
```typescript
|
||
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' },
|
||
|
||
// ... 更多权限
|
||
];
|
||
```
|
||
|
||
### 差异点
|
||
| 差异 | 说明 |
|
||
|------|------|
|
||
| **权限结构** | 实际代码使用`Permission`对象数组,包含id、name、description、module、action字段 |
|
||
| **模块划分** | 实际代码按模块划分(PRODUCT、ORDER、INVENTORY、FINANCE、ADVERTISING、SOURCING、LOGISTICS、AI、COLLECTION、USER、TENANT、AUDIT) |
|
||
| **操作类型** | 实际代码使用READ、WRITE、DELETE、EXECUTE四种操作类型 |
|
||
| **权限数量** | 实际代码中定义了50+个权限点,比文档中更详细 |
|
||
|
||
---
|
||
|
||
## 9. 建议修正
|
||
|
||
### 9.1 表名统一
|
||
建议将文档中的表名统一为实际代码中的单数形式:
|
||
- `cf_users` → `cf_user`
|
||
- `cf_tenants` → `cf_tenant`
|
||
- `cf_departments` → `cf_department`
|
||
- `cf_role_permissions` → `cf_role_permission`
|
||
|
||
### 9.2 字段定义更新
|
||
建议更新文档中的字段定义,与实际代码保持一致:
|
||
- 添加缺失的字段(如`full_name`、`phone`、`is_superadmin`等)
|
||
- 删除不存在的字段(如`dept_id`、`type`、`plan_id`等)
|
||
- 更新字段类型和长度(如`username`从VARCHAR(50)改为VARCHAR(64))
|
||
|
||
### 9.3 数据范围类型更新
|
||
建议将文档中的数据范围类型更新为实际代码中的定义:
|
||
- `SELF` → `OWN`
|
||
- `TEAM` → `SHOP`
|
||
- 保持`DEPT`、`ORG`、`ALL`不变
|
||
|
||
### 9.4 服务方法签名更新
|
||
建议更新文档中的服务方法签名,与实际代码保持一致:
|
||
- `HierarchyService`方法签名更新
|
||
- `AuthService`方法签名更新
|
||
- 删除不存在的`DataScopeService`引用
|
||
|
||
### 9.5 权限点设计更新
|
||
建议更新文档中的权限点设计,与实际代码中的RBACEngine保持一致:
|
||
- 使用`Permission`对象结构
|
||
- 按模块划分权限
|
||
- 使用READ、WRITE、DELETE、EXECUTE操作类型
|
||
|
||
### 9.6 店铺授权表
|
||
建议删除文档中关于`cf_shop_auth`表的描述,因为实际代码中不存在此表。
|
||
|
||
---
|
||
|
||
## 10. 总结
|
||
|
||
| 差异类型 | 数量 | 严重程度 |
|
||
|----------|------|---------|
|
||
| 表名不一致 | 5 | 🟡 中等 |
|
||
| 字段缺失 | 15+ | 🔴 严重 |
|
||
| 字段类型不一致 | 8 | 🟡 中等 |
|
||
| 数据范围类型不一致 | 2 | 🟡 中等 |
|
||
| 服务方法签名不一致 | 3 | 🟡 中等 |
|
||
| 权限点设计不一致 | 1 | 🟡 中等 |
|
||
|
||
**总体评估**: 文档与实际代码存在较多差异,建议尽快更新文档以保持一致性。
|