ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Spring Boot职称评审系统开发:从需求拆解到答辩避坑

Spring Boot职称评审系统开发:从需求拆解到答辩避坑 每到毕业季“计算机毕业设计”就成了一个高频词。今年来问我的人里有一大批是选了Spring Boot 职称评审系统这个方向的。乍看题目平平无奇“温州商学院职称评审系统”听起来像是个定制化项目但把它拆开看它其实是高校管理系统里最典型的一类业务多角色、多流程、多状态、材料审批、专家评分。把这一套吃透了不只是交一个毕设而是把 Spring Boot 后端开发的核心能力完整过了一遍。这篇文章我就以“温州商学院教师职称评审管理系统”为背景从需求拆解、架构设计、数据库建模、核心代码实现到毕设答辩常见的坑一条线讲透。无论你学校名是什么只要题目是“XX学院职称评审系统”这套思路都能直接拿来用。1. 项目概述与需求拆解1.1 职称评审系统到底在解决什么问题先别急着写代码。你拿到这个题目第一件事不是建工程而是搞清楚职称评审在高校里是怎么运转的。教师评职称评的不是一个人的考试成绩而是一套复杂的综合评估。一位教师想要从讲师升副教授要经历个人申报、材料提交、学院审核、学校复核、专家评审、结果公示等多个环节。申报材料里包括学历学位证书、职称证书、论文成果、科研项目、教学工作量、学生评教数据、获奖情况一大堆文件散落在不同部门。纸质化流程下光材料整理就能拖几周审核人要在几十份材料之间来回切换专家评审要从不同校区赶到现场结果统计更是容易出错。“温州商学院职称评审系统”这类项目核心就是把这套线下流程搬到线上教师在线填报信息、上传证明材料系统自动做格式校验和进度跟踪审核人员按权限查看材料、填写意见专家在线打分管理员最终汇总数据、生成公示结果。所以这个系统的价值点在哪我总结了四句话流程清晰化、材料电子化、评审标准化、结果可追溯。你在写开题报告和论文摘要的时候把这四句话作为核心主线来强调比空喊“提高了工作效率”有说服力得多。1.2 用户角色与核心功能拆解职称评审系统最忌讳把需求做混乱。很多同学一上来就把所有页面堆在一起登录后大家都看到一样的菜单这其实在评审老师眼里就是减分项。正确做法是先把角色捋清楚。在我做过的类似项目里通常把用户分成四类教师用户提交职称申报表、上传佐证材料、查看审核进度和评审结果。二级学院教学秘书/院长审核本学院教师提交的材料是否属实核对教学工作量。学校人事处管理员配置评审批次、分配专家、汇总评审结果、发布公示。评审专家接收分配名单在线查看申报材料按维度打分并填写评审意见。围绕这些角色核心模块可以拆成六块用户登录与权限、教师申报管理、材料上传与预览、审核流、专家评审打分、结果管理。这六块里最有技术含量的是审核流和专家评审。审核流程涉及多条状态流转申报一条数据从“草稿”到“已提交”再到“院系审核中”“人事处审核中”“专家评审中”最后到“已公示”每一步操作要记录日志。专家评审看似简单实际牵扯评分策略——有的学校按百分制总分有的按维度加权计算还有去掉最高分最低分的规则。1.3 选题亮点为什么这个题目值得做我见过太多毕设选题什么“学生宿舍管理系统”“图书管理系统”不是说不行而是功能太平面基本都是单表CRUD做完之后既没有深度也没有难度。职称评审系统不一样它天然具备三个毕业设计加分点。第一业务流程复杂能体现系统设计能力。多角色操作同一个数据实体但视角和交互完全不同这不是简单增删改查能覆盖的。第二有状态机。从申报到公示一条数据经历多个状态每个状态下能做的事不一样。用状态模式还是用状态字段要不要记录操作日志这些问题很值得在论文里展开。第三评审打分带有业务规则。如果只是存一个总分太简单了。你可以设计多维度评分表、权重配置、自动算分这一下就把系统的业务复杂度拉上来了。所以我的建议是如果你的毕设选了“XX学校职称评审系统”千万别把它做成多个 CRUD 页面的拼接要把重点放在“流程”和“规则”上。2. 技术选型与Spring Boot架构设计2.1 为什么是Spring Boot聊聊框架选择的理由项目标题里明确点名了 Spring Boot这其实也是目前高校毕设的主流方向。原因很直接Spring Boot 让基于 Spring 的企业级开发变得足够轻不需要繁琐的 XML 配置内嵌 Tomcat 容器打包成 Jar 就能直接跑。对本科生来说它的学习曲线相对友好生态又极其成熟遇到问题一查就有答案。对比一下更直观。早期 SSM 阶段做这套系统你要配 Spring 的 ApplicationContext.xml、SpringMVC 的 dispatcher-servlet.xml、MyBatis 的 mapper 映射文件三个配置文件互相引用稍不注意路径写错就启动失败。Spring Boot 用 starter 加自动配置把大部分样板工作拿掉了你可以把更多精力放在业务逻辑上。但是我要提醒一点答辩的时候老师很可能会问“你为什么选择 Spring Boot而不是 SSM 或者 Spring Cloud”。不要只说“因为简单”要从三个层面回答开发效率层面自动配置 starter 简化了依赖管理和环境搭建维护层面微服务架构的一种轻量实现方式适合中小型系统以后要拆分也方便生态层面与 Spring Security、MyBatis、Redis 都有成熟整合方案。2.2 前后端分离还是直接用模板引擎这个是每一届学生都会纠结的问题。我直接说结论如果你有六个月以上的准备时间选前后端分离如果时间紧、毕业设计答辩在即建议 Spring Boot 结合 Thymeleaf 或者直接用若依这类脚手架。前后端分离方案在后端只需要提供 RESTful API前端用 Vue 或 React 开发部署时可以前端 Nginx 代理后端接口。好处是结构清晰未来扩展移动端不需要改后端而且写论文时可以很自然地把“前后端分离架构”单列一章。坏处是你必须同时掌握并调试两个工程前端打包跨域、Token 存续这类问题会消耗不少时间。对于不想在前端上投入大量精力的同学我推荐一个折中方案后端使用 Spring Boot前端页面用若依的前端框架或者直接用 Vue 3 Element Plus。自己从零写一套漂亮的 UI 工作量很大用现成组件库能快速出效果同时展示效果还相当不错——属于性价比非常高的选择。从我个人的评判标准来看毕设系统最怕的不是功能少而是功能混乱。前后端分离或者脚手架都能帮你把目录理清最忌讳的是自己在模板引擎里写原生 HTML 加 jQuery页面越来越多之后改一个公共头部都费劲。2.3 整体架构规划与项目目录技术栈清单Spring Boot 2.7、MyBatis-Plus、MySQL 8.0、Redis缓存 Token 或菜单权限、Spring Security JWT 做认证授权、Lombok 简化实体类、Swagger/Knife4j 做接口文档。为什么要用 MyBatis-Plus 而不是纯 MyBatis毕业生工程量的角度考虑MyBatis-Plus 内置了通用 CRUD 方法写单表操作的 Mapper 接口几乎不用写 SQL分页由一个内置插件搞定。你只要把精力放在多表查询和复杂统计上这样就合理多了。Spring Data JPA 虽然也行但在复杂 SQL 上不如 MyBatis 直观而且评职称系统里有大量自定义统计查询用 MyBatis 更顺手。标准的项目结构我建议这样规划com.wzbc.review ├── config/ │ ├── SecurityConfig.java │ ├── MybatisPlusConfig.java │ └── CorsConfig.java ├── controller/ │ ├── AuthController.java │ ├── DeclareController.java │ ├── ReviewController.java │ └── AdminController.java ├── service/ │ ├── impl/ ├── mapper/ ├── entity/ ├── dto/ ├── vo/ └── common/ ├── Result.java ├── exception/ └── utils/分层的思想是Controller 只做参数接收和结果封装不写业务Service 里处理业务逻辑和事务Mapper 只负责数据库操作。刚开始写毕设的同学最容易犯的毛病是 Controller 里堆 SQL整个项目看下来根本没有 Service 层应该有的样子。分层不是写论文给别人看的是为了你自己维护方便。你想想后期加一个“根据学院统计申报人数”的功能如果逻辑散落在 Controller 里你得翻多少个文件2.4 数据库设计职称评审的数据核心数据库设计直接决定你的项目完成度。我把核心表拿出来逐个讲。用户表 t_user存放教师和管理员账号。字段就是常规的 username、password、real_name、role_type。role_type 用数字区分比如 0 教师、1 院系秘书、2 人事处管理员、3 评审专家。不要用字符串如“teacher”“admin”当前端展示字段一是查询麻烦二是前端还要做映射。教师信息表 t_teacher_info关联用户表存工号、所属学院、职称、学历、入职时间、研究方向等信息。注意一条用户记录只对应一条教师基础信息如果你做的系统支持教师多个角色比如既是普通教师又是评审专家建议单独建专家库表 t_expert把 basic_info 和评审身份分开。职称申报表 t_declaration这张表是整个系统的业务核心。我列出关键字段申报人 ID、当前职称、申报职称、申报批次 ID、状态、学院审核意见、人事处审核意见、总分、评审结果。状态字段 status 用整型作为状态编码0 草稿、1 已提交待审核、2 院系审核通过、3 院系驳回、4 人事处审核通过、5 人事处驳回、6 专家评审中、7 已公示。这个状态编码在系统里到处都要用建议设计成常量类保持统一。申报材料表 t_declaration_material一对一挂在申报记录下包括论文代表作、科研项目证明、获奖证书、教学工作量证明等。用 file_name、file_url、material_type、upload_time 四个字段就能覆盖大部分场景。如果你是进阶版本可以做成一个主表加多个子表的结构比如论文单独建 t_paper_info 表做详细管理这样系统就更完整但同时也意味着工作量大增。审核记录表 t_approval_log这是很容易被忽略但价值极高的表。记录谁在什么时间对哪条申报记录做了何种操作操作意见是什么。有了这张表你可以在前端做一个“操作轨迹”时间轴答辩演示的时候导师看到这个细节会很加分。评审维度表与评分表评审维度表存维度名称和分值上限如“教学工作量 30 分”“科研成果 40 分”“社会服务 10 分”。专家打分表存专家 ID、申报 ID、每个维度的得分、总分、评语。加权计算逻辑可以放在 Service 层也可以让管理员先配置权重再自动算总评。数据库设计阶段有一条经验值得记住不要一开始就把所有字段都建好先按业务流程把主表和关联关系确定再逐步补字段。MyBatis-Plus 更新表结构还挺方便加了字段只要实体类同步改一下就行但表之间关联关系设计错了后面改起来就伤筋动骨。3. 核心功能模块的实操实现3.1 登录认证与权限控制Spring Security JWT职称评审系统的权限很明确不同用户看到的功能完全不一样所以认证授权模块一定要做得清晰。方案选型上我推荐 Spring Security JWT虽然配置比自定义拦截器复杂一些但这个是主流企业方案写在论文里也好扩展。设计思路是用户登录成功后接口返回一个 JWT 字符串后续每次请求在请求头里带 Authorization: Bearer token后端过滤器解析 token 并加载用户权限。这里有几个关键点。第一JWT 的 secret 密钥要放在 application.yml 里不要硬编码在代码中。第二token 过期时间不能太长也不能太短。设 24 小时比较合理因为教师提交材料可能是一个持续过程中途不能频繁掉线。第三密码存储一定要用 BCrypt 加密这是 Spring Security 内置的不要用 MD5 写习惯了就顺手。第四自定义一个 JwtAuthenticationFilter 继承 OncePerRequestFilter在过滤器里把当前用户信息放进 SecurityContext。核心配置示例我贴一个简版Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/api/auth/login, /api/auth/register, /doc.html, /swagger-ui/**).permitAll() .antMatchers(/api/admin/**).hasRole(ADMIN) .antMatchers(/api/review/**).hasAnyRole(EXPERT, ADMIN) .anyRequest().authenticated() .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } }我说一下这个配置的用意。放行登录接口和接口文档路径其余接口一律需要登录管理端接口限制 ADMIN 角色才能访问专家评审接口限制 EXPERT 和 ADMIN 访问。这样到答辩演示的时候你拿出不同角色账号一登录菜单变化清晰可见权限维度一下就展示出来了。3.2 申报流程与核心状态流转申报模块是推动整个系统运转的入口。教师登录后先能维护自己的基础信息然后创建一份申报记录选择当前职称和目标职称填写基本申报信息提交材料最后正式提交。这里有一个容易做错的地方很多同学把申报表和材料上传放在一个页面提交导致数据要么一次性全保存要么什么都没有。正确的做法是分步保存教师创建申报记录状态设为“0 草稿”在草稿状态下教师可以反复编辑申报信息和材料点击“提交审核”状态从草稿变为“1 待审核”此后教师不可再修改。如果被驳回状态回到驳回节点教师修改后可再次提交。后端的核心是状态流转服务我封装一个简单的方法来处理Transactional(rollbackFor Exception.class) public ResultString submitDeclaration(Long declarationId, Long userId) { Declaration declaration declarationMapper.selectById(declarationId); // 校验只能操作自己的申报单 if (!declaration.getUserId().equals(userId)) { return Result.error(无权操作他人申报单); } // 校验当前状态必须是草稿或已驳回 if (declaration.getStatus() ! 0 declaration.getStatus() ! 3 declaration.getStatus() ! 5) { return Result.error(当前状态不允许提交); } // 统计材料是否齐全至少需要一份材料才能提交 Long count materialMapper.selectCount( new LambdaQueryWrapperDeclarationMaterial() .eq(DeclarationMaterial::getDeclarationId, declarationId)); if (count null || count 0) { return Result.error(请至少上传一份证明材料); } declaration.setStatus(StatusEnum.WAIT_DEPARTMENT_AUDIT.getCode()); declaration.setSubmitTime(LocalDateTime.now()); declarationMapper.updateById(declaration); // 写入审核日志 approvalLogService.saveLog(declarationId, userId, 提交申报, 教师提交申报材料); return Result.success(提交成功); }我把设计原则再强调一下每一次状态变化需要校验当前状态是否处于可流转的条件并且记录日志。这样即使业务复杂代码也不会乱。状态枚举单独用一个类管理不要散落在 Service 里到处写数字这一点在代码评审时非常加分。实际操作中另一个体会是审核操作一定要做幂等处理。比如学院审核一个申报快速点了两次“通过”如果代码里没做状态判断状态就被覆盖了两次。加了状态校验之后第二次点击会因为“当前状态不允许审核”而失败——这个细节能让你的系统稳健性明显提高。3.3 专家评审模块打分与计算逻辑评审模块是职称评审系统业务价值最高的地方也是答辩提问的密集区。我一直觉得这里不要把专家权限做得太粗糙只让专家填个总分就完了。稍微往下深入一层整个系统的档次就完全不一样了。我的设计思路是这样的管理员在某个批次下配置评审维度比如“教学能力 30 分”“科研成果 40 分”“师德师风 10 分”“社会服务 20 分”系统把进入专家评审阶段的申报记录分配给相关专家每个申报至少分配给 3~5 个专家专家登录后看到待评列表点击进入详情查看申报信息和证明材料按维度打分并填写综合评价系统根据规则自动计算最终得分常用规则有去掉一个最高分去掉一个最低分后取平均或者按专家权重计算加权平均分。这个模块的数据库关系是这样的评审任务表 t_review_task 记录专家和申报记录的对应关系评审打分表 t_review_score 记录专家填写的每个维度的分数。前端页面用表格组件实现动态渲染维度列。算法部分看起来复杂实现其实很直接核心是流式处理public BigDecimal calculateFinalScore(Long declarationId) { ListReviewScore scores reviewScoreMapper.selectList( new LambdaQueryWrapperReviewScore() .eq(ReviewScore::getDeclarationId, declarationId)); MapInteger, ListBigDecimal dimensionScores new HashMap(); for (ReviewScore score : scores) { dimensionScores.computeIfAbsent(score.getDimensionId(), k - new ArrayList()) .add(score.getScore()); } BigDecimal total BigDecimal.ZERO; for (ListBigDecimal dimensionScoreList : dimensionScores.values()) { ListBigDecimal sorted dimensionScoreList.stream() .sorted(Comparator.reverseOrder()) .collect(Collectors.toList()); if (sorted.size() 3) { sorted sorted.subList(1, sorted.size() - 1); } total total.add(sorted.stream().reduce(BigDecimal.ZERO, BigDecimal::add)); } return total; }简单说就是先投影到每个维度然后对维度内部的专家打分做去极值或平均处理最后汇总。我用的是去极值法把最高分和最低分去掉再取剩余平均这样能有效避免个别异常分数影响公平。这个模块想好了以后你要在论文里写上“本系统评审规则可配置支持百分制、加权制、去掉极值制”这些都是体现业务深度的描述也是答辩时老师会重点看的地方。3.4 材料上传与文件管理职称评审系统里材料上传是绕不开的。你用什么样的方式管理文件也是评判一个毕设完成度高低的重要维度。最简单的方案是把文件上传到服务器本地磁盘数据库里存文件访问路径。不管你的生产环境怎么部署毕设阶段这样做完全够用且好理解。但要注意几个坑不能直接把文件路径返回给前端作为静态路径然后不做权限控制。因为评审材料涉及教师隐私没登录的人不应该能访问。正确做法是通过后端接口做权限控制后返回文件流。单个文件大小要限制论文附件一般不会太大建议单文件限制 20MB整体申报材料不超过 50MB。文件命名不要用原始文件名直接存要加上 UUID 前缀防止重名覆盖。我给一个文件下载接口的示例理解思路前端传材料的 fileId后端查出材料信息再用 HttpServletResponse 写出文件流。权限校验在 Service 层完成当前登录用户必须是材料所属申报人、审核人或者被分配评阅的专家否则拒绝下载。前两个条件用角色就能判断第三个要查评审任务表确认。这样一来你的项目就比普通的上传下载多了一层安全设计答辩时讲出来是很亮的点。从实用角度讲文件最好分目录存储按申报批次或学号文件夹分类。如果项目用的是本机存储遇到一个大问题就是重启后上传的文件路径对不上解决方法也很简单在配置里用一个固定的绝对路径作为 storage.upload-dir并通过配置文件注入而不是用相对路径拼来拼去。3.5 统计报表与数据可视化职称评审系统做到后期管理员最关心的就是统计。比如今年一共申报了多少副教授各学院分布如何通过率是多少平均分是多少这些数据如果不展示整个系统就显得没有一个闭环。统计模块我建议做两个视角的页面批次概览页展示每个评审批次的申报总数、各状态数量、通过率、专家评分平均分。用哈希统计加一个简单的 SQL 分组查询即可搞定避免在前端做大量循环判空计算。学院维度统计页按学院分组显示申报数量和平均分可以用 MyBatis-Plus 的分组查询再把结果封装成 VO 返回给前端前端用柱状图或饼图展示。代码层面一个典型的分组统计 SQL 可以写成对应 Mapper XML 里select idselectSchoolStatistic resultTypecom.wzbc.review.vo.SchoolStatisticVo SELECT t_school.name AS schoolName, COUNT(t_declaration.id) AS totalCount, SUM(CASE WHEN status 7 THEN 1 ELSE 0 END) AS approvedCount FROM t_declaration LEFT JOIN t_teacher_info ON t_declaration.user_id t_teacher_info.user_id LEFT JOIN t_school ON t_teacher_info.school_id t_school.id WHERE t_declaration.batch_id #{batchId} GROUP BY t_school.name /select我对统计模块的建议是不要贪多三到五个核心图表就够用。一张图代表一种分析视角答辩时你能对着每张图讲明白业务含义比堆十个图表却讲不清逻辑要好得多。4. 常见问题与排查技巧实录4.1 懒加载与 JSON 序列化异常关联查询是职称评审系统里最常见的操作。教师列表要关联学院名称申报记录要关联教师姓名和职称专家列表要关联当前任务数量。很多同学习惯在实体类里加 ManyToOne 或者直接在 Service 里查 A 再循环查 B于是会碰到两个经典问题。MyBatis非 MyBatis-Plus 的关联映射里如果你配置了懒加载且控制器直接返回实体类对象给前端时很容易出现 LazyInitializationException因为在事务结束后 Session 关闭懒加载的数据拿不到了。解决方式并不复杂不要在 Controller 层直接返回实体对象而是把数据转换为 VO 返回。我一般用 MapStruct 或者手写转换更稳妥的是在 Service 内做关联查询并把结果组装完整再封装 VO。这一步既解决了懒加载问题也避免了实体类字段宽范围暴露给前端。另一个经典问题是双向关联导致的递归序列化死循环。比如教师实体里有申报列表申报实体里又持有教师对象Json 化时就会无限嵌套。解决方式要么用 JsonIgnoreProperties 标注要忽略的字段要么就坚持用 VO 而非实体类交互。我倾向于后者因为解耦更彻底而且你的论文里可以写“通过 VO 模式避免实体类直接暴露给前端降低耦合”这句话在代码审查时是个加分项。4.2 Transactional 事务失效的三种场景职称评审提交申报这个动作必然要事务控制。修改申报表状态、写日志表这些都是同一个事务内要完成的。看起来简单但事务失效的坑我见过太多人踩。第一个场景同类内部调用。比如类 A 的方法 X 调用了同类的方法 YY 标了 Transactional实际上不会开启事务因为 Spring 的 AOP 代理机制只对代理对象调用生效同一个类里 this 调用不会经过代理。要解决就拆分注入把 Y 方法放到独立的 Service 类里。第二个场景没有走 Spring 容器管理的 bean。如果自己用 new 创建了一个 Service 实例去调用事务自然不生效。这个在毕设里不常见但偶尔会有同学把工具类写得像 Service 一样直接调用。第三个场景异常被吞。方法里 try-catch 掉了 RuntimeException事务感知到运行时异常才会回滚被吞掉之后事务不会回滚。正确做法是 catch 后往外抛或者在 catch 逻辑里用 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 手动标记回滚。我最想强调第一个场景。毕设答辩时老师如果深挖事务很可能会问“你这个提交申报和日志写入是原子的吗失败会一起回滚吗”你要是从没试过测试回滚被问到时会措手不及。建议你留一个测试用例主动抛异常看看数据库里有没有残留的日志记录。4.3 分页查询效率与 N1 问题职称评审系统里要写列表页申报列表、待审核列表、专家评分列表统统要分页。MyBatis-Plus 的 IPage 配合 LambdaQueryWrapper 非常方便但有一个 N1 问题非常常见。比如查询申报记录一页二十条。你写完主查询返回二十条申报然后循环遍历这二十条分别去查申报人的学院名、学科方向等信息。这就是 N1 次查询。一次列表查询在日志里可能变成数十条 SQL效率会很难看。虽然毕设数据量小看不出差别但等你写完论文去实习面试面试官一问“怎么优化分页查询”你还得答得上来。现在的 MyBatis-Plus 做法是先把主数据查出来再用in批量补充关联数据最后在内存中组装 VO。举例来说先查出二十条申报记录收集所有 user_id再查一次用户表返回列表并转为 Map然后循环组装。整个过程两条 SQL 就能搞定既高效又清晰。如果列表的筛选逻辑很复杂比如要按申报职称、批次、学院、状态、姓名模糊查询直接用 LambdaQueryWrapper 就足够了。注意 like 查询时用户输入的特殊符号像 % 和 _ 在前端传过来会合入 SQL 通配所以在拼查询条件前对用户输入做一下转义比较稳妥。4.4 材料上传的大文件处理和跨域问题跨域问题在前后端分离项目里一定会遇到。如果你的前端跑在 8080后端跑在 8081请求就会因跨域被浏览器拦截。解决方式有三种。第一种后端配置 CorsFilter比如用CrossOrigin注解或者写一个全局的 CorsConfiguration 注册 CorsFilter 到过滤器链上。注意 Spring Security 存在时CORS 配置必须放在 Security 过滤器链之前生效否则预检请求会被安全拦截页面依然报跨域。第二种前端通过 Nginx 反向代理把/api接口转发到后端端口。这种方案更接近真实企业部署但也多了一步配置工作。第三种把前后端部署在同域名的不同路径下利用 Spring Boot 的静态资源映射托管前端打包后的文件。这种方式最简单但对“前后端分离”概念的体现有一定削弱。我的建议是毕设阶段优先用 CorsFilter 方案代码量少演示效果好。然后在论文里把 Nginx 代理方案作为“生产环境部署方案”进行描述表示你理解更完整的部署链路。第二个头疼的问题是文件体积。教师上传的论文 PDF 动辄几十兆如果你在本机用 MultipartFile 直接接收Spring Boot 默认限制单文件 1MB需要在配置里调大spring: servlet: multipart: max-file-size: 20MB max-request-size: 50MB注意 max-request-size 是批量上传的总限制。如果你不调整 max-file-size只调了 max-request-size单个超过 1MB 的文件照样会被拒。这个配置闭着眼睛都该记住。要是文件真的到了几百 MB比如某个老师上传的课堂实录视频那就不能直接进上传接口了建议采用分片上传或者用对象存储服务方案。但毕设阶段其实不用做到那个复杂度在文档里提一句“对于大文件可采用分片上传策略”就够了千万不要给自己加无谓的工程量。4.5 我的调试心得与答辩准备建议最后分享一点个人调试心得。第一个格式问题使用参数统一封装 Result 时静态工厂方法必须覆盖成功和失败两个分支比如 Result.success(data) 和 Result.error(code, msg)。不要写到一个方法里然后用布尔区分成功失败这样前端拿到数据结构不一致处理起来非常恼火。第二个日志问题项目从第一天开始就启用接口访问日志。别人写的代码全靠调试保存时你有完整的日志文件来追踪线上线索效率能差出好几倍。第三个测试问题写完一个接口不要急着做前端联调先用 Swagger 或者 Postman 把接口文档测试一遍。每次改完数据库字段顺手把关联的 SQL 和文档同步更新避免到了联调阶段才发现字段名不一致。要说答辩准备我的建议很直接把整个项目的“核心流程图”和“状态流转图”画熟练这比背代码有用得多。老师大概率会问“如果你希望支持多轮评审怎么办如果评审专家临时换人怎么办”你要能顺着状态流转的思路把可扩展的方向讲清楚而不是陷在代码细节里硬答。结尾做了这么多年开发接触过好几套评审类系统我最大的体会是这类系统真正的难度不在某个技术点上而在于把一条多角色、多状态的业务线理顺。职称评审系统表面是一堆增删改查但当你真正去设计申报状态流转、审核日志、专家评分维度的时候你其实是在锻炼一种能力——把模糊的业务需求翻译成清晰的数据结构和接口设计。如果你现在正被这个题目卡住别慌。先画一张业务流程图把申报、审核、评审、公示四个环节理清楚再动手建表最后写代码。相信我你按这个顺序来速度反而会比从代码开始更快因为设计清楚了代码只是翻译的工作。最后再补充一个小技巧答辩前准备一份“系统缺陷与改进方向”的文档。不要害怕暴露系统的不足主动说“现在的维度配置还只能做到批次级未来可以细化到职级级”的老师往往比只说“系统很完善”的同学拿到的评价更高。因为老师想看到的是你的思考而不是你的完美。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进