这几天搭建了一个算法相关的web前后端小项目,并实现了主要的功能。这篇文章主要是对项目总结,具体代码后续会推到github上供大家查阅订正。
2025年6月28日
代码已经推到仓库https://github.com/Joyce081/MerkleTree.git了,在分支master里,大家可以部署到本地运行。注意修改application.yml文件中数据库配置、redis配置和邮箱服务配置。
效果展示
一、接口测试
(1)与树有关的接口测试
确保数据库中有相关用于测试的数据,以下为本次测试案例中用到的数据
- 数据库中有ID为2的用户(测试数据中已存在)
- 数据库中有ID为49的树及其叶子节点( [“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “11”, “12”, “13”, “14”, “15”])
1. 创建 Merkle Tree
测试用例 1: 创建有效的 Merkle Tree
- 方法: POST
- URL: /api/merkle/create
- 请求参数:
- userId: 2
- treeName: “测试树”
- algorithm: “sha256”
- dataItems: [“data1”, “data2”, “data3”, “data4”]
- 预期结果:
- 状态码: 200
- 返回创建的树信息,包含正确的rootHash和leafCount
- 数据库中有对应的树和叶子节点记录
测试用例 2: 使用无效算法创建树
- 方法: POST
- URL: /api/merkle/create
- 请求参数:
- userId: 2
- treeName: “测试树”
- algorithm: “invalid_algorithm”
- dataItems: [“data1”, “data2”]
- 预期结果:
- 状态码: 500 或其他错误码
- 返回错误信息"不支持的哈希算法"
测试用例 3: 空数据项创建树
- 方法: POST
- URL: /api/merkle/create
- 请求参数:
- userId: 2
- treeName: “空树”
- algorithm: “sha256”
- dataItems: []
- 预期结果:
- 状态码: 400 或其他错误码
- 返回错误信息"数据项不能为空"
2. 获取 Merkle Tree
测试用例 4: 获取存在的树
- 方法: GET
- URL: /api/merkle/49
- 预期结果:
- 状态码: 200
- 返回ID为49的树信息,包含原始数据列表
- 包含正确的rootHash和leafCount
测试用例 5: 获取不存在的树
- 方法: GET
- URL: /api/merkle/9999
- 预期结果:
- 状态码: 404 或其他错误码
- 返回错误信息"未找到指定的MerkleTree"
3. 获取用户的树列表
测试用例 6: 获取有树的用户列表
- 方法: GET
- URL: /api/merkle/user/2
- 预期结果:
- 状态码: 200
- 返回用户2的所有树列表
- 列表不为空
测试用例 7: 获取无树的用户列表
- 方法: GET
- URL: /api/merkle/user/999
- 预期结果:
- 状态码: 200
- 返回空列表
4. 获取验证路径
测试用例 8: 获取存在的叶子节点验证路径
- 方法: GET
- URL: /api/merkle/proof?treeId=49&leafData=1
- 预期结果:
- 状态码: 200
- 返回验证路径列表
- 路径包含正确的兄弟节点和方向信息
- 最后一项是根节点
测试用例 9: 获取不存在的叶子节点验证路径
- 方法: GET
- URL: /api/merkle/proof?treeId=49&leafData=invalid_data
- 预期结果:
- 状态码: 404 或其他错误码
- 返回错误信息"叶子节点不存在"
5. 验证数据变更
测试用例 10: 验证未变更的数据
- 方法: POST
- URL: /api/merkle/verify-change?treeId=49
- 请求体: {“originalData”: “1”, “modifiedData”: “1”}
- 预期结果:
- 状态码: 200
- 返回结果中isChanged为false
- originalHash和modifiedHash相同
测试用例 11: 验证已变更的数据
- 方法: POST
- URL: /api/merkle/verify-change?treeId=49
- 请求体: {“originalData”: “1”, “modifiedData”: “changed”}
- 预期结果:
- 状态码: 200
- 返回结果中isChanged为true
- originalHash和modifiedHash不同
6. 验证整树完整性
测试用例 12: 验证未篡改的树
- 方法: GET
- URL: /api/merkle/verify-tree?treeId=49
- 预期结果:
- 状态码: 200
- 返回结果中isValid为true
- storedRootHash和computedRootHash相同
测试用例 13: 验证篡改后的树(需要先模拟篡改)
- 方法: GET
- URL: /api/merkle/verify-tree?treeId=49
- 前提: 手动修改数据库中某个叶子节点的hash_value
- 预期结果:
- 状态码: 200
- 返回结果中isValid为false
- storedRootHash和computedRootHash不同
7. 检测数据变更
测试用例 14: 检测无变更的数据
- 方法: GET
- URL: /api/merkle/detect-changes?treeId=49
- 请求体: [“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “11”, “12”, “13”, “14”, “15”]
- 预期结果:
- 状态码: 200
- 返回空变更列表
测试用例 15: 检测有变更的数据
- 方法: GET
- URL: /api/merkle/detect-changes?treeId=49
- 请求体: [“changed”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “11”, “12”, “13”, “14”, “15”]
- 预期结果:
- 状态码: 200
- 返回变更列表,包含position=0的变更信息
8. 删除树
测试用例 16: 删除存在的树
- 方法: DELETE
- URL: /api/merkle/49
- 预期结果:
- 状态码: 200
- 返回成功信息
- 数据库中的树和关联叶子节点被删除
测试用例 17: 删除不存在的树
- 方法: DELETE
- URL: /api/merkle/9999
- 预期结果:
- 状态码: 404 或其他错误码
- 返回错误信息"未找到指定记录"
(2)与用户有关的接口测试
1. 发送验证码接口
测试用例 1: 发送验证码到有效邮箱
- 方法: POST
- URL: /user/sendVerifyCode
- 请求体: {“email”: “test@example.com”}
- 前提: test@example.com 未注册
- 预期结果:
- 状态码: 200
- 返回"验证码发送成功"
- Redis中存储了验证码(键为"verify_code_test@example.com")
- 收到验证码邮件
测试用例 2: 发送验证码到已注册邮箱
- 方法: POST
- URL: /user/sendVerifyCode
- 请求体: {“email”: “test02@example.com”} (假设已注册)
- 预期结果:
- 状态码: 201
- 返回"该邮箱已被注册"
- Redis中没有存储验证码
测试用例 3: 发送验证码到无效邮箱格式
- 方法: POST
- URL: /user/sendVerifyCode
- 请求体: {“email”: “invalid_email”}
- 预期结果:
- 状态码: 400
- 返回"邮箱格式不正确"
测试用例 4: 发送验证码到空邮箱
- 方法: POST
- URL: /user/sendVerifyCode
- 请求体: {“email”: “”}
- 预期结果:
- 状态码: 400
- 返回"邮箱不能为空"
2. 用户注册接口
测试用例 5: 使用有效信息注册
- 方法: POST
- URL: /user/register
- 请求体:
{ "username": "newuser", "password": "123456", "email": "newuser@example.com", "verifyCode": "123456" (假设Redis中存储的验证码为"123456") }
- 前提:
- 已发送验证码到newuser@example.com
- Redis中有"verify_code_newuser@example.com"=“123456”
- 预期结果:
- 状态码: 200
- 返回"注册成功"
- 数据库中有新用户记录
- Redis中的验证码被删除
测试用例 6: 使用错误验证码注册
- 方法: POST
- URL: /user/register
- 请求体:
{ "username": "newuser", "password": "123456", "email": "newuser@example.com", "verifyCode": "wrongcode" }
- 前提: Redis中有"verify_code_newuser@example.com"=“123456”
- 预期结果:
- 状态码: 201
- 返回"验证码错误或已过期"
- 数据库中没有新记录
测试用例 7: 使用已注册邮箱注册
- 方法: POST
- URL: /user/register
- 请求体:
{ "username": "existing", "password": "123456", "email": "test02@example.com", (假设已注册) "verifyCode": "123456" }
- 预期结果:
- 状态码: 201
- 返回"用户已存在"
3. 用户登录接口
测试用例 8: 使用正确凭据登录
- 方法: POST
- URL: /user/login
- 请求体:
{ "email": "test02@example.com", "password": "123456" }
- 前提: 数据库中有email=“test02@example.com”, password="123456"的用户
- 预期结果:
- 状态码: 200
- 返回"登录成功"
- 返回用户信息
测试用例 9: 使用错误邮箱登录
- 方法: POST
- URL: /user/login
- 请求体:
{ "email": "nonexistent@example.com", "password": "123456" }
- 预期结果:
- 状态码: 201
- 返回"登录失败"
测试用例 10: 使用错误密码登录
- 方法: POST
- URL: /user/login
- 请求体:
{ "email": "test02@example.com", "password": "wrongpassword" }
- 预期结果:
- 状态码: 201
- 返回"登录失败"
二、后端功能与实现逻辑
(1)用户模块
1. 核心功能
- 用户注册:邮箱验证码注册流程
- 用户登录:基于邮箱的简单认证
- 邮箱验证:发送验证码及验证码校验
- 邮箱检查:检查邮箱是否已被注册
2. 实现逻辑
注册流程
发送验证码:
- 前端提交邮箱 → 后端验证格式 → 检查是否已注册 → 生成6位随机验证码 → 发送邮件 → 存储到Redis(5分钟有效期)
注册验证:
- 用户提交注册信息(含验证码) → 从Redis获取验证码比对 → 验证通过则创建用户 → 删除Redis中的验证码
3. 技术实现
- 验证码生成:
VerifyCodeUtil
生成随机数字验证码 - 邮件发送:
EmailService
使用Spring Mail发送简单邮件 - 数据存储:
- 用户信息:MySQL数据库(
user
表) - 验证码:Redis缓存(5分钟自动过期)
- 用户信息:MySQL数据库(
- 密码处理:加密存储
4. 安全控制
- 邮箱格式正则验证
- 验证码时效性控制(5分钟)
- 注册前邮箱唯一性检查
(2)Merkle树模块
1. 核心功能
- 树创建:根据输入数据构建Merkle树
- 树查询:按ID或用户ID查询树信息
- 验证路径:获取叶子节点的验证路径
- 数据验证:
- 单数据变更验证
- 整树完整性验证
- 数据变更检测
- 树删除:删除树及关联叶子节点
2. 实现逻辑
树构建流程
- 输入处理:接收数据项列表和哈希算法类型
- 哈希计算:
- 计算每个叶子节点哈希
- 逐层计算父节点哈希(左哈希+右哈希)
- 数据存储:
- 树信息存入
merkle_trees
表(含树结构JSON)- 叶子节点存入
leaves
表
3. 验证机制
- 单数据验证:比较原始数据和修改后数据的哈希值
- 整树验证:重新计算树哈希与存储的根哈希比对
- 变更检测:逐个比对当前数据与存储数据的哈希值
4. 技术实现
- 哈希计算:
MerkleTreeUtil
支持SHA256/SHA1/MD5算法 - 树结构存储:JSON格式存储各层级哈希
- 数据关联:
- 树与用户关联(user_id)
- 树与叶子节点关联(tree_id)
- 事务处理:树删除操作包含叶子节点的级联删除
(3)模块间关系
- 用户-树关联:通过
user_id
建立用户与树的所属关系 - 权限控制:树操作需验证用户权限(当前实现中部分接口已包含user_id参数)
- 数据隔离:用户只能访问自己创建的树
(4)关键技术点
- Merkle树构建算法:自底向上逐层哈希计算
- 验证路径生成:从叶子节点到根节点的兄弟节点收集
- JSON处理:树结构的序列化与反序列化
- 事务管理:关键操作如树删除使用@Transactional
(5)不足
- 用户认证:增加token 认证
- 数据验证时不能有重复数据值
三、前端功能与实现逻辑
(1) 系统架构
前端采用Vue 3 + Element Plus + D3.js技术栈,主要包含以下组件:
- App.vue:根组件,负责全局布局和favicon设置
- LoginRegister.vue:登录注册页面
- Home.vue:主功能页面,包含MerkleTree构建、可视化和验证功能
- Personal.vue:个人中心页面,展示历史记录
(2) 核心功能模块
1. 用户认证系统
实现逻辑:
- 使用Vuex管理全局认证状态
- 路由守卫检查访问权限
- 本地存储token和用户信息实现持久化登录
- 提供登录、注册、退出功能
关键代码:
store/index.js
中的auth状态管理router/index.js
中的路由守卫LoginRegister.vue
中的表单验证和API调用
2. MerkleTree构建与可视化
功能特点:
- 支持多行输入数据构建MerkleTree
- 可选择不同哈希算法(SHA-256/SHA-1/MD5)
- 自定义树名称
- 基于D3.js的可视化展示
实现逻辑:
- 数据输入后调用后端API生成MerkleTree
- 使用D3.js的树布局算法计算节点位置
- 动态渲染树形结构,不同层级使用不同颜色
- 支持缩放和下载图片功能
关键代码:
Home.vue
中的generateMerkleTree
和drawTree
方法- D3.js的树布局和数据转换逻辑
3. 数据验证功能
功能特点:
- 叶子节点修改验证
- 整棵树完整性验证
- 可视化高亮显示验证路径
实现逻辑:
- 选择叶子节点后获取验证路径
- 修改数据后调用验证API
- 高亮显示从叶子到根的路径
- 使用不同颜色和动画效果突出显示
关键代码:
verifyDataChange
和verifyEntireTree
方法- 高亮节点和连接线的样式处理
4. 历史记录管理
功能特点:
- 查看历史构建的MerkleTree
- 删除历史记录
- 从历史记录快速恢复
实现逻辑:
- 调用API获取用户的历史记录列表
- 通过路由参数传递历史数据
- 提供删除功能调用后端API
关键代码:
Personal.vue
中的表格展示和操作按钮loadHistory
和deleteTree
方法
(3)技术亮点
-
响应式设计:
- 使用Vue 3的Composition API组织代码
- 动态计算可视化区域高度适应不同屏幕
-
可视化交互:
- D3.js实现动态树形布局
- 节点悬停效果和点击交互
- 平滑的缩放动画
-
状态管理:
- Vuex集中管理认证状态
- 本地存储实现持久化登录
-
UI/UX优化:
- Element Plus提供美观的UI组件
- 表单验证和错误提示
- 加载状态和操作反馈
(4)数据流
-
用户认证流程:
- 登录/注册 → 获取token → 存储到Vuex和localStorage → 路由跳转
-
MerkleTree构建流程:
- 输入数据 → 调用API生成 → 接收树数据 → 可视化渲染
-
验证流程:
- 选择节点 → 获取验证路径 → 修改数据 → 调用验证API → 显示结果
四、总结
该系统整体设计合理,功能完整,通过可视化方式直观展示了MerkleTree的结构和验证过程,为用户提供了良好的交互体验。