尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于Java与Hyperledger Fabric的区块链考勤系统毕业设计实战

基于Java与Hyperledger Fabric的区块链考勤系统毕业设计实战 简介本资源是一套面向计算机专业本科生的Java毕业设计项目聚焦区块链技术在传统考勤管理场景中的创新应用解决考勤数据易篡改、责任难追溯、审计缺乏可信支撑等实际问题。项目采用Java语言开发集成轻量级区块链模块实现考勤记录上链存证、哈希校验与不可逆操作适用于课程设计、毕设选题及区块链入门实践。压缩包共58个文件含29个核心Java源码涵盖区块链节点、区块结构、共识模拟及Web考勤接口、7个properties配置文件数据库与链参数配置、3个XML配置Spring框架与MyBatis映射、2个SQLite数据库文件初始化考勤与用户数据整体仅56KB结构精简、依赖清晰。已有1244人学习下载提供完整可运行工程含pom.xml、.project、.classpath等IDE识别文件开箱即用附带LICENSE与Git忽略规则便于二次开发与教学演示。1. 项目缘起当传统考勤遇上区块链我们想解决什么又到了一年一度的毕业季相信很多计算机相关专业的同学尤其是软件工程、信息管理方向的都在为毕业设计选题发愁。选题太简单显得没技术含量答辩时容易被老师问住选题太复杂时间精力有限怕做不完。我当年也经历过这个阶段最后选择了一个结合了当时热门技术“区块链”和实际应用场景“考勤管理”的课题。今天我就把这个项目的完整思路、技术选型、实现细节以及我踩过的那些坑毫无保留地分享出来希望能给正在为毕设挠头的你提供一个可以直接“抄作业”的范本。这个项目的核心是想解决传统考勤系统里几个让人头疼的老大难问题。比如考勤数据由公司或学校的管理员中心化存储员工或学生总觉得数据可能被篡改缺乏公信力再比如跨部门或跨校区调取考勤记录时流程繁琐数据难以互通还有一旦中心服务器出问题所有历史记录都可能丢失。区块链技术以其“去中心化、不可篡改、可追溯”的特性天然就是解决这些痛点的良药。所以我们想做的不是一个简单的增删改查系统而是一个将每一次考勤打卡行为都作为一笔“交易”记录在链上所有人都可以验证但无法单方面修改的、真正可信的考勤管理系统。2. 技术栈选型与核心架构设计为什么是Java联盟链确定了方向接下来就是技术选型。看到“基于Java区块链技术”很多同学第一反应可能是去搞比特币、以太坊的公链开发。但这里我要泼一盆冷水对于毕业设计而言直接使用公链如以太坊作为后端是极其不现实的。公链需要消耗真实的加密货币Gas费来执行智能合约和存储数据成本高、速度慢且完全脱离了可控的校园或企业内网环境这会让你的项目变成一个“空中楼阁”。2.1 为什么选择联盟链框架——Hyperledger Fabric经过多方对比我最终选择了Hyperledger Fabric作为区块链底层框架。它是Linux基金会旗下的开源企业级联盟链平台完美契合我们考勤管理的场景。原因如下许可制与隐私性Fabric是联盟链只有被许可的组织如公司的人力资源部、IT部、各个分公司才能加入网络、提交交易或查询账本。这比公链的完全匿名开放更适合企业级应用。我们可以为不同部门设置不同的通道Channel实现数据隔离财务部门看不到研发部门的详细打卡记录但可以验证其统计结果的真实性。无需代币Fabric网络运行不需要任何原生加密货币交易是零成本的这彻底解决了公链Gas费带来的开发和测试成本问题。高性能与可扩展其模块化架构如将交易执行与排序分离带来了更高的吞吐量TPS足以应对一个上千人规模公司的打卡并发。智能合约灵活Fabric的智能合约称为“链码”Chaincode可以用Go、Java、Node.js等多种语言编写。既然我们项目主题是Java那么用Java编写链码就是顺理成章的选择能保持技术栈的统一。2.2 后端与前端技术选型区块链层Hyperledger Fabric 2.x。选择2.x版本是因为其相比1.x在链码生命周期管理、新特性支持上更完善。链码智能合约Java。使用Fabric提供的fabric-chaincode-shim和fabric-contract-api-java库进行开发。这是项目的核心逻辑所在。后端服务中间层Spring Boot。这是Java生态中最主流的快速开发框架。它负责提供RESTful API给前端并调用Fabric SDK与区块链网络进行交互。这里有一个关键点应用服务本身不直接存储核心业务数据考勤记录它只是一个“路由器”和“格式转换器”将前端的请求转化为对链码的调用。区块链交互SDKFabric Gateway Java SDK。这是Fabric官方推荐的用于与网络交互的高级SDK比旧版的Fabric Java SDK更简洁易用。前端Vue.js Element UI。选择Vue是因为其轻量、易上手且生态丰富。Element UI提供了丰富的后台管理组件能快速搭建出美观的管理界面。对于毕业设计一个清晰的管理后台足以展示功能。数据库注意区块链本身Fabric的状态数据库默认是LevelDB或CouchDB存储的是“世界状态”的键值对。但我们可能还需要一个传统数据库如MySQL来存储一些无需上链、查询频繁的辅助信息比如用户的基本资料姓名、工号、部门、前端复杂的查询缓存等。这里务必理解核心的考勤打卡、请假审批流等涉及“资产”转移和权属确认的操作必须通过链码写入区块链而用户信息的修改、复杂报表的预计算可以走传统数据库。这种“链上链下”的混合架构是实际项目中的常见做法。2.3 系统核心架构图逻辑层面整个系统可以划分为三层表示层前端Vue.js构建的Web界面供管理员、员工使用。业务逻辑层后端链码Spring Boot后端接收HTTP请求进行基础验证、业务逻辑组装然后通过Fabric Gateway SDK调用链码。Java链码部署在Fabric网络各节点上。包含核心业务逻辑如打卡(employeeId, timestamp, location)、查询个人考勤(employeeId, month)、申请请假(applicantId, reason, days)、审批请假(managerId, applicationId, status)等方法。所有对考勤“资产”记录、申请单的创建和变更都在这里发生并写入区块链账本。数据持久层区块链账本不可篡改地存储所有交易日志和最新的世界状态如最新的考勤记录列表。可选传统关系数据库存储用户档案、部门信息等。注意在项目报告和答辩中一定要清晰阐述为什么选择联盟链而非公链以及“链上链下数据分离”的设计思想。这是体现你技术思考深度的关键。3. 环境搭建与项目初始化从零开始构建Fabric测试网络这是实操的第一步也是劝退很多人的一步。Fabric的环境搭建相对复杂但按照官方文档一步步来完全可以搞定。3.1 基础环境准备你需要准备操作系统Ubuntu 20.04/22.04 LTS 或 macOS。Windows建议使用WSL2Windows Subsystem for Linux这是最接近原生Linux的体验。Docker Docker ComposeFabric网络的所有节点Peer、Orderer、CA都是以Docker容器形式运行的。这是必须的。GoFabric的很多工具如peer、configtxgen是用Go写的需要Go环境来编译和运行。JavaJDK 11或17。确保配置好JAVA_HOME环境变量。Node.js npm用于运行一些前端工具或示例。3.2 部署一个最简单的Fabric测试网络我强烈建议使用Fabric官方提供的fabric-samples仓库中的test-network。这是入门最快的方式。# 1. 克隆 fabric-samples 仓库并下载二进制文件和Docker镜像 git clone https://github.com/hyperledger/fabric-samples.git cd fabric-samples # 执行引导脚本这会下载fabric二进制文件、docker镜像等耗时较长 ./scripts/bootstrap.sh # 2. 进入测试网络目录 cd test-network # 3. 启动一个最简单的网络包含两个组织Org1、Org2一个排序服务 ./network.sh up createChannel -c mychannel # 4. 查看启动的容器应该能看到 peer、orderer、cli 等容器在运行 docker ps如果这一步成功恭喜你一个最基础的Fabric联盟链网络已经在你的本地跑起来了这个网络包含1个排序节点Orderer负责对交易进行排序、打包出块。2个组织Org1, Org2每个组织有1个Peer节点负责维护账本、执行链码。1个通道mychannelOrg1和Org2在此通道内进行交易。3.3 项目工程结构初始化在另一个目录我们初始化我们的Spring Boot项目和链码项目。# 创建项目根目录 mkdir blockchain-attendance-system cd blockchain-attendance-system # 使用Spring Initializr创建后端项目或直接用IDE创建 # 选择Spring Boot 2.7, Java 11, 依赖Web, Lombok简化代码 # 假设项目名为 attendance-backend # 创建链码项目Java mkdir -p attendance-chaincode/src/main/java/com/attendance/chaincode cd attendance-chaincode # 初始化一个Maven项目pom.xml中需要引入fabric-chaincode-shim和fabric-contract-api-java依赖一个清晰的项目结构如下blockchain-attendance-system/ ├── attendance-backend/ # Spring Boot后端服务 │ ├── src/main/java/com/attendance/ │ │ ├── controller/ # REST API 控制器 │ │ ├── service/ # 业务逻辑层集成Fabric SDK │ │ ├── config/ # 配置类如Fabric连接配置 │ │ └── Application.java │ └── pom.xml ├── attendance-chaincode/ # Java链码项目 │ ├── src/main/java/com/attendance/chaincode/ │ │ └── AttendanceContract.java # 核心链码合约类 │ └── pom.xml ├── fabric-network/ # 存放Fabric网络配置、连接配置文件等 │ ├── connection-org1.yaml # Gateway连接配置文件 │ └── crypto-material/ # 从test-network拷贝的证书和密钥 └── attendance-frontend/ # Vue.js前端项目可后续创建4. 核心链码开发用Java定义不可篡改的考勤规则链码是区块链应用的业务逻辑核心。我们将在AttendanceContract.java中定义所有与考勤相关的交易。4.1 定义数据模型资产首先我们需要确定什么数据要作为“资产”上链。核心资产是AttendanceRecord考勤记录和LeaveApplication请假申请。// 这是一个简化的示例使用 Lombok 注解简化代码 Data NoArgsConstructor AllArgsConstructor public class AttendanceRecord implements Serializable { private String recordId; // 记录ID唯一可用 UUID 或 组合键(employeeId_timestamp_hash)生成 private String employeeId; // 员工ID private Long timestamp; // 打卡时间戳毫秒 private String location; // 打卡地点经纬度或Wi-Fi SSID等 private String recordType; // 记录类型IN上班, OUT下班, REMOTE远程等 private String status; // 状态NORMAL正常, LATE迟到, EARLY_LEAVE早退, ABSENT缺勤- 可由链码逻辑计算 private String txId; // 产生此记录的区块链交易ID用于溯源 } Data NoArgsConstructor AllArgsConstructor public class LeaveApplication implements Serializable { private String applicationId; private String applicantId; private String leaveType; // 事假、病假、年假等 private String reason; private Long startTime; private Long endTime; private Integer totalDays; private String status; // PENDING待审批, APPROVED已批准, REJECTED已拒绝 private String approverId; // 审批人ID private Long approveTime; private String txId; }4.2 实现链码合约类链码合约需要继承Contract类并使用方法注解Transaction来标识可以被外部调用的交易函数。Contract(name AttendanceContract) public class AttendanceContract implements ContractInterface { // 初始化账本可以设置一些初始状态 Transaction public void initLedger(final Context ctx) { // 通常可以空实现或初始化一些管理员账户 ChaincodeStub stub ctx.getStub(); stub.putStringState(INIT_KEY, Ledger initialized for Attendance System); } // 1. 打卡交易 Transaction public AttendanceRecord punchClock(final Context ctx, final String employeeId, final Long timestamp, final String location, final String recordType) { ChaincodeStub stub ctx.getStub(); // 基础验证 if (employeeId null || employeeId.isEmpty()) { throw new ChaincodeException(Employee ID cannot be empty); } // 生成唯一记录ID这里采用 employeeId_timestamp 的哈希防止重复打卡 String compositeKey employeeId _ timestamp; String recordId generateHash(compositeKey); // 检查是否已存在该记录防止重复提交 byte[] existingRecord stub.getState(recordId); if (existingRecord ! null existingRecord.length 0) { throw new ChaincodeException(Attendance record already exists for this employee at this time.); } // 计算考勤状态简化逻辑实际可能更复杂如与排班表对比 String status calculateStatus(recordType, timestamp); // 创建记录对象 AttendanceRecord record new AttendanceRecord(); record.setRecordId(recordId); record.setEmployeeId(employeeId); record.setTimestamp(timestamp); record.setLocation(location); record.setRecordType(recordType); record.setStatus(status); record.setTxId(stub.getTxId()); // 获取当前交易ID // 将对象序列化为JSON字符串并存入世界状态 String recordJson serializeToJson(record); stub.putStringState(recordId, recordJson); // 同时可以维护一个按员工ID查询的复合键索引便于范围查询 stub.putStringState(EMPLOYEE_ employeeId _ timestamp, recordId); return record; // 返回创建好的记录 } // 2. 根据员工ID和月份范围查询考勤记录 Transaction(intent Transaction.TYPE.EVALUATE) // 注明是查询类交易不写账本 public String queryAttendanceByEmployee(final Context ctx, final String employeeId, final Long startTime, final Long endTime) { ChaincodeStub stub ctx.getStub(); ListAttendanceRecord records new ArrayList(); // 使用复合键的范围查询前提是之前用复合键做了索引 // 这里演示一个更通用的方法遍历所有键值对筛选出符合条件的仅适用于测试或数据量小的情况 // 生产环境应使用CouchDB的状态数据库并利用其富查询功能。 QueryResultsIteratorKeyValue results stub.getStateByRange(, ); for (KeyValue kv : results) { String key kv.getKey(); String value kv.getStringValue(); // 简单判断key是否包含员工ID实际应根据存储结构优化 if (key.contains(employeeId)) { AttendanceRecord record deserializeFromJson(value, AttendanceRecord.class); if (record.getTimestamp() startTime record.getTimestamp() endTime) { records.add(record); } } } return serializeToJson(records); } // 3. 请假申请 Transaction public LeaveApplication applyForLeave(final Context ctx, final String applicationId, final String applicantId, final String leaveType, final String reason, final Long startTime, final Long endTime) { // ... 参数校验生成申请对象状态设为PENDING存入世界状态 ... // 关键请假申请一旦上链申请人就无法自行撤回或修改保证了流程的严肃性。 } // 4. 审批请假 Transaction public LeaveApplication approveLeave(final Context ctx, final String applicationId, final String approverId, final Boolean isApproved) { ChaincodeStub stub ctx.getStub(); // 1. 根据applicationId从世界状态读取申请 String appJson stub.getStringState(applicationId); if (appJson null || appJson.isEmpty()) { throw new ChaincodeException(Leave application not found.); } LeaveApplication application deserializeFromJson(appJson, LeaveApplication.class); // 2. 权限校验这里简化了实际应检查approverId是否有权审批该申请人的假条 if (!PENDING.equals(application.getStatus())) { throw new ChaincodeException(Application is not in PENDING status.); } // 3. 更新状态 application.setStatus(isApproved ? APPROVED : REJECTED); application.setApproverId(approverId); application.setApproveTime(System.currentTimeMillis()); application.setTxId(stub.getTxId()); // 4. 写回世界状态 stub.putStringState(applicationId, serializeToJson(application)); // 5. 可选触发后续逻辑如自动生成考勤异常豁免记录 if (isApproved) { // 可以在这里调用另一个链码函数生成一条特殊的考勤记录状态为“请假” } return application; } // 辅助方法序列化、反序列化、状态计算等 private String serializeToJson(Object obj) { ... } private T T deserializeFromJson(String json, ClassT clazz) { ... } private String calculateStatus(String recordType, Long timestamp) { ... } private String generateHash(String input) { ... } }4.3 链码的打包、安装与实例化编写完链码后需要将其部署到Fabric网络上。# 1. 在链码项目目录下使用Maven打包 cd attendance-chaincode mvn clean package -DskipTests # 会生成 target/attendance-chaincode-1.0.jar # 2. 将链码打包为Fabric认可的格式在fabric-samples/test-network目录下操作 # 首先设置一些环境变量指向你的组织管理员 export PATH${PWD}/../bin:$PATH export FABRIC_CFG_PATH$PWD/../config/ export CORE_PEER_TLS_ENABLEDtrue export CORE_PEER_LOCALMSPIDOrg1MSP export CORE_PEER_MSPCONFIGPATH${PWD}/organizations/peerOrganizations/org1.example.com/users/Adminorg1.example.com/msp export CORE_PEER_ADDRESSlocalhost:7051 # 3. 打包链码 peer lifecycle chaincode package attendance.tar.gz --path /绝对路径/到/你的/attendance-chaincode --lang java --label attendance_1.0 # 4. 在Org1的Peer上安装链码 peer lifecycle chaincode install attendance.tar.gz # 记住安装后返回的包ID类似 attendance_1.0:hash... # 5. 批准链码定义需要Org1和Org2都批准 peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name attendance --version 1.0 --package-id 上一步的包ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem # 切换到Org2的环境变量重复第4步安装和第5步批准。 # 6. 提交链码定义使其在通道上生效 peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name attendance --version 1.0 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt这个过程是Fabric链码生命周期管理的标准流程虽然步骤多但每一步都有其意义打包、安装、批准、提交确保了链码升级的严谨性。对于毕设你可以在报告里详细描述这个过程并附上关键命令截图。5. 后端服务开发Spring Boot如何与Fabric区块链交互链码部署好了它就在区块链网络上静静运行。现在我们需要一个“中间人”后端服务来连接前端用户和区块链网络。这里我们使用Fabric Gateway Java SDK。5.1 配置Fabric连接首先从test-network目录下将连接网络所需的证书、密钥等文件在organizations文件夹里拷贝到我们后端项目的资源目录下如src/main/resources/fabric-network。然后创建一个连接配置文件connection-org1.yaml。name: test-network-org1 version: 1.0.0 client: organization: Org1 connection: timeout: peer: endorser: 300 credentialStore: path: /tmp/wallet # SDK会自动管理的钱包路径存储用户身份 cryptoStore: path: /tmp/msp tlsCerts: systemCertPool: true channels: mychannel: peers: peer0.org1.example.com: endorsingPeer: true chaincodeQuery: true ledgerQuery: true eventSource: true organizations: Org1: mspid: Org1MSP peers: - peer0.org1.example.com peers: peer0.org1.example.com: url: grpcs://localhost:7051 tlsCACerts: path: classpath:fabric-network/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt grpcOptions: ssl-target-name-override: peer0.org1.example.com hostnameOverride: peer0.org1.example.com5.2 创建Fabric网关服务类这是一个核心服务类负责初始化网关Gateway、获取网络Network、获取合约Contract对象。Service Slf4j public class FabricGatewayService { private Gateway gateway; private Network network; private Contract contract; PostConstruct public void init() throws IOException { // 1. 加载连接配置文件 Path networkConfigPath Paths.get(ClassLoader.getSystemResource(fabric-network/connection-org1.yaml).toURI()); Gateway.Builder builder Gateway.createBuilder(); // 2. 加载用户身份这里使用Org1的管理员身份 Path walletPath Paths.get(System.getProperty(user.home), fabric-wallet); Wallet wallet Wallets.newFileSystemWallet(walletPath); if (!wallet.get(admin).isPresent()) { // 如果钱包里没有admin身份需要从证书文件导入 Path credentialPath Paths.get(ClassLoader.getSystemResource(fabric-network/crypto-config/peerOrganizations/org1.example.com/users/Adminorg1.example.com/msp).toURI()); X509Certificate certificate readX509Certificate(Paths.get(credentialPath.toString(), signcerts, cert.pem)); PrivateKey privateKey getPrivateKey(Paths.get(credentialPath.toString(), keystore, priv_sk)); Identity identity Identities.newX509Identity(Org1MSP, certificate, privateKey); wallet.put(admin, identity); } // 3. 连接配置 builder.identity(wallet, admin) .networkConfig(networkConfigPath) .discovery(true); // 启用服务发现 // 4. 连接到网关 gateway builder.connect(); // 5. 获取指定通道 network gateway.getNetwork(mychannel); // 6. 获取指定链码合约 contract network.getContract(attendance); log.info(Connected to Fabric network and got contract: attendance); } public Contract getContract() { return contract; } PreDestroy public void close() { if (gateway ! null) { gateway.close(); } } // ... 省略读取证书和私钥的辅助方法 ... }5.3 实现业务服务层有了合约对象我们就可以在Spring Boot的Service层调用链码了。Service Slf4j public class AttendanceService { Autowired private FabricGatewayService fabricGatewayService; public AttendanceRecord punchClock(String employeeId, Long timestamp, String location, String type) throws Exception { Contract contract fabricGatewayService.getContract(); // 提交交易写操作 byte[] result contract.submitTransaction(punchClock, employeeId, String.valueOf(timestamp), location, type); return JsonUtil.parseJson(new String(result), AttendanceRecord.class); } public ListAttendanceRecord queryAttendance(String employeeId, Long start, Long end) throws Exception { Contract contract fabricGatewayService.getContract(); // 评估交易读操作不提交 byte[] result contract.evaluateTransaction(queryAttendanceByEmployee, employeeId, String.valueOf(start), String.valueOf(end)); String jsonStr new String(result); return JsonUtil.parseJsonArray(jsonStr, AttendanceRecord.class); } public LeaveApplication applyForLeave(LeaveApplication app) throws Exception { Contract contract fabricGatewayService.getContract(); String appId UUID.randomUUID().toString(); byte[] result contract.submitTransaction(applyForLeave, appId, app.getApplicantId(), app.getLeaveType(), app.getReason(), String.valueOf(app.getStartTime()), String.valueOf(app.getEndTime())); return JsonUtil.parseJson(new String(result), LeaveApplication.class); } // ... 其他方法 ... }5.4 提供RESTful API最后通过Controller暴露API给前端调用。RestController RequestMapping(/api/attendance) public class AttendanceController { Autowired private AttendanceService attendanceService; PostMapping(/punch) public ApiResponseAttendanceRecord punchClock(RequestBody PunchRequest request) { try { // 实际项目中employeeId应从登录用户的Token中解析timestamp可用服务器时间防止客户端篡改 AttendanceRecord record attendanceService.punchClock(request.getEmployeeId(), System.currentTimeMillis(), request.getLocation(), request.getType()); return ApiResponse.success(record); } catch (Exception e) { log.error(Punch clock failed, e); return ApiResponse.error(500, 打卡失败: e.getMessage()); } } GetMapping(/records) public ApiResponseListAttendanceRecord getRecords(RequestParam String employeeId, RequestParam DateTimeFormat(iso DateTimeFormat.ISO.DATE) LocalDate month) { try { // 计算月初和月末的时间戳 long start month.atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000; long end month.plusMonths(1).atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000 - 1; ListAttendanceRecord records attendanceService.queryAttendance(employeeId, start, end); return ApiResponse.success(records); } catch (Exception e) { return ApiResponse.error(500, 查询失败); } } // ... 其他API ... }至此一个完整的、连接了Java链码和Spring Boot后端的考勤系统核心后端就搭建完成了。前端Vue.js项目通过调用这些REST API就能实现打卡、查询等功能而所有核心数据都经由后端服务写入到了不可篡改的区块链中。6. 前端界面与功能实现构建用户操作门户前端部分相对标准主要是调用后端API。这里我强调几个关键界面和与区块链特性相关的展示。6.1 用户登录与身份管理由于我们使用的是联盟链用户身份对应着Fabric网络中的证书。在毕设演示中可以简化处理在后端预置几个用户如employee1,manager1并为他们生成对应的Fabric用户证书.pem和私钥存储在服务端的“钱包”里。前端登录时传递用户名密码或模拟登录后端根据用户名选择对应的Fabric身份来调用链码。这意味着链码里ctx.getClientIdentity().getId()获取到的就是该用户的唯一区块链身份可用于权限控制。6.2 核心功能页面员工打卡页一个简单的按钮点击后调用/api/attendance/punch并传递地理位置信息浏览器navigator.geolocationAPI获取。成功后会返回链上交易IDtxId可以展示给用户“打卡成功交易哈希0x...”增强可信度。个人考勤查询页选择月份以日历或列表形式展示所有打卡记录。每条记录旁边可以显示一个“验证”按钮点击后可以调用区块链浏览器的API如果部署了的话或后端提供的验证接口来证明这条记录确实存在于区块链上且未被修改。请假申请与审批页经理角色员工提交请假申请调用链码生成一个状态为PENDING的申请资产。经理页面会列出所有待审批的申请。经理审批后调用链码更新申请状态。整个流程在链上留痕谁在什么时间申请谁在什么时间审批一目了然无法抵赖。6.3 区块链信息展示为了突出“区块链”特性可以在前端增加一个“区块链浏览器”模块简化版最新区块显示最近添加到账本上的区块号、哈希、交易数量。交易查询输入交易IDtxId可以查看该交易的详细信息需后端提供相应查询链码和API。数据验证输入一个考勤记录的recordId后端可以查询链上状态并返回前端展示证明数据真实存在。7. 项目演示、测试与答辩要点7.1 如何演示启动全套服务依次启动Fabric网络、Spring Boot后端、Vue前端。功能演示员工A打卡展示成功提示和交易ID。员工A查询本月考勤展示记录。员工A提交一个请假申请。切换账号到经理B登录后看到待审批申请进行批准。员工A再次查询看到请假申请状态已更新并且可能看到系统自动生成的“请假”考勤记录。区块链特性验证在Fabric CLI或使用SDK模拟一个“恶意节点”尝试篡改一条考勤记录的状态直接修改数据库是不可能的因为数据在各节点间共识。演示修改失败或会被其他节点发现。展示通过交易ID在“区块链浏览器”查询交易详情证明操作不可篡改。7.2 测试要点链码单元测试Fabric提供了fabric-chaincode-shim的测试库可以模拟链码执行环境对punchClock、approveLeave等方法进行单元测试验证业务逻辑。API集成测试使用Postman或JUnit测试Spring Boot的Controller确保API能正确调用链码并返回结果。并发测试使用JMeter模拟多名员工同时打卡测试系统的并发处理能力。关注Fabric网络的TPS每秒交易数。7.3 答辩可能遇到的问题与回答思路Q为什么用区块链传统的数据库加数字签名不行吗A传统数据库签名可以保证单条记录的真实性但无法防止中心化管理员篡改或删除整个数据库。区块链通过分布式共识确保了历史记录的不可篡改和不可删除。此外区块链天然提供了多方可信协作的框架适合跨部门/公司的考勤数据互信场景。QFabric网络性能如何能支持多少人同时打卡AFabric在适当配置下如Kafka排序、足够多的Peer节点TPS可以达到上千。对于考勤这种低频场景早高峰每秒几十次请求完全足够。我们也可以通过将打卡交易异步化、批量提交等方式进一步提升前端响应速度。Q所有数据都上链存储成本是不是很高A这是区块链应用的经典问题。我们的设计是“混合架构”。只有关键资产打卡记录、请假单和变更日志上链。用户个人信息、部门结构等静态或频繁变更的数据仍存在传统数据库。链上只存储哈希或关键字段也是一种优化方案。Q如果员工手机时间不准打卡记录的时间戳可信吗A链上时间戳是交易被排序服务打包进区块的时间由排序节点决定而非客户端时间。我们可以在链码中以接收到交易的时间区块时间为准或者要求前端打卡时由后端服务器提供可信时间戳。这需要在设计时考虑。Q你的系统怎么防止代打卡A区块链解决的是数据可信存储问题防代打卡属于身份认证和物理采集层面的问题。我们可以结合其他技术比如① 手机App绑定特定设备生物识别指纹、人脸② 使用专用硬件考勤机如带NFC或指纹的终端考勤机作为区块链网络的一个客户端将采集到的生物特征哈希上链。区块链确保了“这个生物特征哈希在某个时间被记录”这件事不可篡改。这个项目从构思到实现涵盖了区块链概念理解、联盟链平台选型、智能合约开发、传统后端与区块链的集成、以及前后端开发是一个综合性很强的毕业设计。它不仅能让你深入理解区块链技术的落地应用还能全面锻炼你的全栈开发能力。希望这份超详细的“攻略”能帮你理清思路少走弯路顺利完成一个让导师眼前一亮的毕设。记住关键在于把“为什么用区块链”以及“区块链如何与传统系统结合”这两个问题讲清楚你的项目就成功了一大半。本文还有配套的精品资源点击获取
返回列表