refactor: 整理项目根目录结构

整理内容:
- 删除 60+ 临时测试输出文件 (*.txt)
- 移动二进制文件到 bin/ 目录
- 移动 Shell 脚本到 scripts/ 目录
  - scripts/dev/: check_gitea.sh, check_sub2api.sh, run_tests.sh
  - scripts/deploy/: deploy_*.sh, simple_deploy.sh
  - scripts/ops/: fix_nginx.sh, fix_ssl.sh, install_docker.sh
  - scripts/test/: test_*.sh, test_*.bat
- 移动批处理文件到 scripts/
- 移动 Python 脚本到 tools/
- 清理临时日志文件

保留根目录必要文件:
- go.mod, go.sum, go.work
- Makefile, docker-compose.yml
- .env.example, .gitignore
- README.md, AGENTS.md, DEPLOY_GUIDE.md

验证: go build ./... && go test ./... 通过
This commit is contained in:
2026-04-07 18:10:36 +08:00
parent 5dbb530b76
commit 5b6bd93179
152 changed files with 8775 additions and 4084 deletions

View File

@@ -0,0 +1,198 @@
# 业务逻辑正确性测试方案
## 概述
本文档定义用户管理系统核心业务逻辑的正确性测试方案,涵盖用户生命周期管理、设备信任、登录日志、统计数据的端到端正确性验证。
---
## 1. 用户注册与审批流程测试
### 1.1 用户注册创建
**测试目标**:验证用户注册后状态流转与数据一致性的正确性
| 用例编号 | 用例描述 | 前置条件 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|---------|-----------|
| REG-001 | 管理员创建用户并设置初始状态为已激活 | 管理员已登录 | 1. 管理员创建用户status=1已激活<br>2. 提交表单 | 1. API 返回 200<br>2. 用户状态为已激活<br>3. 无需邮箱激活即可登录 | `SELECT status FROM users WHERE username='xxx'` 返回 `1` |
| REG-002 | 管理员创建用户并设置初始状态为未激活 | 管理员已登录 | 1. 管理员创建用户status=0未激活<br>2. 提交表单 | 1. API 返回 200<br>2. 用户状态为未激活<br>3. 用户无法登录 | `SELECT status FROM users WHERE username='xxx'` 返回 `0` |
| REG-003 | 用户自助注册流程状态正确 | 系统无管理员 | 1. 用户填写注册表单<br>2. 提交注册 | 1. 创建用户status=0未激活<br>2. 发送激活邮件 | `SELECT status FROM users WHERE email='xxx'` 返回 `0` |
| REG-004 | 重复用户名注册拒绝 | 无 | 1. 创建用户 "testuser"<br>2. 再次创建相同用户名 | 返回错误:用户名已存在 | 用户表仅有一条 username='testuser' 的记录 |
| REG-005 | 创建用户时分配角色 | 存在可用角色 | 1. 创建用户并分配 role_ids=[2,3]<br>2. 查询用户角色 | 1. API 返回成功<br>2. 用户拥有指定角色 | `SELECT role_id FROM user_roles WHERE user_id=xxx` 包含 2,3 |
### 1.2 用户状态变更
**测试目标**:验证用户状态(激活/锁定/禁用)变更的正确性及对登录的影响
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|-----------|
| STA-001 | 管理员禁用用户后用户无法登录 | 1. 禁用用户status=3<br>2. 用户尝试登录 | 登录失败,提示"账号已禁用" | `SELECT status FROM users WHERE id=xxx` 返回 `3` |
| STA-002 | 管理员锁定用户后用户无法登录 | 1. 锁定用户status=2<br>2. 用户尝试登录 | 登录失败,提示"账号已锁定" | `SELECT status FROM users WHERE id=xxx` 返回 `2` |
| STA-003 | 管理员解锁用户后用户恢复登录 | 1. 用户当前 status=2<br>2. 管理员更新为 status=1 | 1. 更新成功<br>2. 用户可正常登录 | `SELECT status FROM users WHERE id=xxx` 返回 `1` |
| STA-004 | 管理员激活未激活用户 | 1. 用户当前 status=0<br>2. 管理员激活用户 | 1. 更新成功<br>2. 用户可正常登录 | `SELECT status FROM users WHERE id=xxx` 返回 `1` |
| STA-005 | 批量更新用户状态 | 1. 选择 5 个用户<br>2. 批量禁用 | 1. 全部更新成功<br>2. 所有用户 status=3 | `SELECT COUNT(*) FROM users WHERE status=3 AND id IN (...)` 返回 `5` |
### 1.3 用户删除
**测试目标**:验证用户删除后的数据完整性
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|-----------|
| DEL-001 | 删除用户后用户角色关联清除 | 1. 删除用户 ID=10<br>2. 查询 user_roles | 1. API 返回 200<br>2. 该用户无角色记录 | `SELECT COUNT(*) FROM user_roles WHERE user_id=10` 返回 `0` |
| DEL-002 | 删除用户后登录日志保留用户ID | 1. 删除用户前查询登录日志<br>2. 删除用户<br>3. 查询登录日志 | 1. 删除前有日志<br>2. 删除后日志仍存在user_id 字段保留 | 登录日志表 user_id 字段保留原值(非级联删除) |
| DEL-003 | 删除用户后设备关联保留 | 1. 删除用户<br>2. 查询 devices 表 | devices 表中该用户的设备保留user_id 不变 | `SELECT COUNT(*) FROM devices WHERE user_id=xxx` 结果不变 |
| DEL-004 | 恢复删除(软删除)的用户 | 1. 系统启用软删除<br>2. 删除用户<br>3. 恢复用户 | 1. 删除成功<br>2. 恢复后用户状态恢复 | 用户记录恢复status 恢复原值 |
---
## 2. 统计数据正确性测试
### 2.1 用户统计正确性
**测试目标**验证用户统计total_users, active_users, new_users 等)的计算正确性
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|-----------|
| STAT-001 | 总用户数统计正确 | 1. 查询当前总用户数<br>2. 创建 3 个新用户<br>3. 再次查询 | 第二次查询比第一次多 3 | `SELECT COUNT(*) FROM users` 与 API 返回 total_users 一致 |
| STAT-002 | 新增用户今日统计正确 | 1. 查看今日新增<br>2. 创建一个用户<br>3. 再次查看 | 新增用户数 +1 | `SELECT COUNT(*) FROM users WHERE created_at >= today_start` 与 new_users_today 一致 |
| STAT-003 | 按状态统计正确 | 1. 创建 2 个用户后禁用其中一个<br>2. 查询统计数据 | disabled_users = 1 | `SELECT COUNT(*) FROM users WHERE status=3` 与 disabled_users 一致 |
| STAT-004 | 创建用户后 dashboard 统计数据更新 | 1. 获取 dashboard stats<br>2. 创建 1 个用户<br>3. 再次获取 | total_users +1, new_users_today +1 | 两次 stats 对比差异正确 |
| STAT-005 | 删除用户后统计更新 | 1. 获取 stats<br>2. 删除 1 个用户<br>3. 再次获取 | total_users -1 | 两次 stats 对比差异正确 |
| STAT-006 | 批量创建用户统计准确 | 1. 批量导入 100 个用户<br>2. 查询统计 | total_users 增加 100 | `SELECT COUNT(*) FROM users` 增加 100 |
### 2.2 登录统计正确性
**测试目标**:验证登录成功/失败次数统计的正确性
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|-----------|
| LOGIN-001 | 登录成功日志记录正确 | 1. 用户成功登录<br>2. 查询登录日志 | 1. 日志存在<br>2. status=1成功<br>3. user_id 正确 | `SELECT status, user_id FROM login_logs ORDER BY id DESC LIMIT 1` |
| LOGIN-002 | 登录失败日志记录正确 | 1. 用户使用错误密码登录<br>2. 查询登录日志 | 1. 日志存在<br>2. status=0失败<br>3. fail_reason 包含原因 | `SELECT status, fail_reason FROM login_logs ORDER BY id DESC LIMIT 1` |
| LOGIN-003 | 登录统计今日成功次数正确 | 1. 查询 logins_today_success<br>2. 3 个用户成功登录<br>3. 再次查询 | 第二次比第一次多 3 | `SELECT COUNT(*) FROM login_logs WHERE status=1 AND created_at >= today_start` |
| LOGIN-004 | 登录统计今日失败次数正确 | 1. 查询 logins_today_failed<br>2. 2 个用户密码错误登录失败<br>3. 再次查询 | 第二次比第一次多 2 | `SELECT COUNT(*) FROM login_logs WHERE status=0 AND created_at >= today_start` |
---
## 3. 设备信任管理正确性测试
### 3.1 设备信任状态变更
**测试目标**:验证设备信任/取消信任操作的正确性
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|-----------|
| DEV-001 | 用户信任当前设备 | 1. 用户登录新设备<br>2. 调用信任设备接口<br>3. 查询设备 | 1. API 返回 200<br>2. is_trusted=true<br>3. trust_expires_at 有值 | `SELECT is_trusted, trust_expires_at FROM devices WHERE id=xxx` |
| DEV-002 | 用户取消信任设备 | 1. 设备 is_trusted=true<br>2. 调用取消信任<br>3. 查询设备 | 1. API 返回 200<br>2. is_trusted=false<br>3. trust_expires_at=null | 同上is_trusted=false |
| DEV-003 | 管理员信任任意设备 | 1. 管理员调用 adminTrustDevice<br>2. 查询设备 | is_trusted=true, trust_expires_at 为 30 天后 | 同上 |
| DEV-004 | 管理员取消信任任意设备 | 1. 管理员调用 adminUntrustDevice<br>2. 查询设备 | is_trusted=false | 同上 |
| DEV-005 | 管理员删除设备 | 1. 管理员调用 adminDeleteDevice<br>2. 查询设备 | 设备不存在 | `SELECT COUNT(*) FROM devices WHERE id=xxx` 返回 0 |
| DEV-006 | 信任过期后状态正确 | 1. 设置 trust_expires_at 为过去时间<br>2. 查询设备状态 | is_trusted=false过期检查逻辑 | `SELECT is_trusted FROM devices WHERE trust_expires_at < now()` |
### 3.2 设备与用户关联正确性
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|-----------|
| DEV-007 | 设备正确归属用户 | 1. 用户 A 登录设备<br>2. 用户 B 查看自己的设备 | 1. 设备属于用户 A<br>2. 用户 B 看不到 | devices 表 user_id 字段正确 |
| DEV-008 | 管理员查看所有设备 | 1. 管理员调用 listAllDevices<br>2. 查看返回列表 | 包含所有用户的设备 | `SELECT COUNT(*) FROM devices` 与返回 total 一致 |
| DEV-009 | 管理员按用户筛选设备 | 1. 设置 user_id_filter=5<br>2. 调用 listAllDevices | 仅返回 user_id=5 的设备 | SQL: `WHERE user_id=5` |
---
## 4. 权限与角色正确性测试
### 4.1 角色分配正确性
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|-----------|
| ROLE-001 | 分配角色后用户拥有对应权限 | 1. 用户无角色<br>2. 分配 role_id=2<br>3. 验证用户权限 | 用户拥有 role_id=2 的所有权限 | `SELECT permission_id FROM role_permissions WHERE role_id=2` 与用户实际权限对比 |
| ROLE-002 | 分配多个角色权限合并 | 1. 分配 role_ids=[2,3]<br>2. 验证用户权限 | 用户拥有 role 2 和 role 3 的所有权限并集 | 权限数量 = role2 权限数 + role3 权限数(去重) |
| ROLE-003 | 移除用户角色 | 1. 用户有角色<br>2. 移除角色<br>3. 验证权限减少 | 用户失去被移除角色的权限 | 移除前后的权限数量对比 |
| ROLE-004 | 角色状态为禁用时用户无该角色权限 | 1. 角色 status=0禁用<br>2. 用户拥有该角色<br>3. 验证权限 | 用户不拥有该角色的任何权限 | 权限检查跳过 status=0 的角色 |
### 4.2 权限继承正确性
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 | 数据库验证 |
|---------|---------|---------|---------|-----------|
| PERM-001 | 子权限继承父权限 | 1. 权限 A 是权限 B 的父级<br>2. 用户拥有 A<br>3. 检查 B 的访问 | 用户同时拥有 A 和 B | 权限树结构验证 |
| PERM-002 | 权限树深度遍历正确 | 1. 用户拥有叶节点权限<br>2. 检查所有祖先权限 | 用户拥有完整路径上的所有权限 | 递归查询权限树 |
---
## 5. 前端行为与后端数据一致性
### 5.1 表单提交与数据库
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 |
|---------|---------|---------|---------|
| FE-001 | 创建用户表单提交后数据库正确 | 1. 填写用户名、邮箱、密码<br>2. 提交<br>3. 页面刷新后数据存在 | 数据库用户表有一条对应记录 |
| FE-002 | 编辑用户信息后数据库同步 | 1. 修改用户昵称<br>2. 保存<br>3. 重新加载页面 | 数据库 nickname 字段已更新 |
| FE-003 | 删除用户后列表刷新 | 1. 删除用户<br>2. 页面自动刷新 | 列表中不再显示该用户,数据库中已删除 |
| FE-004 | 筛选条件变更后列表正确 | 1. 选择"仅活跃用户"<br>2. 查看列表 | 仅显示 status=1 的用户 |
### 5.2 批量操作一致性
| 用例编号 | 用例描述 | 测试步骤 | 预期结果 |
|---------|---------|---------|---------|
| FE-005 | 批量启用用户后数据库一致 | 1. 选中 10 个用户<br>2. 批量启用<br>3. 刷新页面 | 10 个用户 status=1 |
| FE-006 | 批量导入用户后统计正确 | 1. 导入 CSV50 个用户)<br>2. 查看统计 | total_users 增加 50 |
| FE-007 | 批量导出数据完整 | 1. 导出当前用户列表<br>2. 比对记录数 | 导出数量 = 数据库实际数量 |
---
## 6. 测试数据准备脚本
### 6.1 用户统计测试数据准备
```sql
-- 清理测试数据
DELETE FROM users WHERE username LIKE 'stat_test_%';
-- 插入不同状态的用户
INSERT INTO users (username, email, password, status, created_at) VALUES
('stat_test_active_1', 'active1@test.com', '$2a$10$...', 1, NOW()),
('stat_test_active_2', 'active2@test.com', '$2a$10$...', 1, NOW()),
('stat_test_locked', 'locked@test.com', '$2a$10$...', 2, NOW()),
('stat_test_disabled', 'disabled@test.com', '$2a$10$...', 3, NOW()),
('stat_test_inactive', 'inactive@test.com', '$2a$10$...', 0, NOW() - INTERVAL '2 days');
```
### 6.2 登录日志测试数据准备
```sql
-- 清理测试数据
DELETE FROM login_logs WHERE user_id IN (SELECT id FROM users WHERE username LIKE 'login_test_%');
-- 准备测试用户
INSERT INTO users (username, email, password, status) VALUES
('login_test_user', 'logintest@test.com', '$2a$10$...', 1);
-- 插入登录日志
INSERT INTO login_logs (user_id, login_type, status, ip, created_at) VALUES
(currval('users_id_seq'), 1, 1, '192.168.1.1', NOW()),
(currval('users_id_seq'), 1, 0, '192.168.1.2', NOW() - INTERVAL '1 hour'),
(currval('users_id_seq'), 1, 1, '192.168.1.3', NOW() - INTERVAL '2 hours');
```
---
## 7. 测试执行检查清单
### 7.1 测试前检查
- [ ] 测试数据库已初始化
- [ ] 测试用户已创建(有管理员权限)
- [ ] API 服务运行正常
- [ ] 前端开发服务器运行正常
### 7.2 测试后清理
- [ ] 测试数据已清理
- [ ] 无残留测试用户
- [ ] 统计数据已恢复
### 7.3 成功标准
- [ ] 所有测试用例通过
- [ ] 数据库验证全部符合预期
- [ ] 前端行为与数据库状态一致
- [ ] 统计数据计算误差为 0

View File

@@ -0,0 +1,584 @@
# 真实数据量性能与压力测试方案
## 概述
本文档定义在大规模数据场景下的系统性能与可用性测试方案,模拟 10 万级用户、百万级登录日志、权限树爆炸等极端情况,验证系统在各数据量级别下的功能可用性、性能表现和运维能力。
---
## 1. 测试目标与范围
### 1.1 测试目标
| 目标 | 指标 | 说明 |
|-----|------|-----|
| 用户规模支撑 | 100,000 用户 | 系统在 10 万用户规模下功能正常 |
| 登录日志规模 | 1,000,000 条 | 百万级日志下的查询、导出性能 |
| 权限树规模 | 500+ 权限节点 | 权限树爆炸场景下的加载性能 |
| 筛选搜索可用性 | 响应时间 < 2s | 大数据量下筛选搜索不超时 |
| 批量操作稳定性 | 1000 条/批 | 批量操作不出现 OOM 或超时 |
### 1.2 测试范围
- 用户列表查询与筛选
- 登录日志查询与导出
- 设备列表查询与管理
- 权限树加载与渲染
- 仪表盘统计加载
- 批量导入/导出操作
---
## 2. 测试数据准备
### 2.1 数据规模规划
| 数据类型 | 小规模 | 中规模 | 大规模 | 极端规模 |
|---------|-------|-------|-------|---------|
| 用户数 | 1,000 | 10,000 | 50,000 | 100,000 |
| 登录日志 | 10,000 | 100,000 | 500,000 | 1,000,000 |
| 设备数 | 2,000 | 20,000 | 100,000 | 200,000 |
| 角色数 | 10 | 50 | 100 | 200 |
| 权限数 | 50 | 200 | 500 | 1,000 |
| 用户角色关联 | 1,500 | 15,000 | 75,000 | 150,000 |
| 登录日志保留天数 | 7 天 | 30 天 | 90 天 | 180 天 |
### 2.2 测试数据生成脚本
#### 2.2.1 用户数据生成
```python
# scripts/generate_test_users.py
import random
import string
from datetime import datetime, timedelta
def generate_users(count: int, start_id: int = 1):
"""生成测试用户数据"""
statuses = [0, 1, 1, 1, 1, 2, 3] # 权重: 14%未激活, 57%活跃, 14%锁定, 14%禁用
users = []
for i in range(count):
user_id = start_id + i
created_at = datetime.now() - timedelta(
days=random.randint(0, 365),
hours=random.randint(0, 23)
)
users.append({
'id': user_id,
'username': f'testuser_{user_id}',
'email': f'testuser_{user_id}@test.com',
'phone': f'138{random.randint(10000000, 99999999)}',
'password': '$2a$10$dummy_hash_for_test_data',
'status': random.choice(statuses),
'created_at': created_at.isoformat(),
'updated_at': created_at.isoformat(),
})
return users
# 生成 10 万用户
users = generate_users(100000)
```
#### 2.2.2 登录日志数据生成
```sql
-- 使用 PostgreSQL 生成大规模登录日志
-- 登录日志生成函数
CREATE OR REPLACE FUNCTION generate_login_logs(
user_count INT,
logs_per_user INT
) RETURNS VOID AS $$
DECLARE
i INT;
j INT;
user_ids INT[];
statuses INT[];
login_types INT[];
BEGIN
-- 准备用户 ID 数组
SELECT array_agg(id) INTO user_ids FROM users LIMIT user_count;
statuses := ARRAY[0, 1, 1, 1, 1, 1, 1, 1, 1, 1]; -- 90% 成功
login_types := ARRAY[1, 2, 3, 4];
FOR i IN 1..logs_per_user LOOP
FOR j IN 1..user_count LOOP
INSERT INTO login_logs (
user_id,
login_type,
device_id,
ip,
location,
status,
fail_reason,
created_at
) VALUES (
user_ids[j],
login_types[1 + floor(random() * 4)::int],
'device_' || user_ids[j] || '_' || i,
'192.168.' || (1 + floor(random() * 255))::int || '.' || (1 + floor(random() * 255))::int,
CASE floor(random() * 5)::int
WHEN 0 THEN '北京'
WHEN 1 THEN '上海'
WHEN 2 THEN '深圳'
WHEN 3 THEN '杭州'
ELSE '广州'
END,
statuses[1 + floor(random() * 10)::int],
CASE
WHEN statuses[1 + floor(random() * 10)::int] = 0
THEN CASE floor(random() * 3)::int
WHEN 0 THEN '密码错误'
WHEN 1 THEN '账号已锁定'
ELSE '账号已禁用'
END
ELSE NULL
END,
NOW() - (random() * 365 || ' days')::interval
);
END LOOP;
-- 每 10000 条输出进度
IF i % 100 = 0 THEN
RAISE NOTICE 'Generated % login cycles for % users', i, user_count;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- 执行生成 100 万条日志100 用户 x 10000 次登录)
-- SELECT generate_login_logs(100, 10000);
```
#### 2.2.3 权限树数据生成
```sql
-- 权限树生成脚本
-- 生成 500 个权限节点
INSERT INTO permissions (name, code, parent_id, sort_order, created_at) VALUES
-- 系统管理(父节点)
('系统管理', 'system', NULL, 1, NOW()),
('系统配置', 'system.config', 1, 1, NOW()),
('系统日志', 'system.logs', 1, 2, NOW()),
('用户管理', 'system.users', 1, 3, NOW()),
('用户列表', 'system.users.list', 4, 1, NOW()),
('用户创建', 'system.users.create', 4, 2, NOW()),
('用户编辑', 'system.users.edit', 4, 3, NOW()),
('用户删除', 'system.users.delete', 4, 4, NOW()),
('用户导出', 'system.users.export', 4, 5, NOW()),
('角色管理', 'system.roles', 1, 4, NOW()),
('角色列表', 'system.roles.list', 10, 1, NOW()),
('角色创建', 'system.roles.create', 10, 2, NOW()),
('角色编辑', 'system.roles.edit', 10, 3, NOW()),
('角色删除', 'system.roles.delete', 10, 4, NOW()),
('分配权限', 'system.roles.assign', 10, 5, NOW()),
('权限管理', 'system.permissions', 1, 5, NOW()),
('设备管理', 'system.devices', 1, 6, NOW()),
('设备列表', 'system.devices.list', 16, 1, NOW()),
('设备详情', 'system.devices.view', 16, 2, NOW()),
('设备删除', 'system.devices.delete', 16, 3, NOW()),
('设备信任', 'system.devices.trust', 16, 4, NOW()),
('登录日志', 'system.login_logs', 1, 7, NOW()),
('登录日志列表', 'system.login_logs.list', 21, 1, NOW()),
('登录日志导出', 'system.login_logs.export', 21, 2, NOW()),
('操作日志', 'system.operation_logs', 1, 8, NOW()),
('操作日志列表', 'system.operation_logs.list', 23, 1, NOW()),
('操作日志详情', 'system.operation_logs.view', 23, 2, NOW()),
('操作日志导出', 'system.operation_logs.export', 23, 3, NOW()),
-- 业务管理
('业务管理', 'business', NULL, 2, NOW()),
('业务数据', 'business.data', 26, 1, NOW()),
('数据查看', 'business.data.view', 27, 1, NOW()),
('数据编辑', 'business.data.edit', 27, 2, NOW()),
('数据删除', 'business.data.delete', 27, 3, NOW()),
('数据导入', 'business.data.import', 27, 4, NOW()),
('数据导出', 'business.data.export', 27, 5, NOW()),
('报表管理', 'business.reports', 26, 2, NOW()),
('报表查看', 'business.reports.view', 30, 1, NOW()),
('报表生成', 'business.reports.generate', 30, 2, NOW()),
('报表导出', 'business.reports.export', 30, 3, NOW()),
-- 扩展权限(模拟权限树爆炸)
('扩展功能A', 'ext_a', NULL, 3, NOW()),
('扩展功能B', 'ext_b', NULL, 4, NOW()),
('扩展功能C', 'ext_c', NULL, 5, NOW());
```
---
## 3. 测试用例
### 3.1 用户列表大规模测试
#### UL-001: 10 万用户分页查询性能
| 属性 | 内容 |
|-----|------|
| 用例编号 | UL-001 |
| 用例名称 | 10 万用户分页查询性能 |
| 测试步骤 | 1. 准备 100,000 用户数据<br>2. 调用 GET /admin/users?page=1&page_size=20<br>3. 记录响应时间<br>4. 翻页到第 5000 页 |
| 预期结果 | 1. 首次加载 < 1s<br>2. 任意页加载 < 2s<br>3. 返回数据完整 |
| 性能指标 | p95 < 2000ms |
| 数据库索引 | user_id (PK), status, created_at |
#### UL-002: 大数据量关键词搜索
| 属性 | 内容 |
|-----|------|
| 用例编号 | UL-002 |
| 用例名称 | 10 万用户关键词搜索响应时间 |
| 测试步骤 | 1. 准备 100,000 用户数据<br>2. 执行关键词搜索 "testuser_50000"<br>3. 测量响应时间 |
| 预期结果 | 1. 搜索响应 < 2s<br>2. 返回结果正确 |
| 性能指标 | p95 < 2000ms |
#### UL-003: 多条件组合筛选
| 属性 | 内容 |
|-----|------|
| 用例编号 | UL-003 |
| 用例名称 | 多条件组合筛选性能 |
| 测试步骤 | 1. 筛选 status=1, role_ids=2, created_from=2024-01-01<br>2. 测量响应时间 |
| 预期结果 | 1. 响应 < 2s<br>2. 结果正确 |
| 性能指标 | p95 < 2000ms |
#### UL-004: 用户导出 10 万级数据
| 属性 | 内容 |
|-----|------|
| 用例编号 | UL-004 |
| 用例名称 | 10 万用户 CSV 导出 |
| 测试步骤 | 1. 导出全部 100,000 用户<br>2. 测量导出时间<br>3. 验证导出文件完整性 |
| 预期结果 | 1. 导出完成 < 60s<br>2. 文件可正常打开<br>3. 数据量 = 100,000 条 |
| 性能指标 | 内存占用 < 512MB, 时间 < 60s |
---
### 3.2 登录日志大规模测试
#### LL-001: 百万登录日志分页查询
| 属性 | 内容 |
|-----|------|
| 用例编号 | LL-001 |
| 用例名称 | 100 万登录日志分页查询 |
| 测试步骤 | 1. 准备 1,000,000 登录日志<br>2. 调用 GET /admin/logs/login?page=1&page_size=50<br>3. 翻到第 10000 页 |
| 预期结果 | 1. 首页加载 < 1s<br>2. 任意页加载 < 2s |
| 性能指标 | p95 < 2000ms |
#### LL-002: 百万日志时间范围查询
| 属性 | 内容 |
|-----|------|
| 用例编号 | LL-002 |
| 用例名称 | 百万日志按时间范围筛选 |
| 测试步骤 | 1. 查询最近 30 天日志<br>2. 测量响应时间 |
| 预期结果 | 1. 响应 < 3s<br>2. 返回结果数量合理 |
| 性能指标 | p95 < 3000ms |
#### LL-003: 百万日志导出 CSV
| 属性 | 内容 |
|-----|------|
| 用例编号 | LL-003 |
| 用例名称 | 100 万登录日志 CSV 导出 |
| 测试步骤 | 1. 导出 1,000,000 条登录日志<br>2. 测量导出时间<br>3. 验证文件完整性 |
| 预期结果 | 1. 导出时间 < 120s<br>2. CSV 文件可正常打开<br>3. 数据量正确 |
| 性能指标 | 流式导出,内存占用 < 256MB |
#### LL-004: 百万日志导出 XLSX
| 属性 | 内容 |
|-----|------|
| 用例编号 | LL-004 |
| 用例名称 | 100 万登录日志 XLSX 导出 |
| 测试步骤 | 1. 导出 1,000,000 条登录日志为 xlsx<br>2. 测量导出时间 |
| 预期结果 | 1. 导出完成<br>2. 文件可正常打开 |
| 性能指标 | 内存占用 < 1GBXLSX 单文件限制) |
---
### 3.3 设备列表大规模测试
#### DV-001: 20 万设备分页查询
| 属性 | 内容 |
|-----|------|
| 用例编号 | DV-001 |
| 用例名称 | 20 万设备分页查询性能 |
| 测试步骤 | 1. 准备 200,000 设备数据<br>2. 调用 GET /admin/devices?page=1&page_size=20 |
| 预期结果 | 1. 响应 < 2s<br>2. 数据完整 |
| 性能指标 | p95 < 2000ms |
#### DV-002: 设备列表多条件筛选
| 属性 | 内容 |
|-----|------|
| 用例编号 | DV-002 |
| 用例名称 | 设备列表多条件筛选 |
| 测试步骤 | 1. 设置筛选status=1, is_trusted=true, user_id=100<br>2. 测量响应时间 |
| 预期结果 | 1. 响应 < 2s<br>2. 结果正确 |
| 性能指标 | p95 < 2000ms |
---
### 3.4 权限树大规模测试
#### PR-001: 500 权限节点加载
| 属性 | 内容 |
|-----|------|
| 用例编号 | PR-001 |
| 用例名称 | 500 权限节点树加载性能 |
| 测试步骤 | 1. 准备 500 个权限节点<br>2. 调用权限树接口<br>3. 测量前端渲染时间 |
| 预期结果 | 1. 接口响应 < 500ms<br>2. 前端渲染 < 1s |
| 性能指标 | 接口 p95 < 500ms, 前端渲染 < 1000ms |
#### PR-002: 1000 权限节点树爆炸测试
| 属性 | 内容 |
|-----|------|
| 用例编号 | PR-002 |
| 用例名称 | 1000 权限节点树爆炸场景 |
| 测试步骤 | 1. 准备 1000 个权限节点5 层深度)<br>2. 前端加载权限树<br>3. 测量性能 |
| 预期结果 | 1. 加载不超时<br>2. UI 不卡顿 |
| 性能指标 | 内存占用 < 100MB |
#### PR-003: 角色权限分配大规模场景
| 属性 | 内容 |
|-----|------|
| 用例编号 | PR-003 |
| 用例名称 | 角色分配 500+ 权限性能 |
| 测试步骤 | 1. 选择有 500 个权限的角色<br>2. 勾选全部权限<br>3. 保存分配 |
| 预期结果 | 1. 保存成功 < 2s<br>2. 权限正确入库 |
| 性能指标 | p95 < 2000ms |
---
### 3.5 仪表盘统计性能测试
#### DS-001: 大数据量仪表盘加载
| 属性 | 内容 |
|-----|------|
| 用例编号 | DS-001 |
| 用例名称 | 10 万用户仪表盘统计性能 |
| 测试步骤 | 1. 准备 100,000 用户<br>2. 打开仪表盘页面<br>3. 测量加载时间 |
| 预期结果 | 1. 仪表盘加载 < 3s<br>2. 所有统计数据正确显示 |
| 性能指标 | p95 < 3000ms |
#### DS-002: 仪表盘统计数据准确性
| 属性 | 内容 |
|-----|------|
| 用例编号 | DS-002 |
| 用例名称 | 大数据量统计准确性验证 |
| 测试步骤 | 1. 获取仪表盘统计<br>2. 直接查询数据库验证 |
| 预期结果 | API 返回值与数据库 COUNT 完全一致 |
| 准确性 | 误差 = 0 |
---
### 3.6 批量操作压力测试
#### BO-001: 批量导入 1000 用户
| 属性 | 内容 |
|-----|------|
| 用例编号 | BO-001 |
| 用例名称 | CSV 批量导入 1000 用户 |
| 测试步骤 | 1. 准备 1000 用户的 CSV 文件<br>2. 执行批量导入<br>3. 测量导入时间 |
| 预期结果 | 1. 导入完成 < 30s<br>2. 全部用户创建成功<br>3. 统计数据显示正确 |
| 性能指标 | < 30s, 内存 < 512MB |
#### BO-002: 批量启用/禁用用户
| 属性 | 内容 |
|-----|------|
| 用例编号 | BO-002 |
| 用例名称 | 批量更新 1000 用户状态 |
| 测试步骤 | 1. 选择 1000 个用户<br>2. 执行批量禁用<br>3. 测量响应时间 |
| 预期结果 | 1. 操作完成 < 10s<br>2. 所有用户状态更新正确 |
| 性能指标 | < 10s |
---
## 4. 性能基准与验收标准
### 4.1 响应时间标准
| 操作类型 | p50 | p95 | p99 | 最大值 |
|---------|-----|-----|-----|-------|
| 分页列表查询20条 | < 200ms | < 1000ms | < 2000ms | < 5000ms |
| 关键词搜索 | < 500ms | < 2000ms | < 3000ms | < 5000ms |
| 详情查看 | < 100ms | < 500ms | < 1000ms | < 2000ms |
| 创建/更新操作 | < 200ms | < 1000ms | < 2000ms | < 5000ms |
| 删除操作 | < 200ms | < 500ms | < 1000ms | < 2000ms |
| CSV 导出10万条 | < 30s | < 60s | < 90s | < 120s |
| XLSX 导出10万条 | < 60s | < 120s | < 180s | < 300s |
### 4.2 资源使用标准
| 资源 | 正常范围 | 告警阈值 | 严重阈值 |
|-----|---------|---------|---------|
| API 服务器 CPU | < 50% | 70% | 85% |
| API 服务器内存 | < 60% | 75% | 85% |
| 数据库 CPU | < 40% | 60% | 80% |
| 数据库内存 | < 50% | 70% | 85% |
| 数据库连接数 | < 50 | 80 | 100 |
| 磁盘 I/O | < 50% | 70% | 85% |
### 4.3 功能正确性标准
- [ ] 统计数据显示误差为 0
- [ ] 筛选结果与数据库一致
- [ ] 分页数据无遗漏无重复
- [ ] 导出数据与列表数据一致
- [ ] 批量操作无数据丢失
---
## 5. 测试执行方案
### 5.1 测试阶段规划
| 阶段 | 数据规模 | 测试内容 | 预计时间 |
|-----|---------|---------|---------|
| Phase 1 | 1,000 用户 | 基础功能验证 | 2h |
| Phase 2 | 10,000 用户 | 中等规模性能基线 | 4h |
| Phase 3 | 50,000 用户 | 大规模验证 | 8h |
| Phase 4 | 100,000 用户 | 极端规模稳定性 | 8h |
### 5.2 测试环境要求
| 环境 | 配置 | 说明 |
|-----|------|-----|
| CPU | 8 核+ | API 和数据库服务器 |
| 内存 | 16GB+ | API 和数据库服务器 |
| 磁盘 | 100GB+ SSD | 存放测试数据 |
| 数据库 | PostgreSQL 14+ | 测试数据库 |
| 网络 | 1Gbps+ | 内部网络 |
### 5.3 测试监控指标
测试过程中监控以下指标:
- API 响应时间分布
- 数据库查询时间
- 内存使用率
- CPU 使用率
- 数据库连接数
- 错误率
---
## 6. 问题记录与复盘
### 6.1 性能问题分级
| 级别 | 定义 | 处理方式 |
|-----|------|---------|
| P0 | 功能不可用 | 立即修复 |
| P1 | 性能严重不达标(> 3x | 2天内修复 |
| P2 | 性能轻微不达标1.5-3x | 1周内修复 |
| P3 | 优化建议 | 规划中处理 |
### 6.2 复盘内容
每次大规模测试后记录:
- 实际性能数据与预期对比
- 发现的问题及处理方式
- 优化建议
- 下次测试重点
---
## 7. 测试脚本工具
### 7.1 locust 压力测试脚本
```python
# tests/load_test_locust.py
from locust import HttpUser, task, between
import random
class AdminUser(HttpUser):
wait_time = between(1, 3)
@task(3)
def list_users(self):
page = random.randint(1, 100)
self.client.get(f"/api/v1/admin/users?page={page}&page_size=20")
@task(1)
def search_users(self):
keyword = f"testuser_{random.randint(1, 100000)}"
self.client.get(f"/api/v1/admin/users?keyword={keyword}")
@task(2)
def list_login_logs(self):
page = random.randint(1, 1000)
self.client.get(f"/api/v1/logs/login?page={page}&page_size=50")
@task(1)
def get_dashboard(self):
self.client.get("/api/v1/admin/dashboard/stats")
```
### 7.2 k6 性能测试脚本
```javascript
// tests/load_test_k6.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 }, // 预热
{ duration: '5m', target: 100 }, // 稳定
{ duration: '2m', target: 200 }, // 峰值
{ duration: '5m', target: 200 }, // 持续
{ duration: '2m', target: 0 }, // 冷却
],
thresholds: {
http_req_duration: ['p(95)<2000'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const res = http.get(`${__ENV.BASE_URL}/api/v1/admin/users?page=1&page_size=20`);
check(res, {
'status was 200': (r) => r.status === 200,
'response time < 2s': (r) => r.timings.duration < 2000,
});
sleep(1);
}
```
---
## 8. 验收检查清单
### 8.1 功能验收
- [ ] 用户列表 10 万级数据加载正常
- [ ] 登录日志 100 万级数据查询正常
- [ ] 设备列表 20 万级数据管理正常
- [ ] 权限树 500+ 节点加载正常
- [ ] 批量导入/导出功能正常
### 8.2 性能验收
- [ ] 分页查询 p95 < 2s
- [ ] 搜索查询 p95 < 2s
- [ ] 10 万用户导出 < 60s
- [ ] 100 万日志导出 < 120s
- [ ] 仪表盘加载 < 3s
### 8.3 稳定性验收
- [ ] 连续压测 30 分钟无内存泄漏
- [ ] 错误率 < 0.1%
- [ ] 无 OOM 或崩溃