news 2026/10/22 4:09:49

微信小程序+SSM架构下的学生资助审批管理系统设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序+SSM架构下的学生资助审批管理系统设计

简介:一套面向毕业设计的学生资助在线管理小程序完整方案,基于微信小程序+SSM+MySQL架构,实现管理员、班主任、家长三类角色的协同流程,覆盖资助申请、评议、名单公示、申请复议等环节,适合计算机专业学生作为课程设计与毕设参考。压缩包共1036个文件,约72.16MB,包含vue/java/js等前后端源码、sql数据库脚本、xml与properties配置、wxss/wxml小程序页面文件,以及doc毕业论文、mp4演示视频和bat启动脚本;vue文件对应管理后台界面,bak文件为源码备份,三步bat脚本覆盖安装、运行、构建流程,目录结构清晰,便于定位与部署。已有137人学习下载,尤其适合需要快速搭建可运行项目、参考论文写作或理解小程序与服务端数据交互的读者。拿到后可直接导入运行并二次开发,从环境准备到线上演示形成完整闭环。

1. 学生资助在线管理小程序做的是一件事:把资助审批链路搬到微信里

每年开学前后,高校资助专员手里都会堆起厚厚一摞困难认定申请表。学生填表跑腿、老师层层核对、材料散落在各院系手里,学期末要汇总统计时又得从头翻一遍。学生资助在线管理小程序要解决的就是这个场景:把「困难认定 — 资助项目申请 — 审批 — 发放记录」这条完整链路搬进微信小程序。前端用微信小程序原生框架,后端基于 SSM 提供 JSON 接口,业务数据统一落到 MySQL。对于正在选毕设题目的同学来说,它是一个能同时展示前端、后端、数据库和论文写作能力的全栈项目,而且演示时拿一台手机就能走完整个业务流程,评审老师能直接看到系统在真实场景里怎么运转。

2. 技术选型与整体架构:为什么这套组合最稳妥

2.1 前端选微信小程序,是因为它离学生最近

学生资助的最终使用者是学生和老师。微信小程序不需要安装 App、不需要记住额外的网址,扫码或在微信里搜索就能进入。对毕业设计演示来说,这个优势非常明显:评审老师用微信扫一下小程序码,就能看到登录页、申请页、审批页,而不是让老师先装一个 APK,再担心安卓版本兼容问题。

我一般建议直接用微信小程序原生语法开发,不引入 uni-app。原生小程序由 WXML、WXSS 和 JS 三部分组成,语法上接近 HTML、CSS 和 JS,前端基础够用就能上手。开发者工具里可以直接编译、预览、真机调试,对毕设周期来说效率最高。很多人会问「要不要用 uni-app 顺便出个 App」,如果不是导师强制要求,不建议在毕设里同时维护两套端。标题写的是微信小程序,那就把微信小程序做扎实,比多套壳更有说服力。

选微信小程序还需要接受三个限制。第一,主包体积不能超过 2MB,图片、附件不能堆进包里;第二,正式上线时 request 接口必须配置 HTTPS 合法域名,本地开发时可以勾选「不校验合法域名」来绕过;第三,小程序的渲染层和逻辑层是分离的,setData 传大数据会有明显卡顿。这些限制在毕设阶段都不是硬伤,但后面联调时最容易在域名和真机预览上翻车,第 5 章会专门展开。

2.2 后端坚持 SSM,是为了把框架原理讲清楚

现在新开项目都习惯用 Spring Boot,但很多高校的软件工程课程和毕业设计大纲仍然把 SSM(Spring + SpringMVC + MyBatis)作为主线。SSM 的价值不是新,而是分层清楚:Spring 负责管理对象和事务,SpringMVC 负责接收前端请求并分发,MyBatis 负责 SQL 与 Java 对象的映射。

用 SSM 做这个项目,答辩时你能明确回答三个高频提问:Controller 是怎么拿到前端参数的,Service 层的事务为什么能回滚,MyBatis 的 Mapper 接口和 XML 是怎么关联的。换成 Spring Boot 之后这些细节会被自动配置盖住,反而不好讲。所以只要导师没有强制用 Spring Boot,标题写 SSM 就按 SSM 做,部署环境也相对固定:Tomcat 8/9 + JDK 1.8 + MySQL 5.7/8.0,这套组合在课程设计里跑了很多年,遇到问题几乎都能查到现成答案。

SSM 工程的依赖主要围绕 spring-webmvc、mybatis、mybatis-spring、mysql-connector-java 和 druid 连接池展开,外加 jackson-databind 处理 JSON。工程目录按 controller、service、mapper、entity、common 分包,common 里放统一返回结果和拦截器。这个结构不是最优的,但对毕设来说足够清晰,评审老师扫一眼包结构就能看出你懂分层。

2.3 角色权限与业务闭环:先想清楚谁能做什么

资助管理不是简单的增删改查,它的核心是审批流。我在搭代码之前会先列一张角色权限表,把每个人的动作定死,后面建表和后端接口才不会乱。常见角色有四类,实际项目中可以再加一个院系资助专员做两级复核。

角色典型使用场景核心操作
学生提交困难认定、查看进度、申请资助项目填写家庭经济情况、上传证明、提交申请
辅导员初审学生提交的认定材料查看完整信息、通过或驳回
院系/校级资助管理员终审、项目配置、发放登记复核材料、维护资助项目、登记发放记录
系统管理员维护基础数据用户管理、角色分配、数据统计

业务闭环按五步走:第一步,学生提交困难认定申请,填写家庭成员、年收入、困难类型,并上传低保证明、建档立卡证明等图片;第二步,辅导员初审,核对信息是否完整、是否明显不合逻辑;第三步,院系或校级资助管理员做终审复核,结合班级公示情况给出结论;第四步,学生看到认定结果为「通过」后,申请当前学年开放的资助项目,比如国家助学金、临时困难补助;第五步,管理员审核资助申请并登记发放金额、发放时间。整个流程的每一次操作都会写进审核记录表,这条记录链是论文里最重要的展示素材。

从请求链路看,微信小程序通过 wx.request 发起 HTTP 请求,SpringMVC 的 DispatcherServlet 把请求路由到对应 Controller,Controller 调 Service,Service 在事务内调用 MyBatis Mapper 操作 MySQL,最后把统一格式的 JSON 返回给小程序端做 setData 渲染。理解这条链路,后面联调时遇到任何问题都能快速定位是前端参数问题、后端逻辑问题还是数据库问题。

3. 数据库设计与后端骨架:把审批流程落成表和接口

3.1 核心数据表与关键字段设计

资助业务的数据模型围绕「人、申请、审批、发放」四件事展开。我建议先画一份简单的 E-R 图再建表,避免边写边加字段导致后期改表。项目里至少要包含以下这些表,每一张都有明确的业务归属。

表名用途关键字段
user登录用户与角色id、username、password、role、real_name、student_no、phone
student_profile学生扩展信息user_id、college、major、class_name、grade、dormitory
family_economic家庭经济情况student_user_id、family_members、annual_income、family_type、description
hardship_application困难认定申请student_user_id、semester、status、submit_time、reviewer_id、reject_reason
funding_project资助项目定义project_name、funding_year、quota、amount、apply_start、apply_end
funding_application学生对项目的申请student_user_id、project_id、status、apply_time、reviewer_id
grant_record发放记录funding_application_id、student_user_id、amount、pay_time、operator_id
notice通知公告title、content、publish_time、publisher_id
attachment证明材料附件biz_type、biz_id、file_path、file_name、upload_time

困难认定申请表是整个项目的中心表,它的状态字段设计直接决定后端代码的复杂度。我把 status 定义为 int 类型,用数字表达全流程状态:0 表示草稿,1 表示待辅导员初审,2 表示初审通过,3 表示待终审复核,4 表示终审通过,5 表示已公示,6 表示驳回。用 int 而不是字符串,筛选效率高,Java 里判断也方便;每个状态对应的说明写进代码注释,避免自己三天后忘了 2 和 3 的区别。

下面是困难认定申请表的建表语句,其中审核人、审核时间和驳回原因都放在主表而不是单独建审核流水表,追求的是毕设项目直观可读。如果要体现更严谨的设计,可以再拆一张 review_record 表记录每一步操作人,但对大多数答辩场景,主表字段已经能把流程讲清楚。

CREATE TABLE hardship_application ( id INT AUTO_INCREMENT PRIMARY KEY, student_user_id INT NOT NULL COMMENT '学生用户ID', semester VARCHAR(20) NOT NULL COMMENT '学年学期,如2025-2026-1', family_type TINYINT NOT NULL COMMENT '困难类型:1低保 2建档立卡 3一般困难', family_desc TEXT COMMENT '家庭情况说明', status TINYINT NOT NULL DEFAULT 1 COMMENT '0草稿 1待初审 2初审通过 3待终审 4终审通过 5已公示 6驳回', reviewer_id INT COMMENT '最近一次审核人ID', review_time DATETIME COMMENT '最近一次审核时间', reject_reason VARCHAR(255) COMMENT '驳回原因', submit_time DATETIME NOT NULL COMMENT '提交时间', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_student_semester (student_user_id, semester) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='困难认定申请表';

建表时有几个习惯值得保留。金额字段统一用 DECIMAL(10,2),不要用 FLOAT,避免精度问题;所有时间字段用 DATETIME,排序和区间查询都方便;字符集统一 utf8mb4,否则小程序端用户输入的生僻字或表情符号存进去会变成问号;每条业务表都加上 create_time 和 update_time,答辩时导师问「数据怎么审计」可以直接用这两个字段回答。另外,在 student_user_id 和 semester 上建联合索引,因为最频繁的查询是「某个学生某学期的申请记录」,没有索引的话数据量一上去就容易慢。

3.2 SSM 工程结构与配置要点

后端工程建议按标准 Maven Web 工程组织,包结构固定下来后,Controller、Service、Mapper 各司其职,代码量大了也不会乱。目录结构大致如下:

src/main/java/com/example/studentfund/ ├── common # Result 统一返回、异常处理、拦截器 ├── controller # 接收前端请求 ├── service # 业务逻辑与事务 ├── mapper # MyBatis Mapper 接口 ├── entity # 数据实体 └── config # 配置类 src/main/resources/ ├── mapper # MyBatis XML 映射文件 ├── spring # Spring 与 SpringMVC 配置 └── jdbc.properties

pom.xml 里的依赖不需要追求最新版本,稳定能跑通最重要。核心依赖包括 spring-webmvc、mybatis、mybatis-spring、mysql-connector-java、druid、jackson-databind、pagehelper 和 javax.servlet-api。打包时用 maven-war-plugin,最终部署到 Tomcat 的 webapps 目录。

SpringMVC 配置里最容易踩坑的是 JSON 中文乱码和静态资源拦截。下面这段 spring-mvc.xml 是项目的骨架,我习惯把注解驱动、消息转换器和拦截器都集中在这里配置。

<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> <value>text/html;charset=UTF-8</value> </list> </property> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="com.fasterxml.jackson.databind.ObjectMapper"/> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <mvc:default-servlet-handler/>

数据源与 MyBatis 的配置放在 applicationContext.xml。这里有两个参数必须同步配置,否则很容易出现时区报错或者查询结果字段为 null。第一是 JDBC URL 里的 characterEncoding 和 serverTimezone,第二是 MyBatis 的 map-underscore-to-camel-case,开启后数据库的下划线字段才能自动映射到 Java 的驼峰属性。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/student_fund?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="mapUnderscoreToCamelCase" value="true"/> <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/> </bean> </property> </bean>

3.3 审批接口与状态流转实现

后端接口设计不需要 RESTful 得特别标准,但必须保证两个约定:第一,所有接口返回统一格式 Result,小程序端只用判断 code 为 200 就算成功;第二,涉及审批的动作必须带审核结果和审核意见,驳回时意见必填。

先看一下统一返回结构。Result 里只放 code、message 和 data 三个字段,data 用泛型,这样列表和详情都能复用。

public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> ok(T data) { Result<T> r = new Result<>(); r.code = 200; r.message = "success"; r.data = data; return r; } public static <T> Result<T> error(String message) { Result<T> r = new Result<>(); r.code = 500; r.message = message; return r; } }

审批接口是项目的核心。前端提交审核意见时带上 hardshipApplicationId、reviewAction(pass 或 reject)和 reviewRemark。Service 层根据 reviewAction 决定状态是流转到下一步还是直接打回。下面的代码示意了终审环节的处理逻辑,注意整个方法必须加事务注解,否则「更新申请状态」和「写入审核人信息」会变成两个独立的数据库操作,一旦中间出错数据就对不上了。

@Transactional public Result<?> reviewHardship(Integer applicationId, Integer reviewerId, String action, String remark) { HardshipApplication app = hardshipMapper.selectById(applicationId); if (app == null) { return Result.error("申请不存在"); } if ("reject".equals(action)) { app.setStatus(6); app.setRejectReason(remark); } else if ("pass".equals(action)) { // 根据当前状态决定下一步:初审通过进终审,终审通过进公示 if (app.getStatus() == 1) { app.setStatus(2); } else if (app.getStatus() == 2 || app.getStatus() == 3) { app.setStatus(4); } } else { return Result.error("非法的审核动作"); } app.setReviewerId(reviewerId); app.setReviewTime(new Date()); hardshipMapper.updateById(app); return Result.ok(null); }

这段代码有一个必须注意的点:状态流转不是简单地把 status 加 1,而是要根据当前状态判断下一状态。初审通过是 1 变 2,终审通过是 3 变 4,驳回统一变 6,这几个数字对应第 3.1 节的状态机定义。很多同学在这里直接用 status + 1,结果初审通过后直接跳到 3,跳过了「待终审」的展示,演示时被导师看出来流程逻辑不对。

Controller 层只做参数接收和权限校验,不写业务逻辑。审批接口放在 ReviewerController 里,用自定义注解或拦截器校验当前登录用户角色必须是辅导员或管理员。权限拦截器是毕设里非常加分的点,但也是容易被忽略的点。如果只在小程序端隐藏按钮,后端任何人都可以直接调接口审批,这属于越权漏洞,答辩时被问到概率很高。

4. 微信小程序端实现:从登录到审批的完整页面闭环

4.1 小程序工程结构与全局配置

小程序端用原生语法,工程目录不需要太复杂。按角色划分页面,比按功能划分更容易让代码和业务对齐。我的习惯是把学生端页面、审核端页面和管理端页面分开三个目录,公共组件放 components,请求封装放 utils。

miniprogram/ ├── app.js ├── app.json ├── app.wxss ├── pages/ │ ├── login/ # 登录页 │ ├── student/ # 学生端:申请、进度、项目列表 │ ├── reviewer/ # 审核端:待办列表、审批详情 │ ├── admin/ # 管理端:项目管理、发放记录 │ └── profile/ # 个人中心 ├── components/ # 公共组件 └── utils/ └── request.js # 请求封装

app.json 里需要注册所有页面,并配置 tabBar。学生端和审核端的 tab 入口不一样,我一般不在 tabBar 里做复杂权限控制,而是登录后根据角色跳转到不同首页,再在个人中心提供「切换身份」的入口,便于演示时快速从学生视角切到老师视角。

{ "pages": [ "pages/login/login", "pages/student/index", "pages/student/apply", "pages/student/progress", "pages/reviewer/index", "pages/reviewer/detail", "pages/admin/index", "pages/profile/index" ], "window": { "navigationBarTitleText": "学生资助在线管理", "navigationBarBackgroundColor": "#1E90FF", "navigationBarTextStyle": "white" }, "tabBar": { "list": [ { "pagePath": "pages/student/index", "text": "首页" }, { "pagePath": "pages/student/progress", "text": "进度" }, { "pagePath": "pages/profile/index", "text": "我的" } ] } }

app.js 里只在 onLaunch 时做一件小事:读取 storage 里的登录状态放到 globalData。不要在这里调用登录接口,因为 onLaunch 执行时页面可能已经请求了需要 token 的接口,处理不好会出现登录竞态。token 的管理统一放在 4.2 节的 request.js 里处理。

4.2 请求封装与登录态:所有接口都走同一个入口

小程序原生请求 wx.request 如果每个页面各写各的,后台上线时改域名、统一处理 401 会非常痛苦。我在项目里会先封装一个 request.js,所有页面的请求都走这个入口。封装的核心有三点:自动携带 token、统一处理业务错误码、网络异常提示。

const BASE_URL = 'http://localhost:8080/studentfund'; function request(path, method, data) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + path, method: method || 'GET', data: data || {}, header: { 'Content-Type': 'application/json', 'token': wx.getStorageSync('token') || '' }, success(res) { if (res.statusCode === 401) { wx.removeStorageSync('token'); wx.redirectTo({ url: '/pages/login/login' }); return; } if (res.data.code === 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }); reject(res.data); } }, fail(err) { wx.showToast({ title: '网络异常,请检查后端服务', icon: 'none' }); reject(err); } }); }); } module.exports = { request };

这段封装里有几个参数值得说明。BASE_URL 在本地开发时用局域网 IP 加端口,例如 http://192.168.1.100:8080/studentfund,真机调试时必须把 localhost 换成电脑的局域网 IP;上线时换成 HTTPS 域名。token 从 storage 读取后放进请求头,后端拦截器通过这个 header 识别当前登录用户。业务层约定 code 为 200 才算成功,其他 code 统一弹 toast,这样页面代码里不需要到处写错误提示。

登录页的做法取决于是否使用微信登录。这里有一个常见的分叉点:如果后端接了微信 code2session 接口,小程序端先调用 wx.login 拿 code,再传给后端换取 openid,后端自己维护 session 并返回 token;如果只是毕设演示,直接用账号密码登录更省事,也更容易提前准备好测试账号给评委看。我一般建议用账号密码登录,因为 code2session 依赖 appid 和 secret,答辩环境如果没网或者没配好,现场会非常尴尬。

4.3 学生端:困难认定表单与图片上传

学生端最难的不是表单本身,而是表单数据和图片上传的配合。简单做法是:先把文字字段用 request 方法提交生成申请记录,返回申请 ID 后再用 wx.uploadFile 上传图片,附件表通过 biz_type 和 biz_id 关联到这条申请。顺序不能反过来,否则图片上传成功但申请记录没生成,附件就变成孤儿数据。

async function submitApplication(formData) { const app = await request('/hardship/submit', 'POST', formData); const applicationId = app.id; const uploadTasks = formData.images.map((filePath) => { return new Promise((resolve, reject) => { wx.uploadFile({ url: BASE_URL + '/file/upload', filePath: filePath, name: 'file', formData: { bizType: 'hardship', bizId: applicationId }, success: (res) => resolve(JSON.parse(res.data)), fail: (err) => reject(err) }); }); }); await Promise.all(uploadTasks); wx.showToast({ title: '提交成功', icon: 'success' }); }

这段代码里的 Promise.all 是防止用户等太久而故意做的并行上传,但如果要保证附件顺序,就改成 for 循环逐个上传。注意 wx.uploadFile 的返回数据是字符串,必须先 JSON.parse 再判断;后端接收文件后会返回一个 JSON,前端再根据这个 JSON 决定是否提示上传成功。提交按钮在请求期间要加 disabled 状态,避免用户连续点两次生成重复申请。

4.4 审核端:待办列表与审批操作

审核端首页是一个待办列表,核心是分页加载。小程序的分页不能用传统的 page 参数硬翻页,我一般用 lastId 游标方式:每页 10 条,请求参数带上上一次返回列表的最后一条 ID,后端用 where id < lastId 查询,避免 deep page 性能问题。虽然毕设数据量不大,但这样做接口设计更规范,答辩时也能解释清楚。

loadPendingList() { const lastId = this.data.list.length ? this.data.list[this.data.list.length - 1].id : 0; request(`/review/pending?lastId=${lastId}&size=10`, 'GET') .then((res) => { this.setData({ list: this.data.list.concat(res.list), hasMore: res.list.length === 10 }); }); }, onReachBottom() { if (this.data.hasMore) { this.loadPendingList(); } }

审批详情页需要展示学生提交的完整材料,包括家庭经济情况、证明材料图片列表,以及当前的审批状态。页面底部根据 status 渲染不同按钮:待初审状态显示「通过」和「驳回」,驳回时弹出一个输入框要求填写原因,通过时直接调审批接口。这里要注意一个细节:审批接口调用成功后,不要只修改本地 list 数据,而是重新拉取当前列表并把当前项从待办列表移除,保证后续操作看到的数据和数据库一致。

5. 避坑指南:从开发到部署的五个常见翻车点

5.1 现象:本地接口在小程序里连不通

很多同学第一次联调都会遇到这个问题:后端在电脑上跑得好好的,浏览器直接访问接口也能返回 JSON,但小程序开发者工具里请求一直报 fail,真机预览时更是一片空白。

原因分三种情况。第一种,开发者工具默认禁止访问非 HTTPS 域名,本地调试需要在「详情 — 本地设置」里勾选「不校验合法域名」。第二种,代码里写了 localhost,开发者工具能解析,但真机上的 localhost 指向手机自己,根本找不到电脑。第三种,后端 Tomcat 绑定了 127.0.0.1,局域网其他设备无法访问。

解决方法是把 BASE_URL 改成电脑的局域网 IP,例如 http://192.168.1.100:8080/studentfund,并确保手机和电脑在同一个 Wi-Fi 下。Tomcat 默认监听 0.0.0.0,一般不用改;如果改了配置,需要确认 address 不是 127.0.0.1。演示前一天建议用真机完整跑一遍流程,别在答辩现场才第一次连真机。

5.2 现象:后端返回的中文变成问号或乱码

小程序端显示的中文乱码,大概率不是小程序的问题,而是后端响应头里没有声明 UTF-8。SpringMVC 默认使用 ISO-8859-1 处理字符串,JSON 里只要有中文就会乱。

解决要三层同时改。第一层,web.xml 里配置 CharacterEncodingFilter,强制请求和响应都走 UTF-8;第二层,spring-mvc.xml 里配置 StringHttpMessageConverter 的 supportedMediaTypes,把 text/plain 和 text/html 都声明为 UTF-8;第三层,数据库连接 URL 加 characterEncoding=utf8,建表时用 utf8mb4。只改其中一层,问题往往会以其他形式复现,比如数据库存进去是正常的,查出来却乱。

5.3 现象:MyBatis 动态 SQL 判空失效,列表查不出数据

后端列表接口通常需要支持多条件筛选,比如按学期、按状态查困难认定申请。用 MyBatis 动态 SQL 时最容易写错的是 Integer 类型的判空。有人会把状态字段写成 status != '',对 Integer 类型来说这个条件永远成立,等于没筛选。

正确的写法是用<if test="status != null">,MyBatis 会直接判断 Java 参数是否为 null。同理,String 类型才需要同时判断 null 和空字符串。另外<where>标签能自动去掉第一个多余的 and,比手工加 where 1=1 更安全,也更容易读。写完动态 SQL 后打开 MyBatis 的 StdOutImpl 日志,每次请求看控制台输出的 SQL,一眼就能看出条件拼接对不对。

5.4 现象:审核通过后,学生端还是显示「待审核」

这个问题的根源在小程序页面数据没有刷新,而不是后端状态没更新。很多页面在 onLoad 里拉取一次数据后就不再请求,学生从审核端切回学生端时看到的还是旧数据。

解决方法是把数据拉取逻辑放到 onShow 里,每次页面显示都重新请求详情接口。审核端审批成功后,也要主动通知列表页更新。这里有个血泪经验:审批通过后不要直接 setData 本地数据,而是重新调一次列表接口,让服务器状态作为唯一数据源。如果要减少请求次数,可以保留列表数据,但详情页每次 onShow 必须请求最新状态,否则就会给学生「系统没反应」的感觉。

5.5 现象:MySQL 连接报时区错误,或者 Access denied for user

MySQL 8.0 对时区要求更严格,JDBC URL 不带 serverTimezone 会直接报错:The server time zone value '�й���ʱ��' is unrecognized。这个报错其实和乱码一样,都是编码和时区配置没对齐。

解决方法是把连接 URL 改成 jdbc:mysql://localhost:3306/student_fund?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai,同时确认 MySQL 用户允许从当前主机连接。Access denied 时先检查密码是不是真的对,再用 Navicat 或命令行手动连一次,排除数据库本身没问题再排查后端配置。不要一上来就怀疑 MySQL 版本问题,大多数连接失败都是简单原因。

6. 让答辩更有说服力:论文结构、演示视频与三个加分扩展

6.1 论文里把状态流转图画清楚

导师看论文,最关心的不是代码量,而是你有没有把业务逻辑想明白。困难认定申请表的状态机是整篇论文的灵魂,建议单独画一张状态流转图,把「草稿—待初审—初审通过—待终审—终审通过—已公示—驳回」的每个转移条件标注清楚。数据库部分放 E-R 图加核心表字段说明,不要贴大段建表语句,重点写字段设计的理由。

6.2 演示视频按业务闭环录,不要按页面录

视频演示建议控制在 8 分钟以内,脚本按这条线走:账号密码登录学生端 → 提交一份新的困难认定并上传图片 → 切换到辅导员账号做初审通过 → 切换到管理员账号终审通过 → 学生端查看进度变更为「已公示」 → 管理员发布资助项目 → 学生申请该项目 → 管理员登记发放记录。每一步操作后停顿两秒,让评委看清状态变化,语音只讲核心操作,不要照着论文念。

6.3 三个值得做的扩展

如果核心流程已经稳定,可以选一个方向加分。一是审核结果订阅消息,学生提交申请后能收到微信服务通知;二是用 POI 导出困难认定汇总表,管理端一键导出 Excel;三是用 ECharts 在管理端做一个资助金发放趋势图。三个方向里我建议优先做导出,因为它实现简单、演示直观,不容易在答辩现场出 bug。当年我做这个方向时最后悔的,就是临近答辩还在加可视化图表,结果核心流程没来得及多走几遍,现场一把状态没刷出来就被问了很久。先把闭环跑到肌肉记忆,再谈锦上添花,希望帮到你。

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

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

Kylin V10服务器Xorg进程内存泄漏排查与治理实践

凌晨1点40分&#xff0c;监控系统把电话打到了我这里&#xff0c;一台Kylin V10服务器内存占用超过92%&#xff0c;业务系统已经明显卡顿。我登录进去第一件事就是看进程列表&#xff0c;结果发现罪魁祸首不是业务进程&#xff0c;而是Xorg进程&#xff0c;RSS已经涨到7.3GB&am…

作者头像 李华
网站建设 2026/10/22 4:09:01

昇腾AI服务器整机设计:从芯片到系统的工程逻辑与实操要点

1. 从一颗芯片到一台整机&#xff1a;昇腾AI服务器新品背后的工程逻辑第一次看到“基于昇腾AI&#xff0c;百信人工智能服务器再推新品”这个标题&#xff0c;我脑子里蹦出来的不是发布会PPT上的跑分数字&#xff0c;而是一个很实际的问题&#xff1a;从昇腾AI芯片到一台能稳定…

作者头像 李华
网站建设 2026/10/22 4:08:20

多模态大模型MLLM架构设计与训练实战指南

1. 从单模态到多模态&#xff1a;为什么我们需要MLLM这两年做模型的人应该都有一个共同感受&#xff1a;单模态模型的天花板越来越明显了。纯文本模型再卷&#xff0c;也就是在语言理解、代码生成、逻辑推理这些维度上做文章&#xff0c;它感知不到图像里的空间关系&#xff0c…

作者头像 李华
网站建设 2026/10/22 4:07:47

智驾与AI Agent融合深度解析:三层架构、落地场景与实操踩坑

1. 一场深访带来的认知刷新前阵子跟几位分别在主机厂、智驾方案商和AI应用团队做事的朋友做了一轮长聊&#xff0c;主题就一个&#xff1a;汽车智驾和AI Agent到底怎么融合&#xff0c;融合到什么程度了&#xff0c;哪些是真落地&#xff0c;哪些还在PPT里。聊完最大的感受是&a…

作者头像 李华
网站建设 2026/10/22 4:07:10

自动驾驶基座模型深度解析:从专用模块到通用AI能力体系

前两天在行业群里看到一场技术峰会发布会的核心内容&#xff0c;一家做量产辅助驾驶做了好几年的公司&#xff0c;把“基座模型”摆到了台面上。新闻稿不长&#xff0c;却把整套思路从自动驾驶一直延伸到AI能力体系。说实话&#xff0c;这类发布见过不少&#xff0c;但这回的差…

作者头像 李华
网站建设 2026/10/22 4:06:22

基于Pytorch从零实现Seq2Seq神经机器翻译:Attention机制与工程实践

初学NLP的人&#xff0c;多少都会碰到一个绕不开的入门项目&#xff1a;用Pytorch手写一个Seq2Seq模型&#xff0c;把中文翻译成英文&#xff0c;并且加上Attention机制。这个项目几乎每家高校的NLP课程都会布置一遍&#xff0c;覆盖面也确实广——从词表构建、词嵌入&#xff…

作者头像 李华