fix(用户管理): 修复用户列表页面加载失败问题
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

修复用户列表页面加载失败导致测试超时的问题,确保页面元素正确渲染
This commit is contained in:
iven
2026-04-19 08:46:28 +08:00
parent 0ee9d22634
commit 841766b168
174 changed files with 26366 additions and 675 deletions

View File

@@ -3,41 +3,97 @@
## 设计思想
开发环境在 **Windows** 宿主机直接运行所有服务:
- PostgreSQL 16+ 和 Redis 7+ 通过 Windows 原生安装运行
- PostgreSQL 通过 Windows 原生安装运行
- Redis 7+ 通过 Windows 原生安装运行(可选,缺省时限流降级为 fail-open
- 后端 Rust 服务通过 `cargo run` 快速重启
- 前端 Vite 热更新直接在宿主机
- PowerShell 脚本 (`dev.ps1`) 提供一键启动/停止
> Docker Compose 配置保留在 `docker/` 目录下,可供需要容器化环境的场景使用,但日常开发不依赖 Docker。
## 代码逻辑
## 本机环境实际配置
### 服务配置
> **重要:以下为当前开发机的实际配置,以本文件为准。**
| 组件 | 安装路径 | 配置 |
|------|---------|------|
| PostgreSQL 18 | `D:\postgreSQL\` | 服务名 `postgresql-x64-18`, 端口 5432 |
| Redis | 云端实例 | `redis://:redis_KBCYJk@129.204.154.246:6379`, 限流中间件已正常工作 |
| Rust | stable (cargo in PATH) | workspace 根目录编译 |
| Node.js + pnpm | in PATH | apps/web/ |
### 数据库连接
```
用户: postgres
密码: 123123
数据库: erp
连接串: postgres://postgres:123123@localhost:5432/erp
```
psql 路径: `D:\postgreSQL\bin\psql.exe -U postgres -h localhost -d erp`
### 后端启动命令
后端**必须**从 `crates/erp-server/` 目录启动(需要读取 `config/default.toml`),或通过环境变量覆盖:
```powershell
# 方式一:从 crates/erp-server 目录启动(使用 default.toml + 环境变量覆盖)
cd crates/erp-server
$env:ERP__DATABASE__URL = "postgres://postgres:123123@localhost:5432/erp"
$env:ERP__JWT__SECRET = "dev-secret-key-change-in-prod"
$env:ERP__AUTH__SUPER_ADMIN_PASSWORD = "Admin@2026"
cargo run -p erp-server
# 方式二:一键启动脚本(推荐)
.\dev.ps1
```
### 登录凭据
```
用户名: admin
密码: Admin@2026
```
## 服务端口
| 服务 | 端口 | 用途 |
|------|------|------|
| PostgreSQL 16+ | 5432 | 主数据库 |
| Redis 7+ | 6379 | 缓存 + 会话 |
| PostgreSQL 18 | 5432 | 主数据库 |
| Redis 7+ | 6379 (云端) | 缓存 + 限流 |
| erp-server (Axum) | 3000 | 后端 API |
| Vite dev server | 5174 | 前端 SPA |
| Vite dev server | 5174 | 前端 SPA(固定端口,--strictPort |
### 连接信息
### 连接信息(配置文件版本)
```
PostgreSQL: postgres://erp:erp_dev_2024@localhost:5432/erp
Redis: redis://localhost:6379
PostgreSQL: postgres://postgres:123123@localhost:5432/erp
Redis: redis://:redis_KBCYJk@129.204.154.246:6379 (云端实例)
```
### 一键启动
## 一键启动
```powershell
.\dev.ps1 # 启动后端 + 前端
.\dev.ps1 # 启动后端 + 前端(自动清理旧进程 5174-5189
.\dev.ps1 -Status # 查看端口状态
.\dev.ps1 -Stop # 停止所有服务
.\dev.ps1 -Restart # 重启所有服务
```
> `dev.ps1` 会在启动前清理端口 5174-5189 范围内所有残留进程,并使用 `--strictPort` 确保 Vite 固定在 5174 端口。
### 环境变量
通过 `crates/erp-server/config/default.toml` 配置,无需额外环境变量文件即可启动。
必须通过环境变量设置的值(`default.toml` 中为占位符):
| 变量 | 说明 | 开发值 |
|------|------|--------|
| `ERP__DATABASE__URL` | 数据库连接串 | `postgres://postgres:123123@localhost:5432/erp` |
| `ERP__JWT__SECRET` | JWT 签名密钥 | 自定义字符串 |
| `ERP__AUTH__SUPER_ADMIN_PASSWORD` | admin 初始密码 | `Admin@2026` |
| `ERP__REDIS__URL` | Redis 连接串 | `redis://:redis_KBCYJk@129.204.154.246:6379` |
> 所有四个变量在 `default.toml` 中都是 `__MUST_SET_VIA_ENV__` 占位符,**必须**通过环境变量设置,否则服务拒绝启动。
## 关联模块
@@ -50,9 +106,10 @@ Redis: redis://localhost:6379
| 文件 | 职责 |
|------|------|
| `dev.ps1` | 一键启动/停止脚本 |
| `dev.ps1` | 一键启动/停止脚本(自动清理端口 5174-5189 |
| `docker/docker-compose.yml` | 可选的 Docker Compose 配置 |
| `crates/erp-server/config/default.toml` | 默认连接配置 |
| `crates/erp-server/config/default.toml` | 默认配置模板(密钥为占位符) |
| `D:\postgreSQL\bin\psql.exe` | PostgreSQL 客户端 |
## 常用命令
@@ -60,12 +117,22 @@ Redis: redis://localhost:6379
# 一键启动(推荐)
.\dev.ps1
# 手动启动后端
# 手动启动后端(从 crates/erp-server 目录)
cd crates/erp-server
$env:ERP__DATABASE__URL="postgres://postgres:123123@localhost:5432/erp"
$env:ERP__JWT__SECRET="dev-secret"
$env:ERP__AUTH__SUPER_ADMIN_PASSWORD="Admin@2026"
cargo run -p erp-server
# 手动启动前端
cd apps/web && pnpm dev
# 手动启动前端(固定端口)
cd apps/web && pnpm dev -- --strictPort
# 连接数据库
psql -U erp -d erp -h localhost
D:\postgreSQL\bin\psql.exe -U postgres -h localhost -d erp
# 健康检查
curl http://localhost:3000/api/v1/health
# 登录测试
curl -s http://localhost:3000/api/v1/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"Admin@2026"}'
```

View File

@@ -443,3 +443,72 @@ let limits = StoreLimitsBuilder::new()
- **动态表**: 支持 `db_insert("dynamic_table", ...)` 自动创建/迁移表
- **前端集成**: PluginCRUDPage 组件根据 WIT 定义自动生成 CRUD 页面
- **插件市场**: 插件元数据版本管理签名验证
## 插件权限系统(关键)
### 权限码格式
插件数据操作的权限码由 `data_handler.rs` 中的 `compute_permission_code()` 按以下规则自动生成
```
{manifest_id}.{url_entity_name}.{action_suffix}
```
- `manifest_id`plugin.toml `[metadata].id` `erp-crm`
- `url_entity_name`REST API 路径中的实体名 `customer_tag`
- `action_suffix``list`读操作 `manage`写操作
| 操作 | 权限码示例 |
|------|-----------|
| 列表/详情 | `erp-crm.customer.list` |
| 创建/更新/删除 | `erp-crm.customer.manage` |
### 权限码命名铁律P0 级)
**`plugin.toml` `permissions[].code` 的前缀必须与 `schema.entities[].name` 完全一致。**
```
data_handler 生成:{manifest_id}.{url_entity_name}.{action}
↑ 来自 URL 路径中的 entity 参数
manifest 声明: {entity_name}.{action}
↑ 必须与 URL 中的 entity name 匹配
```
每个实体必须同时声明 `.list` `.manage` 两个权限
```toml
# ✅ 正确:权限码前缀与实体名一致
[[schema.entities]]
name = "customer_tag"
[[permissions]]
code = "customer_tag.list" # 匹配!
[[permissions]]
code = "customer_tag.manage" # 匹配!
# ❌ 错误:权限码用了简写,与实体名不一致 → 403
[[permissions]]
code = "tag.manage" # data_handler 生成 erp-crm.customer_tag.manage
# 但 DB 中只有 erp-crm.tag.manage → 403
```
**历史教训:** CRM 插件首个版本中`customer_tag` 实体的权限码写成了 `tag.manage``customer_relationship` 实体的权限码写成了 `relationship.list/manage`结果标签管理客户关系关系图谱三个页面全部 403修复迁移`m20260419_000038_fix_crm_permission_codes.rs`
### 权限注册流程
1. **插件安装时** `register_plugin_permissions()` manifest 中声明的权限批量 INSERT `permissions` `ON CONFLICT DO NOTHING` 保证幂等
2. **权限分配** `grant_permissions_to_admin()` 自动将权限分配给 admin 角色
3. **运行时校验** `data_handler.rs` `compute_permission_code()` URL entity name 生成权限码通过 `require_permission()` 检查 JWT 中的权限列表
### 已修复问题
| 问题 | 修复 |
|------|------|
| 权限未自动分配给 admin 角色 403 | `grant_permissions_to_admin()` install/enable 时自动调用 |
| 权限码与实体名不匹配 403 | 迁移 m20260419_000038 + plugin.toml 修正 |
### 插件 API 路由注意事项
- 后端路由使用 `Path<(Uuid, String)>` 解析 `plugin_id`必须是 UUID 格式
- 前端使用 `plugin.id`数据库 UUID而非 `manifest_id` `erp-crm`构建请求 URL
- 直接用 manifest_id 调用 API 会返回 `UUID parsing failed` 错误