refactor: 重构文档结构和文件位置
docs: 添加Redis集成测试文档 docs: 添加ID生成器分析报告 docs: 添加自由布局和固定布局示例文档 test: 添加ID生成器单元测试 fix: 删除重复的前端文档文件
This commit is contained in:
@ -1,78 +0,0 @@
|
||||
# Redis集成测试
|
||||
|
||||
这个文件包含了Redis集成的测试说明。
|
||||
|
||||
## 环境配置
|
||||
|
||||
1. 复制环境配置文件:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. 根据需要修改 `.env` 文件中的Redis配置:
|
||||
```bash
|
||||
REDIS_URL=redis://:123456@127.0.0.1:6379/9
|
||||
```
|
||||
|
||||
## 启动Redis服务
|
||||
|
||||
确保Redis服务器正在运行:
|
||||
```bash
|
||||
# 使用Docker运行Redis
|
||||
docker run -d -p 6379:6379 --name redis-udmin redis:7-alpine --requirepass 123456
|
||||
|
||||
# 或者使用本地Redis服务
|
||||
redis-server --requirepass 123456
|
||||
```
|
||||
|
||||
## 测试Redis连接
|
||||
|
||||
启动服务器:
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
查看日志中是否有Redis连接成功的信息:
|
||||
```
|
||||
[INFO] Connecting to Redis at: redis://:***@127.0.0.1:6379/9
|
||||
[INFO] Redis connection established successfully
|
||||
```
|
||||
|
||||
## 测试Token存储
|
||||
|
||||
1. 登录获取token:
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"Admin@123"}'
|
||||
```
|
||||
|
||||
2. 检查Redis中的token:
|
||||
```bash
|
||||
# 连接到Redis CLI
|
||||
redis-cli -a 123456 -n 9
|
||||
|
||||
# 查看所有token键
|
||||
KEYS token:*
|
||||
|
||||
# 查看特定用户的token
|
||||
GET token:access:user:1
|
||||
GET token:refresh:user:1
|
||||
```
|
||||
|
||||
## 功能验证
|
||||
|
||||
Redis集成后的新功能:
|
||||
|
||||
1. **Token存储**:所有JWT token都存储在Redis中
|
||||
2. **Token验证**:每次API调用都会验证Redis中的token
|
||||
3. **Token撤销**:logout时会从Redis中删除token
|
||||
4. **性能提升**:减少数据库查询,提升认证性能
|
||||
5. **单点登录**:防止多重登录(可配置)
|
||||
|
||||
## 环境变量说明
|
||||
|
||||
- `REDIS_URL`: Redis连接字符串
|
||||
- `REDIS_TOKEN_VALIDATION`: 是否启用Redis token验证(默认true)
|
||||
- `JWT_ACCESS_EXP_SECS`: 访问token过期时间(秒)
|
||||
- `JWT_REFRESH_EXP_SECS`: 刷新token过期时间(秒)
|
||||
@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "Branch A->COND->B/C (async)",
|
||||
"code": "branch_async",
|
||||
"design_json": {
|
||||
"name": "Branch A->COND->B/C (async)",
|
||||
"execution_mode": "async",
|
||||
"nodes": [
|
||||
{ "id": "A", "kind": "http", "name": "http A", "task": "http", "config": { "url": "https://httpbin.org/get", "method": "GET" } },
|
||||
{ "id": "COND", "kind": "condition", "name": "cond", "task": "condition", "config": { "expression": { "left": { "type": "constant", "value": true }, "operator": "is_true", "right": { "type": "constant", "value": true } } }, "ports": { "yes": { "id": "yes" }, "no": { "id": "no" } } },
|
||||
{ "id": "B", "kind": "http", "name": "http B", "task": "http", "config": { "url": "https://httpbin.org/anything/B", "method": "GET" } },
|
||||
{ "id": "C", "kind": "http", "name": "http C", "task": "http", "config": { "url": "https://httpbin.org/anything/C", "method": "GET" } }
|
||||
],
|
||||
"edges": [
|
||||
{ "from": "A", "to": "COND" },
|
||||
{ "from": "COND", "to": "B", "condition": { "left": { "type": "constant", "value": true }, "operator": "is_true", "right": { "type": "constant", "value": true } } },
|
||||
{ "from": "COND", "to": "C", "condition": { "left": { "type": "constant", "value": true }, "operator": "is_false", "right": { "type": "constant", "value": true } } }
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "branch-sync",
|
||||
"code": "branch_sync_1",
|
||||
"design_json": {
|
||||
"name": "branch-sync",
|
||||
"executionMode": "sync",
|
||||
"nodes": [
|
||||
{ "id": "A", "type": "http", "data": { "title": "A-GET-x", "api": { "method": "GET", "url": "https://httpbin.org/get?x=hello" } } },
|
||||
{ "id": "COND", "type": "condition", "data": { "title": "COND", "conditions": [
|
||||
{ "key": "yes", "value": { "left": { "type": "constant", "content": true }, "operator": "is_true" } },
|
||||
{ "key": "no", "value": { "left": { "type": "constant", "content": false }, "operator": "is_true" } }
|
||||
] } },
|
||||
{ "id": "B", "type": "http", "data": { "title": "B-UUID", "api": { "method": "GET", "url": "https://httpbin.org/uuid" } } },
|
||||
{ "id": "C", "type": "http", "data": { "title": "C-Delay1s", "api": { "method": "GET", "url": "https://httpbin.org/delay/1" } } }
|
||||
],
|
||||
"edges": [
|
||||
{ "sourceNodeID": "A", "targetNodeID": "COND" },
|
||||
{ "sourceNodeID": "COND", "targetNodeID": "B", "sourcePortID": "yes" },
|
||||
{ "sourceNodeID": "COND", "targetNodeID": "C", "sourcePortID": "no" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
#HttpOnly_localhost FALSE / FALSE 1756996792 refresh_token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInVpZCI6MSwiaXNzIjoidWRtaW4iLCJleHAiOjE3NTY5OTY3OTIsInR5cCI6InJlZnJlc2gifQ.XllW1VXSni2F548WFm1tjG3gwWPou_QE1JRabz0RZTE
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"yaml": "# demo flow\nnodes:\n - { id: start, kind: start, name: 开始 }\n - { id: assign, kind: custom, name: 赋值, script: ctx.x = ctx.x + 1; }\n - { id: cond, kind: custom, name: 条件 }\n - { id: end, kind: end, name: 结束 }\nedges:\n - { from: start, to: assign }\n - { from: assign, to: cond }\n - { from: cond, to: end, condition: ctx.x >= 1 }\n - { from: cond, to: end }\n"
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "linear-async",
|
||||
"code": "linear_async_1",
|
||||
"design_json": {
|
||||
"name": "linear-async",
|
||||
"executionMode": "async",
|
||||
"nodes": [
|
||||
{ "id": "N1", "type": "http", "data": { "title": "A-GET", "api": { "method": "GET", "url": "https://httpbin.org/delay/1" } } },
|
||||
{ "id": "N2", "type": "http", "data": { "title": "B-UUID", "api": { "method": "GET", "url": "https://httpbin.org/uuid" } } }
|
||||
],
|
||||
"edges": [
|
||||
{ "sourceNodeID": "N1", "targetNodeID": "N2" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "linear-sync",
|
||||
"code": "linear_sync_1",
|
||||
"design_json": {
|
||||
"name": "linear-sync",
|
||||
"executionMode": "sync",
|
||||
"nodes": [
|
||||
{ "id": "N1", "type": "http", "data": { "title": "A-GET", "api": { "method": "GET", "url": "https://httpbin.org/get" } } },
|
||||
{ "id": "N2", "type": "http", "data": { "title": "B-UUID", "api": { "method": "GET", "url": "https://httpbin.org/uuid" } } }
|
||||
],
|
||||
"edges": [
|
||||
{ "sourceNodeID": "N1", "targetNodeID": "N2" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -78,4 +78,155 @@ const REQUEST_LOG_SUB_ID: u8 = 1;
|
||||
|
||||
pub fn generate_request_log_id() -> i64 {
|
||||
generate_biz_id(BizIdConfig::new(REQUEST_LOG_MAIN_ID, REQUEST_LOG_SUB_ID))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn test_id_sequential_generation() {
|
||||
// 测试1: 连续生成ID,验证递增性
|
||||
let mut prev_id = 0i64;
|
||||
for i in 1..=10 {
|
||||
let current_id = generate_id();
|
||||
println!("ID {}: {}", i, current_id);
|
||||
|
||||
if i > 1 {
|
||||
assert!(current_id > prev_id,
|
||||
"ID {} ({}) 应该大于前一个ID {} ({})", i, current_id, i-1, prev_id);
|
||||
}
|
||||
prev_id = current_id;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_time_interval_generation() {
|
||||
// 测试2: 间隔时间生成ID,验证时间戳影响
|
||||
let mut time_based_ids = Vec::new();
|
||||
for i in 1..=5 {
|
||||
let id = generate_id();
|
||||
time_based_ids.push(id);
|
||||
println!("时间间隔ID {}: {}", i, id);
|
||||
thread::sleep(Duration::from_millis(10)); // 减少等待时间以加快测试
|
||||
}
|
||||
|
||||
// 验证时间间隔ID的递增性
|
||||
for i in 1..time_based_ids.len() {
|
||||
assert!(time_based_ids[i] > time_based_ids[i-1],
|
||||
"时间间隔ID {} ({}) 应该大于前一个ID ({})",
|
||||
i+1, time_based_ids[i], time_based_ids[i-1]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_business_id_types() {
|
||||
// 测试3: 不同业务类型ID的递增性
|
||||
let flow_id1 = generate_biz_id(BizIdConfig::new(1, 1));
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
let flow_id2 = generate_biz_id(BizIdConfig::new(1, 1));
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
let log_id1 = generate_biz_id(BizIdConfig::new(2, 1));
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
let log_id2 = generate_biz_id(BizIdConfig::new(2, 1));
|
||||
|
||||
println!("Flow ID 1: {}", flow_id1);
|
||||
println!("Flow ID 2: {}", flow_id2);
|
||||
println!("Log ID 1: {}", log_id1);
|
||||
println!("Log ID 2: {}", log_id2);
|
||||
|
||||
// 验证同类型业务ID递增
|
||||
assert!(flow_id2 > flow_id1, "Flow ID 2 应该大于 Flow ID 1");
|
||||
assert!(log_id2 > log_id1, "Log ID 2 应该大于 Log ID 1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_concurrent_id_generation() {
|
||||
// 测试4: 多线程并发生成ID测试
|
||||
let handles: Vec<_> = (0..3).map(|thread_id| {
|
||||
thread::spawn(move || {
|
||||
let mut thread_ids = Vec::new();
|
||||
for _ in 0..5 {
|
||||
thread_ids.push(generate_id());
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
(thread_id, thread_ids)
|
||||
})
|
||||
}).collect();
|
||||
|
||||
let mut all_ids = Vec::new();
|
||||
for handle in handles {
|
||||
let (thread_id, ids) = handle.join().unwrap();
|
||||
println!("线程 {} 生成的ID: {:?}", thread_id, ids);
|
||||
all_ids.extend(ids);
|
||||
}
|
||||
|
||||
// 验证所有ID的唯一性
|
||||
let unique_ids: HashSet<_> = all_ids.iter().collect();
|
||||
assert_eq!(all_ids.len(), unique_ids.len(),
|
||||
"发现重复ID,总数: {}, 唯一数: {}", all_ids.len(), unique_ids.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_timestamp_parsing() {
|
||||
// 测试5: 解析ID验证时间戳部分
|
||||
let id1 = generate_id();
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
let id2 = generate_id();
|
||||
|
||||
// 提取时间戳部分(低39位中的时间戳)
|
||||
let timestamp1 = id1 & ((1i64 << 39) - 1);
|
||||
let timestamp2 = id2 & ((1i64 << 39) - 1);
|
||||
|
||||
println!("ID1: {}, 时间戳部分: {}", id1, timestamp1);
|
||||
println!("ID2: {}, 时间戳部分: {}", id2, timestamp2);
|
||||
|
||||
// 注意:在同一毫秒内生成的ID,时间戳部分可能相同,但序列号会递增
|
||||
// 所以这里只验证ID2不小于ID1
|
||||
assert!(id2 > id1, "ID2 应该大于 ID1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_biz_id_parsing() {
|
||||
// 测试6: 业务ID解析功能
|
||||
let config = BizIdConfig::new(123, 45);
|
||||
let id = generate_biz_id(config);
|
||||
|
||||
let (main_id, sub_id, base_id) = parse_biz_id(id);
|
||||
|
||||
assert_eq!(main_id, 123, "解析的main_id应该等于123");
|
||||
assert_eq!(sub_id, 45, "解析的sub_id应该等于45");
|
||||
assert!(base_id > 0, "解析的base_id应该大于0");
|
||||
|
||||
println!("原始ID: {}, 解析结果: main_id={}, sub_id={}, base_id={}",
|
||||
id, main_id, sub_id, base_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_specific_id_generators() {
|
||||
// 测试7: 特定业务ID生成器
|
||||
let flow_log_id1 = generate_flow_run_log_id();
|
||||
let flow_log_id2 = generate_flow_run_log_id();
|
||||
let request_log_id1 = generate_request_log_id();
|
||||
let request_log_id2 = generate_request_log_id();
|
||||
|
||||
// 验证递增性
|
||||
assert!(flow_log_id2 > flow_log_id1, "流程日志ID应该递增");
|
||||
assert!(request_log_id2 > request_log_id1, "请求日志ID应该递增");
|
||||
|
||||
// 验证业务类型解析
|
||||
let (main_id, sub_id, _) = parse_biz_id(flow_log_id1);
|
||||
assert_eq!(main_id, FLOW_RUN_LOG_MAIN_ID, "流程日志ID的main_id应该正确");
|
||||
assert_eq!(sub_id, FLOW_RUN_LOG_SUB_ID, "流程日志ID的sub_id应该正确");
|
||||
|
||||
let (main_id, sub_id, _) = parse_biz_id(request_log_id1);
|
||||
assert_eq!(main_id, REQUEST_LOG_MAIN_ID, "请求日志ID的main_id应该正确");
|
||||
assert_eq!(sub_id, REQUEST_LOG_SUB_ID, "请求日志ID的sub_id应该正确");
|
||||
|
||||
println!("流程日志ID: {}, {}", flow_log_id1, flow_log_id2);
|
||||
println!("请求日志ID: {}, {}", request_log_id1, request_log_id2);
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
use argon2::{Argon2, PasswordHasher}; use argon2::password_hash::{SaltString, rand_core::OsRng}; fn main() { let argon2 = Argon2::default(); let salt = SaltString::generate(&mut OsRng); let password_hash = argon2.hash_password(b"123456", &salt).unwrap().to_string(); println!("{}", password_hash); }
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "测试流程",
|
||||
"code": "test-flow",
|
||||
"yaml": "# demo flow\nnodes:\n - { id: start, kind: start, name: 开始 }\n - { id: assign, kind: custom, name: 赋值, script: ctx.x = ctx.input.name || 'default'; }\n - { id: end, kind: end, name: 结束 }\nedges:\n - { from: start, to: assign }\n - { from: assign, to: end }\n"
|
||||
}
|
||||
BIN
backend/udmin.db
BIN
backend/udmin.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user