news 2026/9/16 16:03:39

Vue+SpringBoot构建学生综合素质评价系统实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue+SpringBoot构建学生综合素质评价系统实战

简介:这是一套面向计算机专业本科生的高分毕业设计级学生综合素质评价系统,采用前后端分离架构,基于Vue实现管理界面、SpringBoot构建后端服务,完整覆盖学生自评、教师评价、教务审核及加权评分等核心业务流程,同时集成用户管理、角色权限、文件上传、操作日志等通用模块,适用于毕设开发、课程设计或Java全栈实战训练。资源包共343个文件,含173个Java后端逻辑类、72个Vue组件与页面、41个JS交互脚本、18个PNG界面图标,以及SQL建表语句、YML配置、LESS样式和BTL模板文件等,结构规范、模块解耦清晰,1.34MB压缩包开箱即用。已有532人学习下载,提供完整可运行源码、MySQL数据库脚本及详细需求文档,无需额外配置即可本地启动,特别适合缺乏项目经验但需快速交付毕设成果的学习者。

1. 为什么一个学生综合素质评价系统要同时用 Vue 和 SpringBoot?

你可能刚接手教务处提的需求:要一个能录入德智体美劳五维数据、支持教师打分、学生自评、家长查看、自动计算雷达图和成长档案的 Web 系统。不是简单增删改查——它得实时响应评分变化、动态渲染多维度权重、导出 PDF 报告、对接教务系统已有学号库,还要在校园内网低带宽环境下稳定运行。这时候,单页应用(SPA)前端 + RESTful 后端的组合就不是“选型偏好”,而是工程约束下的必然解:Vue 负责把复杂的评分表单、拖拽式指标配置、实时权重联动、图表渲染封装成可复用组件;SpringBoot 则承担身份校验(对接 LDAP/统一认证)、事务控制(多张表批量更新不能丢分)、Excel 导入解析(处理教师上传的千行 Excel)、以及最关键的——把“德育加分项”“体育达标阈值”“美育活动类型”这些业务规则固化进 Service 层,避免前端 JS 里写满 if-else。标题里强调“源代码+数据库+需求文档”,恰恰说明这不是玩具项目:它需要可交付、可审计、可交接。新手能照着跑通,老手能快速定位“为什么艺术类活动权重没生效”——这背后是 MyBatis 的 resultMap 映射逻辑、Vue 的 computed 依赖追踪、以及需求文档里第 3.2.4 条对“跨学期累计加分”的明确定义。


2. 搭建最小可运行骨架:从初始化到首页渲染

2.1 初始化 SpringBoot 后端服务(2.7.x 版本兼容性最稳)

学生评价系统对实时性要求不高,但对事务一致性和 SQL 可读性要求高。SpringBoot 2.7.x 是当前高校 Java 课程教学主流版本,与 JDK 8/11 兼容性好,且spring-boot-starter-data-jpa对 MySQL 5.7+ 支持成熟。避免使用 3.x+ 的 Jakarta EE 9+ 命名空间,否则会和现有教务系统 jar 包冲突。

# 使用 Spring Initializr CLI(或访问 start.spring.io 手动勾选) # 必选依赖:Spring Web, Spring Data JPA, MySQL Driver, Lombok, Validation curl https://start.spring.io/starter.zip \ -d dependencies=web,jpa,mysql,lombok,validation \ -d javaVersion=11 \ -d bootVersion=2.7.18 \ -d packageName=com.example.edu \ -d name=student-assessment \ -o student-assessment-backend.zip

提示:不要用 SpringBoot 3.x 初始化。其默认启用 Jakarta EE 9+(如jakarta.persistence.*),而多数高校实验室 MySQL 驱动(mysql-connector-java 8.0.28)仍基于javax.*包,直接导致ClassNotFoundException。2.7.18 是最后一个支持javax.*的 LTS 版本。

解压后,在application.yml中配置数据库连接(注意时区和字符集):

spring: datasource: url: jdbc:mysql://localhost:3306/student_assessment?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: none # 绝不设为 update 或 create!生产环境必须由 DBA 执行 SQL 脚本 show-sql: true properties: hibernate: format_sql: true dialect: org.hibernate.dialect.MySQL8Dialect
2.1.1 创建核心实体:Student、Indicator、AssessmentRecord

学生评价的核心不是“人”,而是“评价行为”。因此实体设计以AssessmentRecord为中心,关联StudentIndicator(指标),而非传统的一对多学生表:

@Entity @Table(name = "t_assessment_record") @Data public class AssessmentRecord { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "student_id", nullable = false) private Student student; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "indicator_id", nullable = false) private Indicator indicator; @Column(name = "score", nullable = false, precision = 3, scale = 1) // 保留1位小数,如 8.5 private BigDecimal score; @Column(name = "semester", length = 20, nullable = false) // 如 "2023-2024-1" private String semester; @Column(name = "evaluator_type", length = 10, nullable = false) // "teacher", "self", "parent" private String evaluatorType; @Column(name = "created_at", nullable = false, updatable = false) @CreationTimestamp private LocalDateTime createdAt; }

参数说明precision=3, scale=1确保分数存储为DECIMAL(3,1),覆盖 0.0~99.9 分区间,避免浮点误差;@CreationTimestamp自动填充时间,省去 Controller 层手动 set;FetchType.LAZY防止查询记录时级联加载全部学生信息,提升列表页性能。

2.2 初始化 Vue 前端项目(v3.4.x Composition API 标准实践)

使用 Vite 创建 Vue 项目,比 Vue CLI 更轻量,启动快,且天然支持 TypeScript——这对定义评分规则这类强类型场景至关重要:

npm create vite@latest student-assessment-frontend -- --template vue-ts cd student-assessment-frontend npm install # 安装关键依赖 npm install axios@1.6.7 element-plus@2.7.8 echarts@5.4.3 pinia@2.2.3

注意axios@1.6.7是当前与 Vue 3.4 兼容性最好的版本;element-plus@2.7.8提供稳定的 Table、Form、Dialog 组件;echarts@5.4.3渲染雷达图时对移动端适配更优;pinia@2.2.3替代 Vuex,状态管理更简洁。

2.2.1 配置 Axios 请求拦截器,统一处理认证与错误

学生系统需区分角色(管理员、教师、学生、家长),所有请求必须携带Authorization头。在src/utils/request.ts中:

import axios from 'axios' const request = axios.create({ baseURL: '/api', // 代理到后端,见 vite.config.ts timeout: 10000, }) // 请求拦截:从 localStorage 读 token request.interceptors.request.use( (config) => { const token = localStorage.getItem('auth_token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, (error) => Promise.reject(error) ) // 响应拦截:统一处理 401/403 request.interceptors.response.use( (response) => response.data, // 直接返回 data,省去 .data 嵌套 (error) => { if (error.response?.status === 401) { localStorage.removeItem('auth_token') window.location.href = '/login' // 强制跳登录页 } return Promise.reject(error) } ) export default request
2.2.2 创建首页路由与基础布局

src/router/index.ts中定义路由,重点体现角色权限隔离:

import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue' import LoginView from '../views/LoginView.vue' import ScoreInputView from '../views/ScoreInputView.vue' import ReportView from '../views/ReportView.vue' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', redirect: '/home' }, { path: '/login', component: LoginView, meta: { public: true } }, // 公共路由 { path: '/home', component: HomeView, meta: { requiresAuth: true, roles: ['admin', 'teacher', 'student', 'parent'] } }, { path: '/score-input', component: ScoreInputView, meta: { requiresAuth: true, roles: ['teacher', 'admin'] } // 仅教师和管理员可录入 }, { path: '/report/:studentId', component: ReportView, props: true, meta: { requiresAuth: true, roles: ['admin', 'teacher', 'student', 'parent'] } } ] }) // 全局守卫:检查角色权限 router.beforeEach((to, from, next) => { const token = localStorage.getItem('auth_token') if (!token && !to.meta.public) return next('/login') if (to.meta.requiresAuth) { const userRole = localStorage.getItem('user_role') if (to.meta.roles && !to.meta.roles.includes(userRole || '')) { return next('/home') // 无权限跳首页 } } next() }) export default router

逻辑说明meta.roles字段声明路由所需角色,beforeEach守卫读取localStorage中的user_role进行比对。这种方式比后端鉴权更轻量,适合内部系统;真实项目中,user_role应由登录接口返回并存入本地,避免伪造。


3. 实现核心业务:五维指标录入与雷达图生成

3.1 后端提供指标配置 API:支持动态增删改查

学生综合素质评价的指标(如“课堂参与度”“志愿服务时长”“艺术展演等级”)不是固定死的,教务处可能每学期调整。因此Indicator实体需支持 CRUD,并通过@PreUpdate自动维护更新时间:

@Entity @Table(name = "t_indicator") @Data public class Indicator { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "code", length = 50, unique = true, nullable = false) // 如 "DE_001" private String code; @Column(name = "name", length = 100, nullable = false) // 如 "德育-课堂表现" private String name; @Column(name = "category", length = 20, nullable = false) // "moral", "intellectual", "physical", "aesthetic", "labor" private String category; // 五维分类 @Column(name = "weight", nullable = false, precision = 3, scale = 2) // 0.00~1.00 private BigDecimal weight; @Column(name = "max_score", nullable = false) // 满分,如 100 private Integer maxScore; @Column(name = "is_enabled", nullable = false) private Boolean enabled = true; @Column(name = "updated_at") @UpdateTimestamp private LocalDateTime updatedAt; }
3.1.1 编写 IndicatorController,暴露 REST 接口
@RestController @RequestMapping("/indicators") @RequiredArgsConstructor public class IndicatorController { private final IndicatorService indicatorService; @GetMapping public Result<List<Indicator>> listAll() { return Result.success(indicatorService.findAllEnabled()); } @PostMapping public Result<Indicator> create(@Valid @RequestBody Indicator indicator) { return Result.success(indicatorService.save(indicator)); } @PutMapping("/{id}") public Result<Indicator> update(@PathVariable Long id, @Valid @RequestBody Indicator indicator) { indicator.setId(id); return Result.success(indicatorService.save(indicator)); } @DeleteMapping("/{id}") public Result<Void> delete(@PathVariable Long id) { indicatorService.deleteById(id); return Result.success(); } }

参数说明@Valid触发@NotNull,@Min,@Max等注解校验;Result<T>是自定义响应包装类,统一包含code,message,data字段,避免前端反复判断response.data.datafindAllEnabled()方法只返回enabled=true的指标,保证前端下拉框不显示已停用项。

3.2 前端实现指标选择与评分录入表单

ScoreInputView.vue中,使用el-form动态渲染指标:

<template> <el-form :model="form" label-width="120px"> <el-form-item label="学生学号"> <el-input v-model="form.studentId" /> </el-form-item> <el-form-item v-for="indicator in indicators" :key="indicator.id" :label="indicator.name" > <el-input-number v-model="form.scores[indicator.id]" :min="0" :max="indicator.maxScore" :step="0.5" :precision="1" placeholder="请输入分数" /> <div class="el-form-item__description"> 满分 {{ indicator.maxScore }},权重 {{ indicator.weight }} </div> </el-form-item> <el-form-item> <el-button type="primary" @click="submit">提交评分</el-button> </el-form-item> </el-form> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue' import request from '@/utils/request' interface Indicator { id: number code: string name: string category: string weight: number maxScore: number } const indicators = ref<Indicator[]>([]) const form = ref({ studentId: '', scores: {} as Record<number, number> // key: indicator.id, value: score }) onMounted(async () => { const res = await request.get('/indicators') indicators.value = res.data }) const submit = async () => { const records = Object.entries(form.value.scores) .filter(([, score]) => score !== undefined && score > 0) .map(([indicatorId, score]) => ({ studentId: form.value.studentId, indicatorId: Number(indicatorId), score, semester: '2023-2024-2', evaluatorType: 'teacher' })) await request.post('/assessment-records/batch', records) ElMessage.success('评分提交成功') } </script>

逻辑说明v-for动态生成表单项,form.scores是一个对象,键为指标 ID,值为分数;submit方法将非空分数构造成数组,调用/assessment-records/batch批量插入,避免 N 次 HTTP 请求。filter确保只提交有值的项,map构造后端所需的 DTO 结构。

3.3 生成雷达图:ECharts 封装与数据映射

雷达图是综合素质评价的视觉核心。后端需按五维聚合数据,前端用 ECharts 渲染:

3.3.1 后端提供聚合接口/reports/summary/{studentId}
@GetMapping("/reports/summary/{studentId}") public Result<Map<String, Object>> getSummary(@PathVariable String studentId) { // 按 category 分组,计算加权平均分 Map<String, BigDecimal> categoryScores = assessmentRecordRepository .findCategoryWeightedAverage(studentId); // 获取各维度指标详情(用于雷达图标签) List<Indicator> indicators = indicatorService.findByCategories( Arrays.asList("moral", "intellectual", "physical", "aesthetic", "labor") ); Map<String, Object> result = new HashMap<>(); result.put("categories", Arrays.asList("德育", "智育", "体育", "美育", "劳育")); result.put("scores", categoryScores.values().stream() .map(BigDecimal::doubleValue).collect(Collectors.toList())); result.put("indicators", indicators.stream() .map(i -> Map.of("name", i.getName(), "weight", i.getWeight())) .collect(Collectors.toList())); return Result.success(result); }
3.3.2 前端使用 ECharts 渲染雷达图
<template> <div ref="chartRef" style="width: 600px; height: 400px;"></div> </template> <script setup lang="ts"> import { ref, onMounted, onUnmounted } from 'vue' import * as echarts from 'echarts' import request from '@/utils/request' const chartRef = ref<HTMLDivElement | null>(null) let chart: echarts.ECharts | null = null onMounted(() => { if (!chartRef.value) return chart = echarts.init(chartRef.value) // 初始化配置 const option = { tooltip: {}, radar: { indicator: [ { name: '德育', max: 100 }, { name: '智育', max: 100 }, { name: '体育', max: 100 }, { name: '美育', max: 100 }, { name: '劳育', max: 100 } ], center: ['50%', '45%'], radius: '60%' }, series: [{ type: 'radar', data: [{ value: [60, 75, 80, 65, 70], // 示例数据 name: '学生综合得分' }] }] } chart.setOption(option) }) onUnmounted(() => { if (chart) chart.dispose() }) </script>

关键点radar.indicator数组顺序必须与后端返回的categories顺序严格一致;series.data[0].value是五维分数数组,需按“德育、智育、体育、美育、劳育”顺序排列;centerradius参数确保图表在容器中居中且大小合适,避免移动端挤压。


4. 数据库设计与初始化:从 ER 图到可执行 SQL

4.1 核心表 ER 关系与字段设计依据

学生综合素质评价系统的数据库不是范式优先,而是业务可读性优先。以下是四张核心表的字段设计逻辑:

表名字段类型说明设计依据
t_studentid,student_id,name,grade,class_namePK, VARCHAR, VARCHAR, VARCHAR, VARCHARstudent_id为业务主键(学号),非自增 ID;gradeclass_name用于筛选统计学号是教务系统唯一标识,避免用自增 ID 关联,防止跨系统数据迁移失败
t_indicatorid,code,name,category,weight,max_score,enabledPK, VARCHAR, VARCHAR, ENUM, DECIMAL, INT, TINYINTcategory用 ENUM 而非外键,因只有 5 个固定值,减少 JOIN 开销五维分类极少变动,ENUM 比关联表查询更快,且便于 SQL 聚合(如GROUP BY category
t_assessment_recordid,student_id,indicator_id,score,semester,evaluator_type,created_atPK, VARCHAR, BIGINT, DECIMAL, VARCHAR, VARCHAR, DATETIMEsemester为字符串(如 "2023-2024-2"),非日期类型,因学期非标准日历周期学期标识含学年+学期,无法用 DATE 类型表达,字符串更直观且支持模糊查询(如LIKE '2023%'
t_userid,username,password,role,real_name,departmentPK, VARCHAR, VARCHAR, ENUM, VARCHAR, VARCHARrole为 ENUM('admin','teacher','student','parent'),非外键角色固定且少,ENUM 比关联表节省空间,且WHERE role='teacher'查询更快
4.1.1 创建数据库与表结构的完整 SQL 脚本
-- 创建数据库(UTF8MB4 支持 emoji,如学生留言用) CREATE DATABASE IF NOT EXISTS student_assessment CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_assessment; -- 学生表 CREATE TABLE t_student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id VARCHAR(20) NOT NULL UNIQUE COMMENT '学号,业务主键', name VARCHAR(50) NOT NULL COMMENT '姓名', grade VARCHAR(10) NOT NULL COMMENT '年级,如 高一、大二', class_name VARCHAR(30) NOT NULL COMMENT '班级名称', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生基本信息'; -- 指标表 CREATE TABLE t_indicator ( id BIGINT PRIMARY KEY AUTO_INCREMENT, code VARCHAR(50) NOT NULL UNIQUE COMMENT '指标编码,如 DE_001', name VARCHAR(100) NOT NULL COMMENT '指标名称', category ENUM('moral','intellectual','physical','aesthetic','labor') NOT NULL COMMENT '五维分类', weight DECIMAL(3,2) NOT NULL DEFAULT 0.00 COMMENT '权重,0.00~1.00', max_score INT NOT NULL DEFAULT 100 COMMENT '满分', enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用:1启用,0停用', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评价指标配置'; -- 评分记录表 CREATE TABLE t_assessment_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id VARCHAR(20) NOT NULL COMMENT '学号', indicator_id BIGINT NOT NULL COMMENT '指标ID', score DECIMAL(3,1) NOT NULL COMMENT '得分,0.0~99.9', semester VARCHAR(20) NOT NULL COMMENT '学期标识,如 2023-2024-2', evaluator_type ENUM('admin','teacher','student','parent') NOT NULL COMMENT '评价者类型', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_student_semester (student_id, semester), INDEX idx_indicator (indicator_id), FOREIGN KEY (student_id) REFERENCES t_student(student_id) ON DELETE CASCADE, FOREIGN KEY (indicator_id) REFERENCES t_indicator(id) ON DELETE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生评分记录'; -- 用户表(用于登录) CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录账号', password VARCHAR(100) NOT NULL COMMENT 'BCRYPT 加密密码', role ENUM('admin','teacher','student','parent') NOT NULL COMMENT '角色', real_name VARCHAR(50) NOT NULL COMMENT '真实姓名', department VARCHAR(100) COMMENT '所属部门/班级', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户';

参数说明FOREIGN KEY ... ON DELETE CASCADE保证删除学生时自动清理其评分记录;ON DELETE RESTRICT防止误删指标导致历史记录丢失;INDEX idx_student_semester加速按学生+学期查询(如生成学期报告);ENGINE=InnoDB支持事务和外键;CHARSET=utf8mb4避免微信昵称等特殊字符乱码。

4.2 初始化测试数据:插入 5 个典型指标与 10 名学生

执行以下 SQL 插入基础数据,确保首次启动即可演示:

-- 插入五维指标(权重总和为 1.00) INSERT INTO t_indicator (code, name, category, weight, max_score) VALUES ('DE_001', '课堂参与度', 'moral', 0.20, 100), ('IN_001', '学业成绩排名', 'intellectual', 0.30, 100), ('PH_001', '体质健康测试', 'physical', 0.20, 100), ('AE_001', '艺术特长等级', 'aesthetic', 0.15, 100), ('LA_001', '劳动实践时长', 'labor', 0.15, 100); -- 插入 10 名学生(模拟高一某班) INSERT INTO t_student (student_id, name, grade, class_name) VALUES ('2023001', '张三', '高一', '1班'), ('2023002', '李四', '高一', '1班'), ('2023003', '王五', '高一', '1班'), ('2023004', '赵六', '高一', '1班'), ('2023005', '钱七', '高一', '1班'), ('2023006', '孙八', '高一', '1班'), ('2023007', '周九', '高一', '1班'), ('2023008', '吴十', '高一', '1班'), ('2023009', '郑十一', '高一', '1班'), ('2023010', '王十二', '高一', '1班'); -- 插入管理员用户(密码为 123456,实际需 BCrypt 加密) INSERT INTO t_user (username, password, role, real_name) VALUES ('admin', '$2a$10$uQXqJZzYVpKxRcLmNnOoPqRsTuVwXyZaBcDeFgHiJkLmNoPqRsTuVwXyZ', 'admin', '系统管理员');

安全提示:示例密码已用 BCrypt 加密($2a$10$...是标准前缀),实际部署时需用 Spring Security 的PasswordEncoder生成;t_user.password字段长度设为 100,足够存储 BCrypt 哈希值(通常 60 字符)。


5. 需求文档落地技巧:把 Word 条款转成可验证的代码逻辑

5.1 将“跨学期累计加分”需求转化为数据库查询与 Service 逻辑

需求文档第 3.2.4 条原文:“志愿服务类指标(code LIKE 'VO%')允许跨学期累计,同一学生在所有学期的得分之和作为最终分,用于评优。” 这不是一句空话,而是要落实到三层:

5.1.1 数据库层:添加累计视图(View)简化查询
-- 创建累计得分视图,供报表模块直接 SELECT CREATE VIEW v_student_volunteer_total AS SELECT student_id, SUM(score) AS total_score, COUNT(*) AS record_count FROM t_assessment_record r JOIN t_indicator i ON r.indicator_id = i.id WHERE i.code LIKE 'VO%' GROUP BY student_id;

优势:视图封装复杂 JOIN 和 WHERE,报表 SQL 只需SELECT * FROM v_student_volunteer_total WHERE student_id = ?;DBA 可独立优化视图底层索引,无需修改 Java 代码。

5.1.2 Service 层:编写专用方法,明确事务边界
@Service @RequiredArgsConstructor public class VolunteerScoreService { private final AssessmentRecordRepository recordRepo; private final IndicatorRepository indicatorRepo; @Transactional(readOnly = true) public BigDecimal getTotalVolunteerScore(String studentId) { // 先查出所有志愿服务类指标 ID List<Long> volunteerIndicatorIds = indicatorRepo.findIdsByCodeLike("VO%"); // 再查这些指标下该学生的全部记录 return recordRepo.sumScoresByStudentAndIndicators(studentId, volunteerIndicatorIds); } }
5.1.3 Controller 层:暴露为独立接口,避免污染通用接口
@GetMapping("/volunteer-total/{studentId}") public Result<BigDecimal> getVolunteerTotal(@PathVariable String studentId) { return Result.success(volunteerScoreService.getTotalVolunteerScore(studentId)); }

验证方法:用 Postman 调用GET /volunteer-total/2023001,对比数据库SELECT SUM(score) FROM t_assessment_record r JOIN t_indicator i ON r.indicator_id=i.id WHERE r.student_id='2023001' AND i.code LIKE 'VO%'结果是否一致。不一致则说明 Service 层逻辑有误,而非前端展示问题。

5.2 “家长只能查看自己孩子”需求的双重校验实现

需求文档第 4.1.2 条:“家长登录后,仅能查看其绑定学生的评价报告,不可越权访问。” 这需要前后端协同防御:

  • 前端校验:登录后,后端返回studentIds: ["2023001"],前端路由/report/:studentIdbeforeRouteEnter守卫检查to.params.studentId是否在studentIds数组中;
  • 后端校验ReportController的每个方法都接收@AuthenticationPrincipal User user,并在 Service 层查询时强制WHERE student_id = ? AND parent_id = ?t_parent_student关联表需提前创建)。
// 在 ReportService 中 public ReportData getReport(String studentId, User currentUser) { // 1. 检查该学生是否属于当前家长 boolean isChild = parentStudentRepository.existsByParentIdAndStudentId( currentUser.getId(), studentId ); if (!isChild) { throw new AccessDeniedException("无权查看该学生报告"); } // 2. 查询报告数据(此处省略具体 SQL) return buildReport(studentId); }

关键点:后端校验是最后一道防线,绝不能只靠前端隐藏按钮。即使黑客绕过前端路由,直接调用/report/2023002,后端也会抛出AccessDeniedException并返回 403。Spring Security 的@PreAuthorize注解也可实现,但显式 Service 层校验更易单元测试和调试。

5.3 使用 Swagger 生成可交互的 API 文档,替代静态 Word

springdoc-openapi-ui依赖加入pom.xml,启动后访问http://localhost:8080/swagger-ui.html即可看到自动生成的 API 文档,且支持在线调试:

<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.6.14</version> </dependency>

落地价值:测试人员不用再翻 Word 文档找接口地址,直接在 Swagger 页面点击Try it out输入参数;前端开发可一键生成 Mock 数据;教务处老师能直观看到“哪些字段必填、哪些有枚举值”,减少沟通成本。这才是真正“活”的需求文档。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 16:03:32

Python爬虫做豆瓣影评分析:课程设计全流程从requests抓取到情感分析

简介&#xff1a;基于Python爬虫的豆瓣影评分析爬取项目&#xff0c;是一套可直接用于课程设计或毕业设计的完整工程包&#xff0c;尤其适合计算机、人工智能、通信工程、自动化等专业学生。项目聚焦豆瓣影评的爬取、清洗、建模与可视化&#xff0c;覆盖从数据采集到情感分析的…

作者头像 李华
网站建设 2026/9/16 16:01:55

Sunshine 游戏串流快速上手指南:一个晚上打通第一帧

Sunshine 游戏串流快速上手指南&#xff1a;一个晚上打通第一帧 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 装好串通&#xff0c;最终能干什么 Sunshine 是一套自托管的游戏串…

作者头像 李华
网站建设 2026/9/16 16:01:26

基于深度学习与图像分类的舌苔识别完整工程实践

简介&#xff1a;一份整合了Python源码、PyQt5图形界面、训练模型与毕业论文的深度学习舌苔识别检测系统&#xff0c;适合计算机视觉或医学图像处理方向的毕业设计及项目实践者。压缩包共110个文件&#xff0c;主要包含Python脚本、pyc编译文件、PyQt5界面ui与ttc字体、模型pth…

作者头像 李华
网站建设 2026/9/16 16:00:22

基于NLP的微博情感分析系统:从数据清洗到Flask部署

简介&#xff1a;基于NLP的微博用户情感分析系统是一套完整的毕业设计Python工程&#xff0c;面向计算机、通信、人工智能、自动化等专业学生及从业者&#xff0c;可用于课程设计、大作业或毕业设计参考&#xff0c;也适合用来掌握中文微博文本情感倾向自动判别的完整流程。压缩…

作者头像 李华
网站建设 2026/9/16 16:00:11

MATLAB实现粒子群优化求解电力系统最优潮流(OPF)

简介&#xff1a;本资源是一份面向MATLAB初学者与电力系统优化方向学习者的粒子群优化算法&#xff08;PSO&#xff09;基础实现包&#xff0c;聚焦于最优潮流&#xff08;OPF&#xff09;等典型工程优化问题的求解。压缩包共3个文件&#xff0c;含2个核心MATLAB源码文件&#…

作者头像 李华