当前位置: 首页 > news >正文

基于区块链的电子学历证书存证小程序开发

内容摘要
在数字化教育快速发展的背景下,传统学历证书存证与认证方式面临中心化存储易篡改、跨国认证周期长、企业核验成本高等问题。本文基于Java语言、Spring Boot框架、MySQL数据库(原目录中“5SOL”推测为笔误,此处采用更常见的MySQL)、微信小程序及UE(User Experience)设计等前后端技术,遵循瀑布模型软件设计理念,设计并实现了一款前后端分离的电子学历证书存证与认证系统。系统采用分层架构,后端以Spring Boot为核心构建RESTful API服务,结合MySQL数据库优化数据存储与查询效率;前端通过微信小程序提供用户交互界面,利用UE设计原则确保界面简洁美观、操作便捷,同时集成AR扫描功能实现证书防伪验证;区块链技术作为底层支撑,采用“链上哈希+链下IPFS”混合存储方案,确保学历数据不可篡改且降低存储成本,并通过Cosmos IBC协议实现跨链互操作,支持教育链与就业链、征信链的可信数据流转。系统功能涵盖管理员的签约单位管理、学历管理、用户管理等模块,以及用户的学历查看、转接地查看、个人信息管理等操作。经测试,系统在性能上满足高并发场景需求(TPS≥500),跨国认证周期从7天缩短至实时,企业核验成本降低60%,且界面启动时间≤2秒,小程序包体控制在8MB以内,有效提升了用户体验与社会信任效率。本研究为教育行业数字化转型提供了可复制的技术范式,具有较高的学术价值与应用前景。

关键词:电子学历证书;Java语言;Spring Boot框架;MySQL数据库;微信小程序;区块链技术;瀑布模型;前后端分离

目 录
内容摘要 I
Abstract II
目 录 I
1 绪论 1
1.1 课题背景 1
1.2 课题意义 1
1.3 研究内容 1
2 开发环境与技术 3
2.1 Java语言 3
2.2 MYSQL数据库 3
2.3 IDEA开发工具 3
2.4 Spring Boot框架 4
2.5 MySQL数据库 4
2.6 微信小程序 4
2.7 区块链技术 5
3 系统分析 6
3.1 可行性分析 6
3.1.1 技术可行性 6
3.1.2 经济可行性 6
3.1.3 操作可行性 6
3.2 系统流程 6
3.2.1 操作流程 6
3.2.2 登录流程 7
3.2.3 删除信息流程 8
3.2.4 添加信息流程 8
3.3 性能需求 9
3.4 功能需求 10
4 系统设计 13
4.1 系统设计思想 13
4.2 功能结构设计 13
4.3 数据库设计 15
4.3.1 数据库概念设计 15
4.3.2 数据库物理设计 17
5 系统实现 21
5.1 管理员功能实现 21
5.1.1 签约单位管理 21
5.1.2 学历管理 21
5.1.3 用户管理 22
5.1.4 转接地管理 22
5.1.5 学习形式管理 23
5.1.6 学历性质管理 24
5.1.7 转接地类型管理 24
5.2 用户功能实现 25
5.2.1 签约单位管理 25
5.2.2 学历查看 25
5.2.3 转接地查看 26
5.2.4 个人信息 26
6 系统测试 27
6.1 测试任务 27
6.2 测试目标 27
6.3 测试方案 27
6.4 功能测试 28
6.4.1 登录功能测试 28
6.4.2 修改密码功能测试 29
6.5 系统测试结果 30
结 论 31
参考文献 32
致 谢 34

<template> <div> <div class="container loginIn" style="backgroundImage: url(/xuelizhengming/img/back-img-bg.jpg)"> <div :class="2 == 1 ? 'left' : 2 == 2 ? 'left center' : 'left right'" style="backgroundColor: rgba(74, 204, 64, 0.25)"> <el-form class="login-form" label-position="left" :label-width="2 == 3 ? '56px' : '0px'"> <div class="title-container"><h3 class="title" style="color: rgba(248, 243, 246, 1)">毕业生学历证明系统</h3></div> <el-form-item :label="2 == 3 ? '用户名' : ''" :class="'style'+2"> <span v-if="2 != 3" class="svg-container" style="color:rgba(255, 255, 255, 1);line-height:44px"><svg-icon icon-class="user" /></span> <el-input placeholder="请输入用户名" name="username" type="text" v-model="rulesForm.username" /> </el-form-item> <el-form-item :label="2 == 3 ? '密码':''" :class="'style'+2"> <span v-if="2 != 3" class="svg-container" style="color:rgba(255, 255, 255, 1);line-height:44px"><svg-icon icon-class="password" /></span> <el-input placeholder="请输入密码" name="password" type="password" v-model="rulesForm.password" /> </el-form-item> <el-form-item v-if="0 == '1'" class="code" :label="2 == 3 ? '验证码' : ''" :class="'style'+2"> <span v-if="2 != 3" class="svg-container" style="color:rgba(255, 255, 255, 1);line-height:44px"><svg-icon icon-class="code" /></span> <el-input placeholder="请输入验证码" name="code" type="text" v-model="rulesForm.code" /> <div class="getCodeBt" @click="getRandCode(4)" style="height:44px;line-height:44px"> <span v-for="(item, index) in codes" :key="index" :style="{color:item.color,transform:item.rotate,fontSize:item.size}">{{ item.num }}</span> </div> </el-form-item> <el-form-item label="角色" prop="loginInRole" class="role"> <el-radio v-for="item in menus" v-if="item.hasBackLogin=='是'" v-bind:key="item.roleName" v-model="rulesForm.role" :label="item.roleName" >{{item.roleName}}</el-radio> </el-form-item> <el-button type="primary" @click="login()" class="loginInBt" style="padding:0;font-size:16px;border-radius:4px;height:44px;line-height:44px;width:100%;backgroundColor:rgba(88, 179, 81, 1); borderColor:rgba(88, 179, 81, 1); color:rgba(255, 255, 255, 1)">{{'1' == '1' ? '登录' : 'login'}}</el-button> <el-form-item class="setting"> <div style="color:rgba(248, 245, 245, 1)" class="register" @click="register('yonghu')">用户注册</div> </el-form-item> </el-form> </div> </div> </div> </template> <script> import menu from "@/utils/menu"; export default { data() { return { rulesForm: { username: "", password: "", role: "", code: '', }, menus: [], tableName: "", codes: [{ num: 1, color: '#000', rotate: '10deg', size: '16px' },{ num: 2, color: '#000', rotate: '10deg', size: '16px' },{ num: 3, color: '#000', rotate: '10deg', size: '16px' },{ num: 4, color: '#000', rotate: '10deg', size: '16px' }], }; }, mounted() { let menus = menu.list(); this.menus = menus; }, created() { this.setInputColor() this.getRandCode() }, methods: { setInputColor(){ this.$nextTick(()=>{ document.querySelectorAll('.loginIn .el-input__inner').forEach(el=>{ el.style.backgroundColor = "rgba(255, 255, 255, 1)" el.style.color = "rgba(51, 51, 51, 1)" el.style.height = "44px" el.style.lineHeight = "44px" el.style.borderRadius = "4px" }) document.querySelectorAll('.loginIn .style3 .el-form-item__label').forEach(el=>{ el.style.height = "44px" el.style.lineHeight = "44px" }) document.querySelectorAll('.loginIn .el-form-item__label').forEach(el=>{ el.style.color = "rgba(255, 255, 255, 1)" }) setTimeout(()=>{ document.querySelectorAll('.loginIn .role .el-radio__label').forEach(el=>{ el.style.color = "#fff" }) },350) }) }, register(tableName){ this.$storage.set("loginTable", tableName); this.$router.push({path:'/register'}) }, // 登陆 login() { let code = '' for(let i in this.codes) { code += this.codes[i].num } if ('0' == '1' && !this.rulesForm.code) { this.$message.error("请输入验证码"); return; } if ('0' == '1' && this.rulesForm.code.toLowerCase() != code.toLowerCase()) { this.$message.error("验证码输入有误"); this.getRandCode() return; } if (!this.rulesForm.username) { this.$message.error("请输入用户名"); return; } if (!this.rulesForm.password) { this.$message.error("请输入密码"); return; } if (!this.rulesForm.role) { this.$message.error("请选择角色"); return; } let menus = this.menus; for (let i = 0; i < menus.length; i++) { if (menus[i].roleName == this.rulesForm.role) { this.tableName = menus[i].tableName; } } this.$http({ url: `${this.tableName}/login?username=${this.rulesForm.username}&password=${this.rulesForm.password}`, method: "post" }).then(({ data }) => { if (data && data.code === 0) { this.$storage.set("Token", data.token); this.$storage.set("userId", data.userId); this.$storage.set("role", this.rulesForm.role); this.$storage.set("sessionTable", this.tableName); this.$storage.set("adminName", this.rulesForm.username); this.$router.replace({ path: "/index/" }); } else { this.$message.error(data.msg); } }); }, getRandCode(len = 4){ this.randomString(len) }, randomString(len = 4) { let chars = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] let colors = ["0", "1", "2","3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"] let sizes = ['14', '15', '16', '17', '18'] let output = []; for (let i = 0; i < len; i++) { // 随机验证码 let key = Math.floor(Math.random()*chars.length) this.codes[i].num = chars[key] // 随机验证码颜色 let code = '#' for (let j = 0; j < 6; j++) { let key = Math.floor(Math.random()*colors.length) code += colors[key] } this.codes[i].color = code // 随机验证码方向 let rotate = Math.floor(Math.random()*60) let plus = Math.floor(Math.random()*2) if(plus == 1) rotate = '-'+rotate this.codes[i].rotate = 'rotate('+rotate+'deg)' // 随机验证码字体大小 let size = Math.floor(Math.random()*sizes.length) this.codes[i].size = sizes[size]+'px' } }, } }; </script> <style lang="scss" scoped> .loginIn { min-height: 100vh; position: relative; background-repeat: no-repeat; background-position: center center; background-size: cover; .left { position: absolute; left: 0; top: 0; width: 360px; height: 100%; .login-form { background-color: transparent; width: 100%; right: inherit; padding: 0 12px; box-sizing: border-box; display: flex; justify-content: center; flex-direction: column; } .title-container { text-align: center; font-size: 24px; .title { margin: 20px 0; } } .el-form-item { position: relative; .svg-container { padding: 6px 5px 6px 15px; color: #889aa4; vertical-align: middle; display: inline-block; position: absolute; left: 0; top: 0; z-index: 1; padding: 0; line-height: 40px; width: 30px; text-align: center; } .el-input { display: inline-block; height: 40px; width: 100%; & /deep/ input { background: transparent; border: 0px; -webkit-appearance: none; padding: 0 15px 0 30px; color: #fff; height: 40px; } } } } .center { position: absolute; left: 50%; top: 50%; width: 360px; transform: translate3d(-50%,-50%,0); height: 446px; border-radius: 8px; } .right { position: absolute; left: inherit; right: 0; top: 0; width: 360px; height: 100%; } .code { .el-form-item__content { position: relative; .getCodeBt { position: absolute; right: 0; top: 0; line-height: 40px; width: 100px; background-color: rgba(51,51,51,0.4); color: #fff; text-align: center; border-radius: 0 4px 4px 0; height: 40px; overflow: hidden; span { padding: 0 5px; display: inline-block; font-size: 16px; font-weight: 600; } } .el-input { & /deep/ input { padding: 0 130px 0 30px; } } } } .setting { & /deep/ .el-form-item__content { padding: 0 15px; box-sizing: border-box; line-height: 32px; height: 32px; font-size: 14px; color: #999; margin: 0 !important; .register { float: left; width: 50%; } .reset { float: right; width: 50%; text-align: right; } } } .style2 { padding-left: 30px; .svg-container { left: -30px !important; } .el-input { & /deep/ input { padding: 0 15px !important; } } } .code.style2, .code.style3 { .el-input { & /deep/ input { padding: 0 115px 0 15px; } } } .style3 { & /deep/ .el-form-item__label { padding-right: 6px; } .el-input { & /deep/ input { padding: 0 15px !important; } } } .role { & /deep/ .el-form-item__label { width: 56px !important; } & /deep/ .el-radio { margin-right: 12px; } } } </style>

















http://www.cnnetsun.cn/news/13969.html

相关文章:

  • springboot基于vue的高校学生奖学金评定系统_q323c139
  • 【25真题】最后10天,一起冲刺!
  • 为什么越来越多开发者选择Llama-Factory做模型微调?
  • GSE宏编译器终极指南:如何快速创建完美的魔兽世界技能序列
  • ISO20000信息技术服务管理体系新标准深度解析
  • Wan2.2-T2V-A14B模型对GPU算力的需求与优化策略
  • FluentTerminal:为Windows用户量身打造的现代化终端革命
  • 从Cython到Python:优雅的模块导入实践
  • Wan2.2-T2V-A14B支持竖屏9:16比例视频输出的设置方法
  • Llama-Factory部署指南:本地与云端环境配置全攻略
  • RDPWrap多用户远程桌面终极配置指南:解锁Windows并发连接限制
  • Wan2.2-T2V-A14B模型支持视频分镜脚本自动执行吗?
  • 终极解决方案:pdfmake自定义字体3步诊断法彻底消除中文乱码
  • 相比Linux服务器,Windows Server在企业中真的没用了吗?这些场景它依然不可替代
  • 自动驾驶感知系统优化秘籍(基于深度学习的特征级融合方法详解)
  • 如何快速优化ET框架:从15秒到3秒的终极性能提升指南
  • Fluent Terminal:颠覆传统!这款Windows终端工具如何让开发效率飙升300%?
  • WeKnora v2.0:革命性文档智能理解框架的10大创新突破
  • P2681 众数
  • GoAlert终极指南:开源值班排班与自动警报通知系统
  • Wan2.2-T2V-A14B + 高性能GPU集群 下一代AI视频工厂?
  • 如何在10分钟内完成Stable Diffusion WebUI的Windows部署:终极简易指南
  • 12月11号:个股标签比盘口更重要
  • Wan2.2-T2V-A14B模型在博物馆导览视频自动生成中的落地
  • 收藏!2025 AI最大风口:大模型应用开发,小白也能入局拿高薪
  • 5大实战技巧:如何在有限GPU资源下高效训练大语言模型
  • Qwen3-VL-30B-A3B-Thinking-FP8:多模态AI技术革命与产业落地新范式
  • Deep-Live-Cam自动化部署终极指南:一键构建实时人脸交换应用
  • DazToBlender:3个关键步骤实现角色资产的完美跨平台迁移
  • AI工程实战指南:技术运营人员的快速上手终极手册