ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Django+Vue3+区块链:心理健康系统存证防篡改架构解析

Django+Vue3+区块链:心理健康系统存证防篡改架构解析 简介这是一份基于Django与Vue3开发的学生心理健康管理系统完整源码聚焦区块链技术在校园心理服务场景中的落地实践适合毕业设计选题、前后端分离项目学习及区块链应用研究者参考。系统采用B/S架构前端Vue.js负责交互界面后端Django提供业务接口MySQL存储数据覆盖管理员端、教师端与学生端支持心理文章浏览、心理测试、咨询预约和论坛交流等功能。资源包为zip格式共266个文件约4.39MB主体为142个Vue组件、67个TypeScript文件及27个SCSS样式另有JSON配置文件、SQL数据库脚本、环境变量与部署配置等目录结构清晰便于按模块拆解学习。目前已有52人学习浏览。通过研读源码可掌握前后端分离开发模式、角色权限设计、心理评测流程与区块链数据存证思路同时可直接复用登录鉴权、预约管理和论坛模块作为二次开发基础。1. 一个带区块链亮点的校园心理健康系统到底能拿来做什么学生心理健康管理系统这类项目在高校里很常见但大多数版本都死在同一个点上测评记录、干预方案这些敏感数据后台管理员动一下就能改出了问题谁也说不清。这套用 Django 和 Vue3 写的源码加区块链不是噱头——它是把评估记录和干预方案的关键信息做哈希上链生成一条谁都改不了的存证后续随时可以校验这条记录在某个时间点确实存在过而且内容没被动过。适合三类人拿来做毕业设计或课程设计的学生想在企业内部搭一个带可信审计功能后台系统的开发者以及想知道区块链功能到底怎么嵌进传统 Web 项目里、而不只是炒概念的技术人。先说破边界区块链在系统里做的是防抵赖不是防删库这个定位贯穿后面所有代码。2. 先看这套方案怎么拼Django 做数据中枢Vue3 做交互层区块链只做存证这一件事2.1 三个组件各管什么哪些数据才值得上链先说分工。这套管理系统本质上是三类角色的工作台学生填心理测评量表、查看自己的评估结果心理咨询师录入评估结论、制定干预计划辅导员和管理员做学生关怀台账和风险等级管理。Django 在这里承担的是完整的数据中枢职责——学生档案、管理员后台、REST API一套搞定Vue3 这边是典型的后台管理系统界面页面路由、表单校验、状态管理都在前端通过 axios 调用 Django 提供的接口。那区块链往哪儿放很多新手拿到标题第一反应是把每个学生的所有心理数据都上链这是最容易翻车的地方。心理测评数据是强隐私数据明文上链等于把学生隐私写到一条可被任意节点读取的链上这个动作本身就不合规。常见做法是只上链存证指纹对一条记录做 SHA-256 哈希把哈希值、记录编号、操作时间和操作人 ID 写到区块链上原始数据继续放在 MySQL 或 PostgreSQL 里。这样既能在事后证明记录真实、未被篡改又不泄露任何明文内容。上链事件建议只覆盖三类测评结果产生、评估报告生成、干预方案变更。这三个事件恰好对应心理健康管理里最容易被质疑、最需要事后审计的动作。至于学生登录、修改密码、浏览页面这类日志完全没必要走链留在 Django 自己的日志表里就行。这个取舍也决定了后面区块链部分的代码量不会失控一个chainapp 就能装下。2.2 后端 App 划分与前端模块映射拿到源码先看这两份清单拿到一个 zip 源码包别急着跑先看目录。常见做法是后端一个backend/前端一个frontend/根目录放 README 和部署文档。这个方案的 Django 工程一般会拆成四个 appaccounts管登录注册和角色assessments管量表和测评结果reports管报告和干预计划chain管哈希生成与上链请求。前端 Vue3 的src/views下面通常是Login.vue、StudentDashboard.vue、Assessments.vue、Reports.vue一一对应后端接口。backend/ ├── manage.py ├── config/ # Django 项目配置 ├── accounts/ # 用户、角色、权限 ├── assessments/ # 测评量表与结果 ├── reports/ # 评估报告与干预方案 ├── chain/ # 哈希生成、上链、校验 └── requirements.txt frontend/ ├── package.json ├── vite.config.js # 开发代理配置 └── src/ ├── api/ # axios 封装 ├── stores/ # pinia 状态 └── views/ # 页面组件chain这个 app 就是标题里区块链技术的落点后面第四章会专门拆它。requirements.txt里通常会有django、djangorestframework、django-cors-headers这几个前端package.json里是vue、vue-router、pinia、axios、element-plus这类后台管理系统的常驻依赖。先对照这份清单确认依赖没少再进下一步。源码里如果还带了docs/或sql/目录说明作者把表结构和接口说明也一并交付了这类源码的可信度通常更高。2.3 表结构设计与数据流向为什么存证记录要单独建表后端的数据模型一般分两组。业务表User继承 AbstractUser 加角色字段、StudentProfile、AssessmentRecord、Report、InterventionPlan存证表ChainRecord单独放一条记录的哈希指纹、上链编号和时间戳。# chain/models.py from django.db import models class ChainRecord(models.Model): biz_type models.CharField(max_length32) # 业务类型: assessment/report/plan biz_id models.CharField(max_length64) # 业务记录主键 digest models.CharField(max_length64) # 记录内容的SHA-256哈希 operator models.CharField(max_length64) # 操作人用户名 created_at models.DateTimeField(auto_now_addTrue) # 本地记录时间 tx_hash models.CharField(max_length128, uniqueTrue) # 区块链交易哈希digest是链上校验的核心tx_hash是区块链返回的交易凭证uniqueTrue保证一条业务记录只对应一次存证重复提交会直接报错。业务表和存证表分开的好处是查列表时不用 JOIN 区块链信息只有需要展示已存证标记时才去ChainRecord查表之间耦合低。后面想把模拟链换成真链时只需要改chainapp 里的提交函数业务表完全不动。数据流向是Vue3 提交测评表单 → Django 写入AssessmentRecord→ 同时调用chain服务生成哈希并上链 → 拿到tx_hash写回ChainRecord→ 前端报告页展示已存证。这个链路里ChainRecord是只追加的审计表任何业务表的修改都不会反向影响它这也是它存在的意义。2.4 为什么是这套组合Django Vue3 的边界感做 django 项目实战新手最容易犯的错是试图让 Django 既出接口又渲染页面让 Vue3 既写界面又直连数据库。这套源码的合理之处在于它把边界划得很清楚Django 只做 ORM 和 APIVue3 只做 UI 和交互区块链只做哈希存证。Django 的 admin 后台在项目初期是免费的福利一次性把角色权限的表结构理清楚省掉大量手工配权限的时间Vue3 的 Composition API 写问卷页和报告页这类表单密集型页面代码组织比 Options API 清爽得多。如果换成 Spring Boot Vue 也能做但在这个体量下显然更重。Python 生态里djangorestframework的序列化器和django-cors-headers的跨域支持几乎是开箱即用而区块链部分用 Python 写哈希和 HTTP 调用也最顺手一个语言打通后端和链上服务不用引入第二套运行时。这套组合真正值钱的地方不是某个框架而是敏感数据留在业务库、只有指纹上链这个架构决策。3. 本地跑通全流程依赖安装、数据库迁移与前后端启动的完整命令3.1 后端环境虚拟环境、requirements 与数据库迁移先把后端跑起来。这个系统是 Django 4.x 的项目Python 用 3.10 或 3.11 都不会有大问题但低于 3.8 基本没戏——Django 4.x 已经不兼容 Python 3.7 了。在backend/下建虚拟环境再装依赖避免把系统的 Python 环境搞乱cd backend python3 -m venv venv source venv/bin/activate pip install -r requirements.txt python manage.py migrate python manage.py createsuperuserrequirements.txt里如果缺了某个包比如djangorestframework或corsheaders直接补pip install就行这不算改代码。migrate会把四个 app 的数据表全部建好createsuperuser是给管理员建账号这个账号一会儿用来登录 Django 后台和 Vue3 管理端。装完依赖建议先跑一遍 Django 自检python manage.py check输出没有错误后再启动开发服务器。开发阶段用runserver就够了它会自动监听代码变化并热重载python manage.py runserver 0.0.0.0:80000.0.0.0让服务监听所有网卡前端在同一台机器的另一个端口访问时不会被本机回环挡住。如果只在本地跑去掉这个参数也不影响。3.2 数据库选择先用 SQLite 验证逻辑再切 MySQL源码默认配置很可能指向 SQLite也就是一个本地文件db.sqlite3。SQLite 对开发来说是零成本的不需要额外启动服务migrate直接生效。但真实交付时一般会切到 MySQL 或 PostgreSQL原因有两个一是心理测评系统在院级部署后并发量虽然不高但多进程写 SQLite 容易触发锁二是运维侧需要数据库备份和恢复能力SQLite 单文件在服务器崩溃时更容易损坏。切换数据库只需要改config/settings.py里的配置DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: mental_health, USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: {charset: utf8mb4}, } }utf8mb4必须写上否则中文心理健康测评内容里的 emoji 或特殊字符会报编码错误。切库之后重新跑一遍migrate之前的db.sqlite3不要直接删先用python manage.py dumpdata backup.json把数据导出等新库建好再loaddata导回。这个操作顺序是 Django 项目换库的标准动作也是新手最容易跳过的步骤。3.3 前端环境Node 版本、npm 安装与跨域代理配到能跑Vue3 项目需要 Node.js 18 以上npm 随 Node 一起装好。先看package.json里有没有type: moduleVite 5 的项目基本都是 ESM 规范。安装依赖并启动cd frontend npm install npm run dev正常情况下 Vite 会打印一个本地地址默认http://localhost:5173。打开页面发现接口全报 404十有八九是跨域或代理没配。关键三步第一步确认 Django 端装了django-cors-headers且在INSTALLED_APPS和MIDDLEWARE里都注册了第二步确认CORS_ALLOWED_ORIGINS配置正确开发阶段可以全放行生产环境必须收紧第三步在vite.config.js里把/api代理到 Django// frontend/vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://127.0.0.1:8000, // Django 开发服务器地址 changeOrigin: true } } } })changeOrigin: true会修改请求头里的HostDjango 校验ALLOWED_HOSTS时才不会因为域名不匹配拒绝。如果跳过代理直接请求http://127.0.0.1:8000/api/...就必须靠 CORS 放行两套方案选一套不要两个都配然后以为都生效了。改完vite.config.js必须重启 dev serverVite 不会自动重载配置文件。3.4 初始化数据量表、学生账号与第一条测评记录能登录不代表能演示——没有量表数据问卷页就是空白的。源码包里一般会带fixtures/目录常见做法是 Django fixture 文件比如assessments/fixtures/initial_data.json导入命令python manage.py loaddata initial_datafixture 里通常是三样东西一份心理健康量表比如 SCL-90 或大学生心理适应量表、几个测试学生账号、几条测评记录。如果源码没带 fixture就用 shell 手搓python manage.py shellfrom accounts.models import User from assessments.models import AssessmentRecord stu, _ User.objects.get_or_create( usernamestu001, defaults{password: pbkdf2_sha256$...} ) stu.set_password(test123456) stu.save() AssessmentRecord.objects.create( studentstu, scale_nameSCL-90, score128, statuscompleted )get_or_create和set_password是这里最值得讲的两个细节前者避免重复执行时主键冲突后者确保密码经过 Django 的哈希算法而不是明文存储。role字段是不是存在、取值是什么取决于源码里的用户模型定义如果模型里没有就改成UserProfile.objects.create(...)以代码为准。这步做完前端列表页就有数据可渲染先跑通链路再补测试数据。4. 区块链存证链路拆解哈希生成、模拟链上链与防篡改校验4.1 摘要生成SHA-256 为什么够用序列化为什么是哈希的命门区块链不是把整条记录塞进去而是对记录的关键字段 时间戳 操作人做一次规范的字符串拼接再做 SHA-256。选 SHA-256 的原因很直接它是不可逆的单向哈希输出固定 64 位十六进制字符串链上存储开销极小而且只要输入有一个字符不同输出就会完全变样这正好可以用来检测记录是否被改过。# chain/utils.py import hashlib import json from django.utils import timezone def build_digest(biz_type, biz_id, content_dict, operator): # 关键所有字段必须按固定顺序序列化否则同一内容会产生不同哈希 payload { biz_type: biz_type, biz_id: str(biz_id), content: content_dict, operator: operator, ts: timezone.now().isoformat() } raw json.dumps(payload, sort_keysTrue, ensure_asciiFalse, separators(,, :)) return hashlib.sha256(raw.encode(utf-8)).hexdigest()sort_keysTrue保证字典字段顺序稳定separators(,, :)去掉多余空格这是最容易踩坑的地方两次哈希结果不一样往往不是数据变了而是序列化时键顺序或空格不一致。ensure_asciiFalse让中文按原样编码如果两个环境一个开一个关哈希也会对不上。timezone.now()在这里不仅记录时间还让每条存证拥有唯一的时间锚点即使两条记录内容完全相同哈希也不同。4.2 模拟链节点开发环境里把上链这件事跑真很多人一听到上链就觉得要去搭一个全节点、部署智能合约。在 Django Vue3 项目里开发阶段完全不需要真链一个模拟链服务就能把流程跑通。常见做法是chainapp 内部封装一个submit_to_chain函数开发环境直接 POST 到一个本地模拟链服务。这个模拟链本质上是个极简 HTTP 服务收到摘要就生成交易哈希并存储# mock_chain.py 独立小进程开发时当链节点用 from http.server import HTTPServer, BaseHTTPRequestHandler import json, hashlib, time store {} class Handler(BaseHTTPRequestHandler): def do_POST(self): if self.path /api/put: length int(self.headers.get(Content-Length, 0)) body json.loads(self.rfile.read(length)) digest body[digest] tx_hash hashlib.sha256((digest str(time.time())).encode()).hexdigest() store[tx_hash] digest resp json.dumps({tx_hash: tx_hash}).encode() self.send_response(200) self.send_header(Content-Type, application/json) self.end_headers() self.wfile.write(resp) def log_message(self, fmt, *args): pass # 关掉默认日志避免刷屏 if __name__ __main__: HTTPServer((127.0.0.1, 8545), Handler).serve_forever()启动它只需要python mock_chain.py。这几十行代码把区块链最核心的抽象表达清楚了交易哈希tx_hash由内容摘要加时间戳二次哈希生成链上只存摘要到交易的映射。真实链的记账、共识、默克尔树在这里都不需要因为开发阶段我们只验证业务链路通不通。等真正要部署时store这个内存字典换成联盟链 SDK 的调用即可。4.3 业务接入保存报告时自动上链返回交易哈希模拟链就绪后把上链动作封装成独立服务函数。业务代码只关心返回的tx_hash不关心底层是模拟链还是真链# chain/services.py import requests from django.conf import settings from .models import ChainRecord from .utils import build_digest def submit_to_chain(biz_type, biz_id, content_dict, operator): digest build_digest(biz_type, biz_id, content_dict, operator) resp requests.post( settings.CHAIN_NODE_URL /api/put, json{digest: digest}, timeout5 ) resp.raise_for_status() tx_hash resp.json()[tx_hash] ChainRecord.objects.create( biz_typebiz_type, biz_idstr(biz_id), digestdigest, operatoroperator, tx_hashtx_hash, ) return tx_hash在reports/views.py里创建报告时调用它from chain.services import submit_to_chain def create_report(request): # ... 业务逻辑保存 Report 对象 ... report Report.objects.create(...) submit_to_chain( biz_typereport, biz_idreport.pk, content_dict{title: report.title, conclusion: report.conclusion}, operatorrequest.user.username, ) return JsonResponse({code: 0, tx_hash: tx_hash})settings.CHAIN_NODE_URL在开发配置里指向http://127.0.0.1:8545生产环境换成真链的接入网关。ChainRecord.objects.create放这里而不是放进build_digest是为了保持工具函数无副作用——哈希只负责算存证业务负责落库职责分离后测试也好写。仓库里如果看到类似的chain.py或services.py结构大概率和我描述的一致。4.4 校验与展示改动一个字段后前端如何弹警告存证不能只写不看校验是这套系统的信任基础。校验逻辑很简单把当前数据库记录重新做一次哈希拿新哈希和链上存的哈希比对一致说明没被改过不一致说明这条记录的内容、操作人或时间戳至少有一项被动了。# chain/services.py def verify_record(chain_record): from reports.models import Report if chain_record.biz_type report: report Report.objects.get(pkchain_record.biz_id) content {title: report.title, conclusion: report.conclusion} operator report.updated_by.username else: raise ValueError(f不支持的 biz_type: {chain_record.biz_type}) new_digest build_digest( chain_record.biz_type, chain_record.biz_id, content, operator ) return new_digest chain_record.digest严格来说生产环境应该去链上查tx_hash对应的原始摘要再和本地摘要比对而不是直接比数据库里的ChainRecord.digest因为数据库本身也可能被改。开发阶段模拟链没有查询接口所以先用本地存证值比对等接真链时补一个query_from_chain(tx_hash)就好。前端调用校验接口后报告页可以这样展示状态// frontend/src/views/ReportDetail.vue const checkChain async () { const { data } await axios.get(/api/chain/verify/report/${reportId}/) if (data.ok) { ElMessage.success(存证校验通过交易哈希${data.tx_hash}) } else { ElMessage.error(检测到记录已被修改存证校验失败) } }这整个链路做完从生成报告到弹出防篡改警告就闭环了。演示时先给评委看校验通过的绿色标签再当着他的面去数据库里把一条评估结论改掉刷新页面点校验红色警告弹出来比任何讲解都有说服力。这套逻辑和区块链溯源系统的核心思路是相通的——溯源查的是从哪里来这里查的是有没有被动过。5. 常见问题与排查六个让新手原地卡住的坑5.1 坑一pip 装完依赖启动时报 ModuleNotFoundError现象python manage.py runserver一执行就报缺少某个模块比如ModuleNotFoundError: No module named rest_framework。原因requirements.txt不完整是源码包的通病作者本地环境里有这个包但清单里漏写了或者当前虚拟环境没激活pip 装到了系统 Python 里。两种情况表现一样但处理方式完全不同。解决先pip list看包在不在当前环境不在就pip install djangorestframework django-cors-headers再看命令行提示符前缀有没有(venv)没有就source venv/bin/activate重新激活。这是最气人的坑因为它不是配置问题是你以为装过但其实没装进当前环境。后面再遇到缺什么包就按这个顺序先查环境再补装。5.2 坑二npm install 报 ERESOLVE 依赖树冲突现象npm install中途失败报ERESOLVE unable to resolve dependency tree。原因项目里某个依赖要求的 vue 版本范围和 package-lock.json 不一致或者 Node 版本太老。element-plus 这类组件库对 vue 版本很敏感这个报错在 Vue3 生态里出现频率不低。解决先试npm install --legacy-peer-deps大多数项目能过还不行就把node_modules和package-lock.json删掉重新装。千万别一上来跑npm audit fix它会顺手升级一堆依赖版本把原本能跑的代码改到不能跑。等 npm 版本冲突的时候记住node_modules 是黑匣子能不动就不动。5.3 坑三前端能打开但登录接口 403 或 404现象页面渲染正常一提交登录表单就 403 Forbidden或者 404 Not Found。原因403 通常是 CSRF 校验没处理或者 Django 的ALLOWED_HOSTS没包含localhost404 通常是前端代理没生效请求打到了 Vite 自己的服务上。解决先看浏览器 Network 面板里请求的完整 URL。如果是http://localhost:5173/api/login/说明代理没接管重点查vite.config.js的 proxy 配置并重启 dev server如果是http://localhost:8000/api/login/报 403就在 Django 的MIDDLEWARE里检查CsrfViewMiddleware是否全局启用登录接口在开发阶段可以用csrf_exempt生产必须换 Token 认证方案。404 还有一种容易被忽略的原因Django 路由里配的是/api/login没加斜杠前端却请求了/api/login/Django 默认重定向在某些 axios 配置下不会跟着走。5.4 坑四存证校验一直失败但明明没改过数据现象verify_record返回 False复查数据库记录发现内容没变。原因八成是哈希拼接顺序或序列化规则不一致。比如生成存证时 content 里有score: 128是 int校验时从数据库读出来变成128字符串或者时间戳一个是带时区后缀的 ISO 格式一个是本地时间格式字符串对不上哈希自然不一样。这种问题不是数据被篡改是同一个事实被表达了两次但表达方式不同。解决在build_digest里把所有参与序列化的字段做类型归一化——数字全转字符串、时间统一用timezone.now()并按同一格式格式化。调试时把payload和raw字符串打印出来生成和校验两边逐字符对比不用猜直接能看到差在哪。哈希这个问题一旦序列化规则定下来就别再动它这会成为你唯一的后悔药。5.5 坑五删除一条业务记录时ChainRecord 也被级联删了现象在 Django 后台删除某条报告对应的存证记录跟着消失后面审计时找不到存根。原因ChainRecord.biz_id如果建了外键且on_deletemodels.CASCADE删除业务主记录就会连带删掉存证记录。这是设计失误——存证的初衷是防抵赖记录本身没了还得靠存证证明它曾经存在过怎么能跟着删。这也是 django 执行查询-删除对象时最容易忽略的关联行为。解决把biz_id改成普通CharField存业务主键字符串不要建外键约束如果已经建了外键迁移时改成on_deletemodels.SET_NULL并允许空值。经验是业务表之间用外键没问题但审计表、存证表这类只追加的表不应该参与任何级联删除。改完这个不管业务记录怎么删存证都在。5.6 坑六npm run build 后部署到 Nginx接口请求打到自己身上现象npm run build后部署到 Nginx页面能打开但所有接口请求都指向localhost:8000一片报错。原因开发时接口地址写死在 axios 的baseURL里Vite 代理只在 dev server 生效打包后就变成浏览器直连。这个问题的本质是把开发环境配置和生产环境配置混在了一起。解决用环境变量管理接口地址。.env.development里VITE_API_BASE请求走 Vite 代理.env.production里VITE_API_BASE/api让请求走 Nginx 反向代理到 Django。Nginx 配置加一条location /api { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }改完这个打包产物就变成纯静态文件加一个反向代理转发规则不再依赖任何开发服务器的魔法。这一步做完这套源码才真正具备交付的资格。6. 进阶把存证校验做成接口顺手解决生产环境的三个隐患很多源码跑通后区块链部分只是个入库动作——存了哈希就再也不管了。这样的系统经不起追问验收时一句你怎么证明这条记录没被改过就能把它问住。我一般会把这个动作变成 Django 的一个标准接口再在 Vue3 报告页上加一个链上校验按钮# chain/views.py from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response api_view([GET]) permission_classes([IsAuthenticated]) def chain_verify(request, biz_type, biz_id): record ChainRecord.objects.filter(biz_typebiz_type, biz_idbiz_id).first() if not record: return Response({ok: False, error: no_chain_record}, status404) passed verify_record(record) return Response({ ok: passed, tx_hash: record.tx_hash, digest: record.digest, })接口返回的tx_hash是可直接展示的凭证。前端拿到ok: true就在报告标题旁显示区块链存证校验通过的标签拿到false就弹红色警告。这一步完成系统才真正把基于区块链技术从标题落到可演示、可验收的功能。接口上线前建议顺手做三件小事。第一把submit_to_chain里的requests.post加上重试机制模拟链偶尔会拒连接真实链节点也有抖动重试一次能避免用户看到 500 页第二在ChainRecord里加一个verified_at字段记录上次校验时间审计时能看出这条存证多久没被验证过第三给chain_verify接口加一个频率限制比如每个用户每分钟 10 次防止有人循环调接口制造不必要的链节点压力。最后说一个我自己踩过的教训早期做类似系统时只写了存证没写校验接口结果演示那天说这是防篡改的台下问改完之后呢你这里能看出来吗我只能现场打开数据库去比对哈希场面非常被动。后来所有带存证功能的系统我都把校验接口作为上线前提每次演示前先改一条测试数据跑一遍校验流程确认红色警告弹得出来再开始讲。这套源码能不能直接当生产系统用另说但把存证、校验、展示串成一条完整链路的习惯是任何类似项目都通用的。希望帮到你。本文还有配套的精品资源点击获取
返回列表