在Solidity智能合约中存储用户成绩记录历史的技术问询
实现Solidity用户成绩历史记录查询功能
问题背景
需要编写Solidity智能合约,实现用户成绩的历史记录存储与查询:调用setUserDetails多次为同一用户添加不同成绩后,getUserGrades能返回该用户所有成绩及对应的添加时间。
当前遇到的问题:
- 通过事件获取历史数据需指定区块号,操作不便
- 原有合约中结构体每次会覆盖旧数据,仅能返回最新成绩
输入示例:
setUserDetails('XYZ', 5) setUserDetails('XYZ', 3)
期望输出:
getUserGrades('XYZ') Grade 5 added for XYZ on 01/01/22 12:00 Grade 3 added for XYZ on 01/01/22 12:01
原有合约代码:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract Users { struct User { string username; uint grade; } mapping(string => User) userMap; event userDetailsChanged( string username, uint grade ); function setUserDetails(string memory _username, uint _grade) external { userMap[_username].username = _username; userMap[_username].grade = _grade; emit userDetailsChanged(_username, _grade); } function getUserGrades(string memory _username) external view returns(uint _grade) { return ( userMap[_username].grade ); } }
解决方案
要实现需求,需利用Solidity的结构体数组、映射关联数组和区块时间戳特性,修改后的合约如下:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract Users { // 定义单条成绩记录,包含成绩和添加时间戳 struct GradeRecord { uint grade; uint timestamp; } // 映射:用户名对应其所有成绩记录的数组 mapping(string => GradeRecord[]) private userGradeHistory; event GradeAdded(string username, uint grade, uint timestamp); function setUserDetails(string memory _username, uint _grade) external { // 向用户的成绩数组中追加新记录,不会覆盖旧数据 userGradeHistory[_username].push(GradeRecord({ grade: _grade, timestamp: block.timestamp })); emit GradeAdded(_username, _grade, block.timestamp); } function getUserGrades(string memory _username) external view returns(GradeRecord[] memory) { // 返回该用户的全部成绩历史记录 return userGradeHistory[_username]; } }
关键修改说明
- 结构体重构:将原
User结构体改为GradeRecord,专注存储单条成绩的核心数据(成绩值+时间戳) - 映射类型调整:把
mapping(string => User)替换为mapping(string => GradeRecord[]),让每个用户对应一个成绩记录数组,每次添加成绩时通过push追加新元素,彻底避免覆盖旧数据 - 时间戳记录:使用
block.timestamp获取区块打包时间(单位为秒),前端可将其转换为可读的日期时间格式 - 查询功能升级:
getUserGrades返回整个成绩数组,前端遍历数组即可格式化输出所有历史记录 - 事件优化:事件中加入时间戳,方便链下服务监听同步数据
注意事项
block.timestamp存在一定误差(取决于区块打包速度,通常几秒到几十秒),无法实现毫秒级精度的时间记录- 数组存储会增加链上存储空间占用,每次添加成绩的gas成本会随记录数量缓慢上升,但对于成绩记录这类低频操作完全可接受
内容的提问来源于stack exchange,提问作者Wojtek
相关产品推荐
相关产品推荐

