SpringBoot+Vue+MySQL科研项目验收管理系统:从选题到答辩完整实战

发布时间:2026/10/19 4:25:05
SpringBoot+Vue+MySQL科研项目验收管理系统:从选题到答辩完整实战 毕业设计选题的时候我见过太多人掉进两个极端一个是题目太“大而新”比如搞一套分布式微服务的什么平台三个月搭不起来答辩时候四处漏风另一个是题目太“小而旧”就是普通增删改查演示十秒钟就完了老师问两句就没货了。最后我选了这个“科研项目验收管理系统”技术栈限定在 SpringBoot Vue MySQL业务上又带一条真实的验收流程既不会失控又能把话说透。科研项目验收这个东西平时听着离学生很远实际上一所高校或科研机构每年在研项目几百个结题时全都要走“提交材料 → 形式审核 → 专家评审 → 结论登记”这条路。没系统之前科研处靠邮箱收材料、用共享表格分配专家、拿计算器汇总分数一件不大的事被邮件反复拉扯放大效率极低。这个项目要做的就是把这套线下流程搬到线上让项目负责人、科研处管理员、评审专家各就各位把“项目、材料、专家、评分、结论”连成一条清晰的数据链。这篇文章我会把这个项目从题目拆解、数据库设计、核心技术实现到部署上线、论文写作、答辩演示的完整过程讲一遍。整体方案谈不上多高深但贵在业务完整、逻辑清晰毕设源码、数据库脚本、论文和部署文档这四样东西都能稳稳当当交付出来。1. 题目拆解科研项目验收到底在管什么1.1 这个题目为什么适合作为毕业设计一个毕业设计题目好不好的标准我自己的判断是“业务有闭环技术有难点工作量可丈量”。科研项目验收管理系统天然符合这三点。业务上它有申请、审核、评审、归档是一条完整的闭环流程技术上它有专家分配、评分汇总、状态流转这些值得展开讲的逻辑点不是无脑的增删改查工作量上它涉及的实体足够多光数据库表就能设计十张以上论文里光画表和描述流程就能凑够篇幅。更关键的是这个题目有现实依据。很多高校的科研管理系统里验收环节仍然是半手工状态系统一般只管立项和经费真正到结题验收时反而要线下折腾。你把它做成毕设答辩时老师问“你这个系统解决什么问题”你不需要背概念直接描述一个真实场景就能打动他。1.2 业务角色与核心流程系统里主要靠四类角色撑起整个验收流程项目负责人登录系统后申报项目提交验收申请上传结题报告、成果证明、经费使用情况等材料。科研处管理员对验收申请做形式审核确认材料是否齐全、格式是否合规通过后进入专家评审阶段。评审专家查看被分配的项目材料按指标体系打分填写评审意见给出“通过/建议整改/不通过”的结论。系统管理员维护用户、角色、专家库、评分指标等基础数据。流程可以概括成一条主线项目负责人提交项目信息与验收材料科研处管理员审核通过后系统把所有待验收项目归类存档管理员从专家库中选出与本项目无关的专家分配评审任务专家逐一打分后台按权重汇总平均分最终由科研处根据得分和专家意见确认项目验收结论。这条流程里最容易被人忽略的是“过程留痕”。一个项目从申报到最后结项每一步是谁在什么时间做了什么操作都应该有日志记录。哪怕论文里不提这点也会在设计评审表时被老师追问提前做了能加分。1.3 功能模块全景具体功能模块可以拆成下面这张表论文的功能模块图和开发时的工程结构都按这个来模块子功能主要对象登录认证账号登录、退出、修改密码全部用户项目管理新增、修改、查询、删除、状态查看项目负责人、科研处材料管理材料上传、下载、预览、类型分类项目负责人形式审核审核通过、驳回、填写审核意见科研处专家库管理专家信息维护、领域维护、回避单位记录系统管理员专家分配手动分配、自动随机分配、取消重分配科研处评审打分分指标打分、评审意见、建议决议专家汇总统计项目通过率、得分排行、项目类型分析科研处、管理员流程记录状态流转日志、操作日志全部到这里题目的业务面基本就清楚了。后面所有设计都是围绕这张表展开的。2. 技术选型与数据库设计把业务落到表上2.1 SpringBoot Vue MySQL 为什么是稳妥组合技术选型不追求新追求“能自洽、能讲清楚、能装得下业务”。后端用 SpringBoot核心原因就是起步快、约定大于配置。SpringBoot 自带 Tomcat、内嵌 JSON 解析、自动化配置一个spring-boot-starter-web依赖就把 Web 开发的环境全部搞定。配合 MyBatis-Plus单表 CRUD 几乎不用写 SQL复杂统计再手写 XML 或注解 SQL开发效率明显比 SSM 手写一大坨配置高。前端选 Vue。Vue 的上手曲线比较平缓用 Vue CLI 搭工程、配好 vue-router 和 vuex再用 Element UI 把页面组件凑起来大学生完全可以在两三天内把管理后台的界面撑出来。选 Vue2 Element UI 是因为它的中文资料最多遇到问题搜索一下基本都能解决Vue3 生态虽然更新但对一个没有太多项目经验的人来说坑反而更多。MySQL 则是经典中的经典。InnoDB 支持事务、外键约束、行级锁一套系统跑几千个项目的数据量毫无压力。字符集记得设置成utf8mb4存中文姓名、标点符号都不会出乱码。技术栈评价表如下层次技术理由后端SpringBoot 2.7.x MyBatis-Plus 3.5.x开发效率高、资料多前端Vue 2.6.x Element UI 2.15.x组件全、入门快数据库MySQL 8.0 InnoDB utf8mb4稳定、事务支持好认证方式JWT 拦截器轻量、答辩时容易讲细节部署Nginx JAR 包结构清晰、资源占用低2.2 核心表结构与字段设计数据库是整个项目的地基。我最终设计了十一张表这里挑最关键的五张说说设计思路。用户表sys_userCREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登录账号, password VARCHAR(100) NOT NULL COMMENT BCrypt加密后的密码, real_name VARCHAR(30) NOT NULL COMMENT 真实姓名, role VARCHAR(20) NOT NULL COMMENT 角色admin/manager/principal/expert, department VARCHAR(100) DEFAULT NULL COMMENT 所属学院或部门, expert_field VARCHAR(100) DEFAULT NULL COMMENT 研究领域专家用户才有, status TINYINT DEFAULT 1 COMMENT 1启用 0禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表;这里没有单独拆“角色表”和“用户角色关联表”因为项目用户只有四种固定身份拆三张表反而增加了联查复杂度。用角色字段直接区分在拦截器里判断一下就能完成接口权限控制属于“够用原则”。项目表projectCREATE TABLE project ( id BIGINT PRIMARY KEY AUTO_INCREMENT, project_code VARCHAR(50) NOT NULL COMMENT 项目编号, project_name VARCHAR(200) NOT NULL COMMENT 项目名称, principal_id BIGINT NOT NULL COMMENT 项目负责人ID关联sys_user, department VARCHAR(100) DEFAULT NULL COMMENT 负责人所属单位, project_type VARCHAR(30) DEFAULT 纵向项目 COMMENT 项目类型, fund_amount DECIMAL(10,2) DEFAULT 0 COMMENT 到账经费, start_date DATE DEFAULT NULL, end_date DATE DEFAULT NULL, apply_status INT DEFAULT 0 COMMENT 0待提交 1待形式审核 2待专家评审 3评审中 4待终审 5已结项 6已退回整改, final_score DECIMAL(5,2) DEFAULT NULL COMMENT 最终验收得分, final_result VARCHAR(20) DEFAULT NULL COMMENT 通过/不通过/建议整改, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT科研项目表;材料表project_material存储每个项目上传的结题材料CREATE TABLE project_material ( id BIGINT PRIMARY KEY AUTO_INCREMENT, project_id BIGINT NOT NULL, material_name VARCHAR(200) NOT NULL COMMENT 材料名称, file_path VARCHAR(255) NOT NULL COMMENT 服务器存储相对路径, file_size BIGINT DEFAULT 0 COMMENT 文件大小字节, file_type VARCHAR(50) DEFAULT NULL COMMENT 扩展名, verify_status INT DEFAULT 0 COMMENT 0未审核 1通过 2被驳回, upload_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT项目验收材料表;评审任务表review_task是核心中的核心。一个项目分配给多个专家就会产生多条评审记录拿项目 ID 关联CREATE TABLE review_task ( id BIGINT PRIMARY KEY AUTO_INCREMENT, project_id BIGINT NOT NULL, expert_id BIGINT NOT NULL COMMENT 专家用户ID, score DECIMAL(5,2) DEFAULT NULL COMMENT 专家综合得分, conclusion VARCHAR(20) DEFAULT NULL COMMENT 通过/建议整改/不通过, comment VARCHAR(500) DEFAULT NULL COMMENT 专家意见, submit_time DATETIME DEFAULT NULL, UNIQUE KEY uk_project_expert (project_id, expert_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT专家评审任务表;再加一张评审明细表review_score用来存专家对每个指标的打分CREATE TABLE review_score ( id BIGINT PRIMARY KEY AUTO_INCREMENT, task_id BIGINT NOT NULL COMMENT 对应评审任务ID, indicator_id BIGINT NOT NULL COMMENT 指标ID, score DECIMAL(5,2) NOT NULL, UNIQUE KEY uk_task_indicator (task_id, indicator_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT评审指标得分表;指标表accept_indicator单独维护因为权重和指标名称以后可能会调整写成死在代码里再改就麻烦了。2.3 把验收状态抽象成状态机这系统最值得讲的东西不是 CRUD而是apply_status字段对应的一整套状态流转。我在项目里定义七种状态0 待提交项目负责人还在编辑项目信息可自由修改。1 待形式审核负责人点了“提交验收申请”科研处可以审核。2 待专家评审形式审核通过但专家还没分配。3 评审中专家已分配部分专家已打分还有专家没提交。4 待终审所有专家已打完分科研处根据汇总结果做最终确认。5 已结项终审通过项目归档。6 已退回整改形式审核不通过或终审建议整改项目回到负责人手里。状态不是随便跳的每条转换都有触发动作和操作人。例如0 → 1负责人提交。1 → 2科研处形式审核通过。1 → 6科研处审核不通过。2 → 3自动分配专家后立即进入评审中。3 → 4所有专家提交打分完成。4 → 5科研处终审通过。4 → 6科研处终审建议整改。6 → 1负责人整改后重新提交。每次状态变更都往accept_record表里插一条日志记录“谁在什么时间把状态从几改成几备注是什么”。这样管理员在界面上看到的“流程历史”其实就是一个按时间倒序的日志列表后端实现非常简单但效果专业很多。3. 后端核心逻辑实现登录、上传、分配、评分3.1 登录认证JWT 拦截器不额外引框架毕业设计阶段我不建议把 Spring Security 拉进来。Security 本身是套很完整的权限体系配置类、过滤器链、UserDetailsService 那一套学起来就能占掉一周时间而且它帮你做了大部分事答辩时老师问“过滤器链里面每一步是怎么回事”你一旦答不上来反而暴露短板。自己写 JWT 拦截器逻辑完全可控每一个环节都能讲明白。JWT 生成放在登录接口里登录成功把用户的 userId、真实姓名、角色塞进 token设置过期时间例如 2 小时。自定义拦截器public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { if (/api/user/login.equals(request.getRequestURI())) { return true; } String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录或登录已过期\}); return false; } token token.substring(7); try { Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } catch (Exception e) { response.setStatus(401); response.getWriter().write({\code\:401,\msg\:\token无效\}); return false; } } }拦截器注册时只需要配置一个路径拦截规则比如/api/**再放行/api/user/login和文件访问/files/**。接口级权限我通过自定义注解控制Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String value(); }拦截器里在放行前拿到当前方法上的注解判断角色是否匹配。这个设计比给每个方法手写一次角色判断干净得多也值得写进论文第五章里。3.2 材料上传的两个关键点材料上传最容易被忽略的是配置和路径规划。首先 SpringBoot 默认单文件上传上限是 1MB实测定不够用。配置文件里要主动调大spring: servlet: multipart: max-file-size: 50MB max-request-size: 100MB文件保存路径我采用了“硬盘目录 数据库相对路径”的方式。上传时读取系统临时目录或配置的 uploadPath 属性按年月建目录文件名改成UUID.原后缀避免中文名和重名问题。数据库里只存相对路径例如/uploads/2025/05/xxxxx.pdf显示或下载时再拼上服务器地址。解析上传请求时不要用MultipartFile[]直接一个个入库先校验扩展名和后缀是否匹配。学聪明一点的做法是先允许上传入库后材料状态默认 0未审核科研处形式审核时再确认材料。毕设虽然不用做到病毒扫描那么重但至少要限制 PDF、WORD、ZIP 等常见扩展名否则一个 exe 传上来演示现场会很难看。3.3 专家自动分配与回避规则专家分配是答辩老师最喜欢问的模块它不像登录那些有现成方案而是需要自己设计业务规则。我的规则是这样定的某个项目进入“待专家评审”状态时科研处管理员可以一键自动分配。系统先从专家库中筛选出“领域匹配”的专家即专家档案里的研究领域与项目所属方向一致然后过滤掉“回避专家”也就是专家所属学院与项目负责人所属学院相同防止同事之间相互评审最后按“当前待评审项目数”升序排列优先安排手上任务少的专家取前三位。关键代码逻辑public void autoAssign(ListLong projectIds) { for (Long projectId : projectIds) { Project project projectMapper.selectById(projectId); ListUser candidates expertMapper.selectAvailableExperts(project.getDepartment(), 3); if (candidates.size() 3) { throw new BizException(项目 project.getProjectCode() 可用专家不足3人); } // 这里是业务核心批量插入评审任务 ListReviewTask tasks new ArrayList(); for (User expert : candidates) { ReviewTask task new ReviewTask(); task.setProjectId(projectId); task.setExpertId(expert.getId()); tasks.add(task); } reviewTaskMapper.insertBatchSomehow(tasks); project.setApplyStatus(3); projectMapper.updateById(project); } }这里必须加事务。如果在循环中间专家数量不足前面项目已经分配了一半后面报错回滚整个分配动作会后悔都来不及。我当时的处理是用Transactional(rollbackFor Exception.class)包住整个批量分配方法并在分配之前先一次性校验专家数量而不是边分配边判断。3.4 评分汇总与项目结论判定专家在评审页面填写每个指标的得分系统实时算出该专家的综合得分。等所有专家都提交后项目终态得分由系统汇总专家综合得分 Σ(指标得分 × 指标权重)项目最终得分 所有专家综合得分的算术平均指标权重我放在accept_indicator表的 weight 字段里比如创新性占 30%、完成度占 30%、成果产出占 25%、经费使用占 15%。这样权重调整时只需要改数据库记录不需要重新编译后端。分数和结论判定使用 BigDecimal不能用 double。double 在求和时会有经典的 0.1 0.2 精度问题一旦答辩演示时算出一个 89.999999 分自己都会尴尬。最终结论规则我设置成最终得分结论≥ 75通过60 ~ 75建议整改 60不通过所有专家都完成打分后系统自动计算最终得分调用一个finishReview(projectId)的方法在同一个事务里完成三件事更新 project 的最终得分和状态、把所有评审任务的提交时间补上、往流程日志表里插入“评审完成”记录。这三件事必须在一个事务里因为如果只更新了分数忘了记录日志流程历史里就会出现“项目状态从评审中变成待终审但没有可追溯记录”的漏洞。3.5 分页查询与统计接口列表页需要的不是全表数据而是“分页 多条件筛选”。MyBatis-Plus 的分页插件配置好后写一个带条件的查询只需一行LambdaQueryWrapperProject wrapper Wrappers.lambdaQuery(Project.class) .eq(StringUtils.hasText(name), Project::getProjectName, name) .eq(status ! null, Project::getApplyStatus, status) .eq(StringUtils.hasText(department), Project::getDepartment, department) .orderByDesc(Project::getCreateTime); PageProject page projectMapper.selectPage(new Page(current, size), wrapper);统计接口则写了两条自定义 SQL一条按项目类型分组查数量一条按月份查验收通过率返回给前端 ECharts 渲染柱状图和折线图。这类 SQL 不需要复杂优化数据量在毕业设计范围内完全跑得动。4. 前端实现细节页面、组件与联调4.1 前端工程结构和路由设计Vue CLI 生成的前端工程目录我按职责划分src/ ├── api/ // 所有接口请求方法 ├── assets/ // 静态资源 ├── components/ // 通用组件 ├── router/ // 路由配置 ├── store/ // vuex 状态管理 ├── utils/ // axios 封装、token 工具 └── views/ // 页面组件路由配置上每个角色看到的菜单不一样。这个项目没有做高度动态化而是在侧边栏菜单里用v-if判断用户角色来显示对应菜单组。例如“专家评审”菜单只有角色为 expert 才显示。好处是代码简单直观不容易出错坏处是权限控制没有做到按钮级别但对毕设系统来说完全够用。路由守卫加一道基础校验router.beforeEach((to, from, next) { if (to.path /login) { next(); return; } const token localStorage.getItem(token); if (!token) { next(/login); } else { next(); } });这套方案细致但不过度答辩演示时如果临时清了缓存被踢回登录页响应也是符合预期的。4.2 el-upload 自定义上传处理Element UI 的 el-upload 组件默认把文件直接 POST 到指定的 action 地址这在接口需要携带 token、地址又被代理的情况下会很别扭。我的做法是用http-request属性覆盖默认上传行为自己用封装好的 axios 上传handleCustomUpload(options) { const formData new FormData(); formData.append(file, options.file); formData.append(projectId, this.projectId); uploadMaterial(formData).then(res { if (res.code 200) { this.$message.success(上传成功); this.loadMaterialList(); } }).catch(() { this.$message.error(上传失败); }); }这样 token 会自动通过 axios 拦截器加到请求头里后端也能统一校验比组件自带 action 方式可控得多。另一个容易踩的是上传成功后要刷新材料列表不然用户会以为传了个寂寞。4.3 流程步骤条与状态展示项目详情页最直观的部分就是流程步骤条。用 Element UI 的 el-steps 把七种状态映射成用户能看懂的四到五个步骤申报提交、形式审核、专家评审、终审结项。状态映射放在前端工具函数里export function statusText(status) { const map { 0: 待提交, 1: 待形式审核, 2: 待专家评审, 3: 评审中, 4: 待终审, 5: 已结项, 6: 已退回整改 }; return map[status] || 未知; }状态变更时重新拉取项目详情整个步骤条实时更新。这一步是演示时的视觉加分项能让老师一眼看到系统对流程的控制。4.4 axios 封装与全局错误处理前端所有请求都走同一个 axios 实例拦截器做两件事请求加 token响应统一解包和报错。service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer token; } return config; }); service.interceptors.response.use( response { const res response.data; if (res.code 200) { return res; } if (res.code 401) { localStorage.removeItem(token); router.push(/login); } this.$message.error(res.msg || 请求失败); return Promise.reject(res); }, error { this.$message.error(网络异常); return Promise.reject(error); } );这个封装让业务代码里不用到处写 try catch数据流转清晰后端返回的 code 统一在拦截器处理。开发后期我就是靠这套体系一遇到 401 就立刻知道 token 出问题不用一个页面一个页面找。5. 踩坑实录开发期间我遇到的七个真实问题5.1 开发环境的跨域问题症状前端页面请求后端接口浏览器控制台报 CORS error请求根本没发出去。原因开发时前端跑在 8080后端跑在 9000浏览器同源策略拦截了跨域请求。解决方式开发环境不要在 axios 里写死完整后端地址而是在vue.config.js里配置 proxy 代理用相对路径/api发起请求代理转发到http://localhost:9000。这样前端页面访问的是同源地址既解决了跨域也避免了后端接口地址写死在前端代码里。生产环境则由 Nginx 提供反向代理同样只暴露一个域名。5.2 时间字段和时区问题症状前端展示的后台时间比实际时间早 8 小时数据库里存的时间没问题但 JSON 返回时被转成了 UTC 格式。原因SpringBoot 默认处理 LocalDateTime 时没有指定时区Jackson 序列化用的是默认 UTC。解决方式在 application.yml 里统一配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8同时 MySQL 连接串加上serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8。这两处配置缺一个时间就会在整个链路某一段发生偏移。这种问题在本地测不出部署到国内服务器后如果时区不对马上会暴露。5.3 JWT 过期导致重复跳转症状token 过期后页面同时发出多个请求全部返回 401结果响应拦截器里的router.push(/login)被调用了好多次浏览器里路由报错。原因拦截器里没有做“是否已跳转登录页”的判断。解决方式在拦截器外层用一个标志位控制如果已经触发过一次 401 跳转后续请求就不再重复跳let isRedirecting false; if (res.code 401) { if (!isRedirecting) { isRedirecting true; localStorage.removeItem(token); router.push(/login); } }这个小细节虽然不起眼但演示时候如果 token 恰好过期系统不会出现一堆重复跳转的奇怪现象。5.4 上传文件大小限制症状上传一个十几兆的 PDF 时报错请求会在进入 Controller 之前就被 Tomcat 拒掉。原因SpringBoot 默认单文件上传上限是 1MB同时如果部署了 NginxNginx 的默认请求体上限也是 1MB。解决方式两处都要改。后端在 application.yml 中调大 multipart 限制Nginx 配置中加client_max_body_size 100m;只改后端不改 Nginx部署到线上照样失败。我第一次就只改了后端本地测试通过部署后被打了个措手不及。5.5 文件下载中文名乱码症状下载验收材料时文件名带中文的文件在部分浏览器里变成一串乱码。原因Content-Disposition响应头里的 filename 是 ISO-8859-1 编码中文没有正确编码。解决方式后端下载接口设置响应头时使用 RFC 5987 格式String encodedName URLEncoder.encode(realName, UTF-8).replaceAll(\\, %20); response.setHeader(Content-Disposition, attachment; filename*UTF-8 encodedName);实际项目中我是先查数据库拿到存储的文件名再返回给前端旧文件名。后端存储名用 UUID但下载时展示的还是用户上传时的原始名字这个体验不能省。5.6 前端刷新页面 404症状部署后用户在一个详情页刷新Nginx 直接返回 404。原因Vue Router 使用了 history 模式浏览器直接访问某个具体路由时Nginx 没有对应的真实物理文件所以找不到。解决方式在 Nginx 配置一个 try_fileslocation / { try_files $uri $uri/ /index.html; }这样所有路径都先找静态文件找不到就回退到 index.html由前端路由接管。这个配置几乎每个 Vue 部署项目都会遇到属于必考项。5.7 MyBatis-Plus 字段自动填充不生效症状实体类里明明加了TableField(fill FieldFill.INSERT_UPDATE)但更新数据时 update_time 字段没变化。原因自动填充需要一个实现类去真正执行赋值逻辑加了注解不代表框架会自动找到值从哪来。解决方式定义一个实现类在 insertFill 和 updateFill 方法里填充当前时间Component public class MyMetaObjectHandler implements MetaObjectHandler { Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, createTime, LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } }如果连这套都不想做最省事的办法就是像项目表里那样用数据库层的ON UPDATE CURRENT_TIMESTAMP前提是数据库自己管理更新时间不依赖业务代码。6. 论文、部署文档和答辩展示的实战经验6.1 论文怎么组织结构毕设论文一般六到七章我的参考结构是第一章 绪论写科研项目验收的背景和意义列出国内外管理系统研究现状再说明本文主要工作。第二章 相关技术介绍SpringBoot、Vue、MySQL、MyBatis-Plus、JWT。不需要长篇大论每种技术一小节两三页就够。第三章 系统分析角色分析、业务流程、功能需求、非功能需求。这里要把“验收流程”画成流程图把每个用例写清。第四章 系统设计总体架构图、功能模块图、数据库 E-R 图、核心表结构。第五章 系统实现环境说明、核心模块实现过程配关键代码截图和界面效果图。重点放专家分配、评分汇总、状态流转。第六章 系统测试功能测试用例表 性能测试数据。论文里要注意一个原则图和表永远比文字重要。E-R 图、用例图、流程图、系统架构图这些图一摆页数瞬间厚重起来老师也更容易看懂你的系统。6.2 部署文档怎么写才能直接照做部署文档一定要按“从零开始”的思路写而不是只写你自己服务器的操作记录。我总结的部署文档结构环境要求JDK 1.8MySQL 8.0Nginx 1.2Node.js 16。数据库初始化执行db_init.sql脚本里包含建库、建表、初始数据。管理员和测试账号的密码统一在SQL里注明明文和加密后值。后端部署mvn clean package -DskipTests打成 jar用下面的命令启动java -jar acceptance-system.jar --server.port9000 --spring.profiles.activeprod前端部署npm run build生成 dist把 dist 里所有文件拷贝到 Nginx 的 html 目录。Nginx 配置转发/api到后端 9000 端口转发/files到本地磁盘路径其余路径指到前端静态文件。启动顺序和验证先启动 MySQL再启动 jar最后启动 Nginx。浏览器访问首页能登录成功就算部署完成。部署文档里一定要写清楚“端口是否被占用”“如何看日志”“如何重启”这是最容易忽略但实际运维里最先用到的三件事。6.3 答辩演示路径与常见问题应对答辩演示不要一上来就摆技术先按业务角色走一遍。我的演示动线是用项目负责人账号登录新建一个“基于深度学习的图像识别系统横向课题”项目上传一份结题报告 PDF。切换到科研处账号进行形式审核审核通过后进入专家分配点击“自动分配”演示系统选出三位本领域专家。切换到专家账号打开两个待评审项目分别填写指标分数和评审意见提交。回到科研处查看项目状态已变为“待终审”最终得分 82.5 分点击“确认通过”项目结项。打开统计页面看项目类型分布图和验收通过率折线图。这样演示下来所有核心功能都覆盖了。老师最后提问主要集中在几个点状态是怎么流转的、专家分配规则是什么、为什么不引入 Spring Security、多专家打分时数据库怎么防止重复提交。回答的核心思路永远是从业务需求出发状态流转因为有状态机字段专家分配因为有回避规则不引入 Security 是因为自定义 JWT 拦截器够用且在论文里能讲清细节。只要自己的逻辑是自洽的老师们一般不会死磕某一个框架。最后再分享一个小技巧整个项目编码过程我给自己定了一条纪律每个接口提交前都要先想“如果数据是脏的、并发是高的这个接口会不会崩”。按这条标准去自查一遍能少踩很多并发和空指针的坑。答辩时也把这个过程讲出来比干背知识点要有说服力得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询