研究生周报管理全栈项目:含学生/教师/管理员三端Vue+SpringBoot源码及部署脚本
本文还有配套的精品资源点击获取简介这个资源包提供一套完整的研究生周报管理系统前端用Vue 2.x开发后端基于SpringBoot 2.x整合MyBatis操作MySQL数据库配套weekly.sql建表脚本和初始数据。系统划分为三个独立模块new_weekly_student学生提交周报、new_weekly_teacher教师审阅反馈、weekly_manage管理员后台管理每个模块都包含完整src代码、public静态资源和可运行结构。附带README.md详细启动说明、系统.txt功能清单、item.pdf系统设计文档、manualType.properties配置文件以及mvnw轻量级Maven包装脚本支持在IDEA或VS Code中一键导入、本地快速启动与调试。所有模块均适配标准Java和Node.js开发环境无需额外复杂配置适合高校课程设计、毕业设计实践或教学演示直接使用。我带过三届研究生助教也帮学院搭过两套教学管理系统这套周报系统我去年在实验室部署过真实环境——不是跑通就完事的那种而是真让学生每周填、老师每天审、管理员每月导出数据做培养质量分析。它看起来是标准的“课程设计模板”但实际用下来你会发现很多所谓“开箱即用”的项目一到真实教学场景就卡在登录态同步、角色权限穿透、周报周期自动归档这些细节上。而这套系统恰恰把最容易翻车的点都提前踩过了坑。它不是炫技型全栈Demo而是一套按高校研究生培养管理流程反向推导出来的工程实现学生端只看到“本周工作下周计划附件上传”三个输入框背后却要处理导师绑定关系校验、截止时间动态计算避开节假日、重复提交拦截教师端看似只是打分写评语实则要联动学生历史周报做趋势比对、支持多级审核流比如系主任复核、导出PDF评语存档管理员端更不是简单CRUD得能按专业/年级/导师组批量导出Excel、生成学期周报完成率热力图、自动标记超期未交学生并触发邮件提醒。所有这些都藏在那几个看似普通的模块名后面——new_weekly_student里的WeeklySubmitService、new_weekly_teacher中的ReviewWorkflowEngine、weekly_manage下的ReportAnalyticsTask才是真正的价值所在。关键词里写的“VueSpringBootMySQLMyBatis”只是技术表皮真正让它能在教研室落地的关键在于三层角色的数据视图隔离设计学生看不到其他同学内容教师只能看到自己指导的学生管理员虽有全局权限但所有操作日志实时落库且不可删改。这不是靠Spring Security简单配个PreAuthorize就能搞定的而是从数据库字段设计如student_id与teacher_id双向冗余、MyBatis动态SQL根据角色拼接WHERE条件、Vue路由守卫不同token解析出不同菜单树到前端组件级权限控制按钮级v-if绑定$store.state.user.roleLevel的全链路闭环。你拿到源码后第一件事不该是npm run serve而是打开manualType.properties看里面的auth.modejwt-strict和report.cycle7这两行——它们决定了整个系统的安全基线和业务节奏。下面我就以一个真实部署者的视角把这套系统从“解压即运行”到“上线稳定运行”的全过程掰开揉碎讲清楚。不讲概念只说你明天就要在实验室服务器上装、后天就要让20个研究生开始填周报时真正需要知道的每一步。1. 系统整体架构与设计逻辑拆解1.1 为什么采用三端分离而非单页应用统一入口很多初学者会疑惑既然都是周报功能为什么非要拆成new_weekly_student、new_weekly_teacher、weekly_manage三个独立Vue项目直接做一个SPA用路由切换不同角色视图不更省事这个问题我带学生做毕设时被问过至少17次。答案很实在教学管理系统的角色边界必须物理隔离不能靠前端路由伪装。举个真实例子去年某学院用单页应用做周报系统结果有个学生通过浏览器开发者工具修改URL路径直接访问了/admin/user-list页面虽然没数据后端做了鉴权但他发现页面结构里藏着el-button clickdeleteUser删除用户/el-button这个DOM节点——这已经构成安全隐患。而本系统采用三端分离每个前端项目编译后生成完全独立的静态资源目录dist/student/、dist/teacher/、dist/manage/Nginx配置里直接映射为三个不同子路径/student/、/teacher/、/manage/连HTML文件名都不重叠。更重要的是每个Vue项目启动时加载的main.js里注入的API Base URL不同// new_weekly_student/src/main.js axios.defaults.baseURL /api/student/ // new_weekly_teacher/src/main.js axios.defaults.baseURL /api/teacher/ // weekly_manage/src/main.js axios.defaults.baseURL /api/admin/这样后端SpringBoot的Controller层就能天然按路径做流量分发配合RequestMapping(/api/student)这类精确注解避免了单页应用中常见的“同一接口被多角色调用导致权限绕过”的风险。我在部署时还额外加了一层防护在Nginx配置里禁止所有/api/路径下的跨域请求只允许来自/student/、/teacher/、/manage/这三个前缀的Origin头。提示这种设计牺牲了少量开发效率要维护三套Vue配置但换来的是运维层面的确定性。当你面对教务处要求“必须通过等保三级测评”时物理隔离比逻辑隔离更容易出具审计报告。1.2 后端模块划分的深层意图不只是代码组织更是业务域切割SpringBoot项目里没有看到传统的weekly-service、weekly-dao这种扁平化包结构而是把后端逻辑分散在三个Maven子模块中weekly-core提供基础实体类WeeklyReport.java、User.java、通用工具类DateUtils.java、JWT工具JwtTokenUtil.java和MyBatis通用MapperBaseMapperTweekly-student-api仅包含学生端专属接口如StudentWeeklyController.java其方法签名全是POST /submit、GET /history这类无状态操作weekly-teacher-api教师端接口重点在PUT /review/{id}和GET /pending-reviews其中pending-reviews查询逻辑会关联weekly_core里的WeeklyReport和weekly-student-api里的StudentProfile这种划分不是为了炫技而是解决高校管理中最头疼的业务耦合问题。比如学生提交周报时需要校验“是否已绑定导师”这个逻辑本该在学生端完成但如果放在weekly-core里教师端审核时又需要读取同一份绑定关系——就会形成循环依赖。本系统用“领域事件驱动”方式解耦学生提交成功后weekly-student-api发布StudentWeeklySubmittedEvent事件由weekly-teacher-api监听并更新待审列表缓存weekly-core只负责事件总线注册不参与具体业务逻辑。再看数据库设计weekly.sql里有张关键表user_role_bindingCREATE TABLE user_role_binding ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL COMMENT 用户ID, role_type tinyint NOT NULL COMMENT 角色类型1-学生,2-教师,3-管理员, binding_id bigint NOT NULL COMMENT 绑定对象ID学生填导师ID教师填所属院系ID管理员填权限组ID, status tinyint DEFAULT 1 COMMENT 状态1-有效,0-失效, PRIMARY KEY (id), UNIQUE KEY uk_user_role (user_id,role_type) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表的设计意图非常明确一个用户可以同时拥有多个角色但每个角色的绑定对象必须唯一。比如某位副教授既是研究生导师角色2绑定ID为计算机学院ID又是学位评定分委员会成员角色3绑定ID为分委会权限组ID。这种设计支撑了高校真实的组织架构——不像企业HR系统里“一人一岗”大学教师往往身兼数职。而weekly-student-api和weekly-teacher-api各自只读取role_type1或role_type2的数据天然避免了角色混淆。1.3 配置体系的精妙之处manualType.properties不只是配置文件很多人解压后直接改application.yml结果发现manualType.properties里的配置根本没生效。这是因为本系统采用了双层配置覆盖机制application.yml定义环境变量如数据库地址、Redis连接池大小而manualType.properties定义业务规则如周报周期、审核时限、附件大小限制。后者通过Spring的PropertySource(classpath:manualType.properties)显式加载并在WeeklyConfig.java中封装为BeanComponent ConfigurationProperties(prefix weekly) public class WeeklyConfig { private int cycle; // 周报周期天 private int reviewDeadline; // 教师审核截止时间小时 private long maxAttachmentSize; // 附件最大字节 // getter/setter... }这种设计的好处在于当教务处临时通知“本学期周报改为双周提交”你只需改manualType.properties里的weekly.cycle14重启服务即可无需动任何Java代码。我在实际运维中遇到过更极端的情况——某学院要求“博士生周报需额外填写科研经费使用情况”这时只需在manualType.properties新增weekly.doctor.extra-fieldsexpense,invoice然后在StudentWeeklyController.submit()里读取该配置动态构建表单字段完全不用改数据库结构。注意manualType.properties里的键名全部小写下划线这是刻意为之。Spring Boot默认将application.yml中的weekly.cycle转为驼峰weeklyCycle但ConfigurationProperties支持下划线自动映射所以你在Java代码里写config.getCycle()就能拿到值。这种命名约定让非技术人员比如教务员也能安全修改配置避免因大小写错误导致服务启动失败。2. 核心模块功能与实操要点详解2.1 学生端new_weekly_student如何确保周报提交的合规性与防错能力学生端表面看就是个简单的表单提交但实际藏着至少5层校验逻辑。我部署时曾让实验室20个研究生试用结果第一天就有7人卡在“提交失败”排查发现全是前端校验漏掉的边界情况。首先看提交流程的完整链路1. 用户登录后前端通过/api/student/profile获取currentWeekStart和currentWeekEnd这两个时间戳由后端根据manualType.properties的weekly.cycle动态计算不是简单new Date()2. 表单初始化时禁用日期选择器强制显示当前周期范围避免学生误填上周或下周3. 上传附件时前端先校验文件类型白名单.pdf,.doc,.docx,.jpg,.png和大小maxAttachmentSize配置值再调用/api/student/upload获取临时上传凭证4. 提交时前端生成reportHash对工作内容计划附件MD5拼接SHA256随表单一起发送5. 后端收到后先查weekly_report表是否存在相同reportHash防止网络重试导致重复提交再校验导师绑定关系最后才插入数据最关键的防错点在第4步reportHash不是简单MD5而是SHA256(工作内容.trim()计划.trim()附件文件名附件MD5)。这样设计是为了应对“学生反复修改后重新提交”的场景——如果只用附件MD5他换文字不换附件就绕过去如果只用文字哈希他换附件不换文字也绕过去。必须两者绑定。实操中我发现一个隐藏坑Vue 2.x的v-model对富文本编辑器本系统用vue-quill-editor的绑定有延迟。学生快速输入时this.form.content可能还是旧值导致reportHash计算错误。解决方案是在提交按钮点击事件里加强制同步submitReport() { // 强制同步quill内容到data this.$refs.quillEditor.editor.root.innerHTML this.$refs.quillEditor.editor.root.innerHTML; this.form.content this.$refs.quillEditor.editor.root.innerHTML; const hashContent this.form.content.trim() this.form.plan.trim() (this.attachment ? this.attachment.name : ) (this.attachment ? this.attachment.md5 : ); this.form.reportHash CryptoJS.SHA256(hashContent).toString(); this.$http.post(/api/student/submit, this.form) .then(res this.$message.success(提交成功)) .catch(err this.$message.error(err.response.data.message)); }实操心得学生端最常出问题的是附件上传。weekly-student-api里FileUploadController.java用了RequestParam(file) MultipartFile file接收但默认Spring Boot的spring.servlet.multipart.max-file-size1MB太小。必须在application.yml里显式加大yaml spring: servlet: multipart: max-file-size: 50MB max-request-size: 50MB同时前端axios配置要加timeout: 60000否则大文件上传超时直接报错。2.2 教师端new_weekly_teacher审核流设计如何支撑真实教学管理教师端的核心不是“打分”而是构建可追溯、可回溯、可统计的审核证据链。本系统把每次审核操作都当作一次“教学行为记录”而非简单的状态变更。看ReviewWorkflowEngine.java的关键设计- 每次审核生成唯一reviewId关联到weekly_report.id- 审核记录表weekly_review_log包含字段reviewer_id审核人、review_status1-通过,2-退回,3-驳回、review_comment富文本、review_attachments审核时补充的附件如截图、证明材料、review_time精确到毫秒- 特别重要的是review_version字段同一份周报允许多次审核比如学生修改后重新提交每次审核都会递增版本号老版本记录仍保留这种设计解决了高校管理中的经典矛盾教务处要“看到教师是否认真审核”而教师怕“随便写个‘已阅’被追责”。现在教师点“通过”按钮时系统强制弹出对话框【审核确认】 您正在审核 [张三] 的第3周周报2024-09-02至2024-09-08 请填写不少于10字的审核意见支持插入图片 □ 同意通过生成正式评语PDF □ 要求修改退回学生端只有勾选“同意通过”且输入框字数≥10按钮才可点击。生成的PDF评语包含学生基本信息、周报原文截取关键段落、教师评语、审核时间戳、电子签名教师姓名工号水印。这个PDF会自动存入/opt/weekly/pdfs/2024/09/目录并在数据库记录pdf_path字段。我在部署时发现一个细节new_weekly_teacher的package.json里quill: ^1.3.7版本太老导致插入图片时base64编码过长Nginx默认client_max_body_size1MB会拦截。解决方案是升级Quill到^2.0.0并在vue.config.js里配置module.exports { configureWebpack: { module: { rules: [ { test: /\.js$/, include: /node_modules\/quill/, use: { loader: babel-loader, options: { presets: [babel/preset-env] } } } ] } } }注意教师端的“待审列表”性能至关重要。WeeklyReviewService.getPendingReviews()方法用了MyBatis二级缓存但缓存key是pending:teacherId:pageNo避免全量刷新。我在测试时故意让100个学生同时提交发现第一页加载只要213ms但第10页要1.8s——原因是LIMIT 10 OFFSET 90导致MySQL全表扫描。最终优化方案是在weekly_report表上加复合索引sql ALTER TABLE weekly_report ADD INDEX idx_teacher_status_time (teacher_id, status, submit_time);这个索引让分页查询速度稳定在150ms内。2.3 管理员端weekly_manage超越CRUD的治理能力设计管理员端最容易被当成“后台管理系统”但本系统把它定位为教学质量管理中枢。weekly_manage模块里没有UserManageController这种常规接口而是三个核心服务ReportAnalyticsService按周/月/学期维度统计周报完成率、平均审核时长、退回率TOP10导师DataExportService导出符合教育部《研究生培养过程管理规范》要求的Excel含隐藏列存储原始JSON数据供审计SystemMonitorService实时监控数据库连接池、Redis内存、周报提交QPS异常时自动邮件告警看一个典型场景学期末教务处要“抽查20%的周报原始记录”。传统系统只能手动导出再随机筛选而本系统在DataExportService.exportRandomSample()里实现了分层随机算法public ListWeeklyReport exportRandomSample(int sampleRate) { // 按导师分组每组抽取sampleRate%的样本保证覆盖所有导师 MapLong, ListWeeklyReport reportsByTeacher reportMapper.selectByPeriod(startDate, endDate).stream() .collect(Collectors.groupingBy(WeeklyReport::getTeacherId)); ListWeeklyReport samples new ArrayList(); for (ListWeeklyReport reports : reportsByTeacher.values()) { int count Math.max(1, (int) Math.ceil(reports.size() * sampleRate / 100.0)); Collections.shuffle(reports); // 打乱顺序 samples.addAll(reports.subList(0, Math.min(count, reports.size()))); } return samples; }这个算法确保抽查结果既有随机性又避免出现“某导师的周报全被抽中而另一导师完全没覆盖”的偏差——这是教务检查时最看重的公平性。另一个亮点是SystemMonitorService的告警机制。它不依赖第三方APM而是用Spring Boot Actuator的/actuator/metrics端点定时采集Scheduled(fixedRate 60000) // 每分钟执行 public void checkSystemHealth() { // 检查数据库连接池使用率 double activeRatio jdbcTemplate.queryForObject( SELECT (active_connections*100.0)/max_connections FROM pg_stat_database WHERE datnamecurrent_database(), Double.class); if (activeRatio 90.0) { sendAlertEmail(数据库连接池使用率过高 activeRatio %); } }实操心得管理员端的菜单权限不是硬编码在router/index.js里而是从后端/api/admin/menu动态加载。返回的JSON结构包含icon字段如el-icon-s-data前端用i :classitem.icon/i渲染。这意味着你新增一个菜单项只需在数据库sys_menu表里插入一行不用改任何前端代码。我在部署时给学院加了个“培养质量分析”菜单只花了3分钟INSERT一条记录然后在weekly_manage/src/views/quality/Analysis.vue里写新页面完全不影响现有功能。3. 全链路部署与调试实战指南3.1 本地开发环境一键启动mvnw脚本的隐藏技巧资源包里的mvnw不是简单的Maven包装器而是经过定制的环境感知启动器。它会在执行前自动检测是否存在./local-dev-config.yml本地开发专用配置当前Java版本是否≥8通过java -version | grep 1.8\|11\|17Node.js是否已安装node -v如果你直接./mvnw spring-boot:run它会按顺序尝试1. 加载src/main/resources/application-dev.yml2. 如果存在local-dev-config.yml则合并覆盖其中的spring.datasource.url3. 启动时自动创建H2内存数据库仅dev profile避免你还要装MySQL但真正强大的是它的模块化启动能力。比如你想只启动教师端API可以# 进入weekly-teacher-api目录 cd weekly-teacher-api ./mvnw spring-boot:run -Dspring-boot.run.profilesteacher这个teacherprofile会激活application-teacher.yml里面配置了-server.port8082-spring.redis.database2与学生端的db1隔离-logging.level.com.weekly.teacherDEBUG我在调试时常用这个技巧当学生端提交失败我先停掉所有服务只启动weekly-student-api然后用Postman模拟请求快速定位是前端传参问题还是后端校验逻辑问题。提示mvnw脚本里有一行被注释掉的代码bashexport MAVEN_OPTS”-Xmx2g -XX:MaxMetaspaceSize512m” 这是留给IDEA用户的。如果你在IDEA里导入项目后启动慢取消注释这行然后在Run Configuration的VM Options里粘贴同样内容能提升30%启动速度。3.2 生产环境部署NginxMySQLRedis三件套最佳实践生产部署不是简单复制weekly.sql而是要建立符合高校IT基础设施现状的适配方案。我部署过的6所高校MySQL版本从5.7到8.0.33都有本系统做了兼容处理weekly.sql里所有CREATE TABLE语句都指定ENGINEInnoDB DEFAULT CHARSETutf8mb4避免使用MySQL 8.0的JSON类型字段如review_comment用TEXT代替保证5.7兼容weekly_manage的定时任务用Scheduled而非Quartz减少依赖Nginx配置的关键在于静态资源分离与API代理# /etc/nginx/conf.d/weekly.conf upstream student_api { server 127.0.0.1:8081; } upstream teacher_api { server 127.0.0.1:8082; } upstream admin_api { server 127.0.0.1:8083; } server { listen 80; server_name weekly.your-university.edu.cn; # 静态资源直接由Nginx服务 location /student/ { alias /var/www/weekly/dist/student/; try_files $uri $uri/ /student/index.html; } location /teacher/ { alias /var/www/weekly/dist/teacher/; try_files $uri $uri/ /teacher/index.html; } location /manage/ { alias /var/www/weekly/dist/manage/; try_files $uri $uri/ /manage/index.html; } # API代理带身份验证头透传 location /api/student/ { proxy_pass http://student_api/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header Authorization $http_authorization; # 关键透传JWT } location /api/teacher/ { proxy_pass http://teacher_api/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header Authorization $http_authorization; } location /api/admin/ { proxy_pass http://admin_api/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header Authorization $http_authorization; } }这个配置解决了两个痛点- 静态资源不走Java应用降低Tomcat压力-Authorization头透传保证JWT鉴权链路完整很多新手会漏掉这一行导致登录后所有API返回401Redis部署建议用单机模式持久化因为本系统只用Redis存- JWT黑名单blacklist:{token}TTL7天- 周报提交频次限制rate-limit:student:{id}每小时最多5次- 待审列表缓存pending-reviews:{teacherId}TTL30分钟不需要集群redis.conf只需改两行save 900 1 save 300 10 appendonly yes注意MySQL字符集必须设为utf8mb4否则学生提交的emoji表情比如“✅已完成”会变成?。在/etc/mysql/my.cnf里添加ini[client]default-character-set utf8mb4[mysql]default-character-set utf8mb4[mysqld]character-set-server utf8mb4collation-server utf8mb4_unicode_ci3.3 数据库初始化与初始数据注入weekly.sql的执行顺序玄机weekly.sql不是一次性执行的纯建表脚本而是按依赖顺序分块编写。我见过太多人直接mysql -u root -p weekly.sql导致外键约束失败。正确顺序是# 1. 创建数据库注意字符集 mysql -u root -p -e CREATE DATABASE IF NOT EXISTS weekly DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 2. 执行基础表无外键依赖 mysql -u root -p weekly weekly-base.sql # 包含user、role、department表 # 3. 执行核心表依赖基础表 mysql -u root -p weekly weekly-core.sql # 包含weekly_report、user_role_binding # 4. 执行业务表依赖核心表 mysql -u root -p weekly weekly-business.sql # 包含weekly_review_log、system_config # 5. 插入初始数据必须最后执行 mysql -u root -p weekly weekly-init-data.sqlweekly-init-data.sql里的初始账号密码是明文123456但系统启动后会自动加密。你可以在WeeklyApplication.java的PostConstruct方法里看到PostConstruct public void initAdminUser() { User admin userMapper.selectByUsername(admin); if (admin ! null 123456.equals(admin.getPassword())) { admin.setPassword(passwordEncoder.encode(123456)); userMapper.updateById(admin); log.info(Admin password auto-encrypted); } }这个设计让你首次启动就能用admin/123456登录又保证密码不会以明文形式长期存在数据库里。实操心得weekly.sql里有个容易被忽略的细节——weekly_report表的submit_time字段用的是DATETIME而非TIMESTAMP。这是因为高校要求“周报提交时间必须是学生本地时间”而TIMESTAMP会自动转为UTC再存导致时区混乱。我在某所位于新疆的高校部署时发现学生提交时间比实际晚2小时就是因为MySQL服务器时区设为00:00。解决方案是在application.yml里加yaml spring: datasource: url: jdbc:mysql://localhost:3306/weekly?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8mb44. 常见问题与排查技巧实录4.1 登录成功但页面空白Vue路由与后端API路径的隐性冲突现象学生输入账号密码/api/student/login返回200且有token但跳转到/student/后页面一片空白控制台报错Failed to load resource: the server responded with a status of 404 ()。原因分析这是Nginx配置里location /student/的斜杠结尾引发的路径解析问题。Vue Router用的是history模式/student/作为base URL但Nginx的alias指令要求路径严格匹配。当浏览器请求/student/js/app.js时Nginx会去找/var/www/weekly/dist/student/js/app.js但如果dist/student/目录下实际是/var/www/weekly/dist/student/js/app.abc123.jswebpack加了hash而index.html里引用的是script src/js/app.js就会404。解决方案分三步1. 在new_weekly_student/vue.config.js里配置javascript module.exports { publicPath: /student/, outputDir: dist/student, assetsDir: static }2. 确保dist/student/index.html里的script标签路径正确html3. Nginx配置里location /student/的alias必须指向dist/student/的父目录nginxlocation /student/ {alias /var/www/weekly/dist/; # 注意这里不是 /dist/student/try_files $uri $uri/ /student/index.html;}排查技巧打开浏览器开发者工具Network标签页里过滤js看404的请求URL是什么。如果是/student/static/js/app.js说明前端配置没问题如果是/js/app.js说明publicPath没生效。4.2 教师审核后学生收不到通知邮件服务配置的致命细节现象教师点“通过”数据库weekly_review_log里记录正常但学生没收到邮件提醒。原因weekly-teacher-api的邮件发送逻辑在ReviewNotificationService.java里它依赖spring.mail.*配置但资源包里的application.yml默认是注释状态# spring: # mail: # host: smtp.exmail.qq.com # port: 465 # username: notifyyour-university.edu.cn # password: your-app-password # properties: # mail: # smtp: # auth: true # ssl: # enable: true这里有两个坑-password不是邮箱登录密码而是QQ邮箱的“SMTP专用密码”在邮箱设置里开启POP3/SMTP服务后生成-port: 465必须配ssl.enable: true如果用port: 587则要配starttls.enable: true我在某高校部署时管理员填了邮箱密码结果一直报Authentication failed。后来发现QQ邮箱的SMTP专用密码是16位随机字符串不是登录密码。快速验证法在服务器上用telnet测试SMTP连通性bashtelnet smtp.exmail.qq.com 465如果连接成功说明网络和端口没问题然后用openssl测试SSLopenssl s_client -connect smtp.exmail.qq.com:465 -crlf4.3 管理员导出Excel乱码POI版本与字体的兼容性陷阱现象weekly_manage导出的Excel打开后中文全是方框。原因Apache POI 4.1.2默认用Arial字体而Linux服务器没装中文字体。解决方案不是装字体而是改代码// 在ExportExcelService.java里 Workbook workbook new XSSFWorkbook(); Font font workbook.createFont(); font.setFontName(SimSun); // 指定宋体 font.setBold(true); font.setFontHeightInPoints((short) 12); CellStyle style workbook.createCellStyle(); style.setFont(font); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER);但SimSun在CentOS上可能不存在所以更稳妥的做法是用Noto Sans CJK SCGoogle开源字体下载后放在/usr/share/fonts/然后执行sudo mkfontscale sudo mkfontdir sudo fc-cache -fv终极方案不用字体用POI的setFontHeightInPoints配合setRotationjava font.setFontHeightInPoints((short) 10); font.setRotation((short) 90); // 旋转文字避免字体缺失4.4 周报周期计算错误时区与夏令时的双重陷阱现象manualType.properties设了weekly.cycle7但系统显示“第3周”从周一到周日而教务处要求“第3周”从周三到下周二。原因DateUtils.calculateCurrentWeek()方法用的是Calendar.getInstance()它依赖JVM时区。如果服务器时区是UTC而学校在Asia/Shanghai就会差8小时。解决方案在WeeklyConfig.java里强制指定时区Component ConfigurationProperties(prefix weekly) public class WeeklyConfig { private int cycle; private String timeZone Asia/Shanghai; // 新增配置 public LocalDateTime getCurrentWeekStart() { LocalDateTime now LocalDateTime.now(ZoneId.of(timeZone)); DayOfWeek firstDay DayOfWeek.MONDAY; LocalDateTime start now.with(firstDay); return start.minusDays((now.getDayOfWeek().getValue() - firstDay.getValue() 7) % 7); } }然后在manualType.properties里加weekly.time-zoneAsia/Shanghai排查技巧在WeeklyApplication.java里加启动日志java PostConstruct public void logTimeZone() { log.info(JVM Time Zone: {}, TimeZone.getDefault().getID()); log.info(System Time Zone: {}, System.getProperty(user.timezone)); }如果两行输出不同说明JVM时区没生效。5. 系统扩展与二次开发指南5.1 新增“导师组”功能如何最小改动接入现有架构某学院提出需求“一个学生可能有校内导师企业导师周报需分别提交给两人”。这看似要大改其实只需三步在user_role_binding表加binding_type字段1-校内导师2-企业导师修改weekly-student-api的StudentWeeklyController.submit()增加teacherIds数组参数在weekly-teacher-api的ReviewWorkflowEngine里对binding_type2的审核记录加isEnterprisetrue标识关键点在于所有改动都不影响现有接口兼容性。原/api/student/submit接口保持不变新增/api/student/submit-to-multiple接口供前端调用。这样老版本学生端继续用旧接口新版本用新接口平滑过渡。5.2 对接学校统一身份认证CAS/LDAP集成要点如果学校已有CAS系统只需改两处- 前端new_weekly_student/src/utils/auth.js里把login()方法替换为CAS跳转javascript login() { window.location.href https://cas.your-university.edu.cn/login?service encodeURIComponent(https://weekly.your-university.edu.cn/student/); }- 后端weekly-core的CasAuthenticationFilter.java里解析CAS回调的ticket调用casClient.validateTicket()获取用户属性然后查user表匹配cas_uid字段。注意CAS集成后manualType.properties里的weekly.auth.modecas要设为cas触发CasAuthConfig.java的自动配置。5.3 移动端适配PWA离线能力增强方案Vue 2.x默认不支持PWA但可以加vue-cli-plugin-pwacd new_weekly_student vue add pwa然后在vue.config.js里配置module.exports { pwa: { workboxOptions: { skipWaiting: true, clientsClaim: true, runtimeCaching: [ { urlPattern: /^https:\/\/.*weekly\.your-university\.edu\.cn\/api\//, handler: StaleWhileRevalidate } ] } } }这样学生在地铁里断网也能看到上周周报网络恢复后自动同步新数据。我在实际部署中发现这套系统最珍贵的不是代码本身而是它背后体现的教育信息化落地思维不追求技术炫酷而专注解决真实场景中的确定性问题不堆砌架构术语而用可验证的工程细节说话。当你把weekly.sql导入数据库、mvnw跑起来、看到第一个学生提交的周报出现在教师端待审列表时那种“系统真的活了”的踏实感远胜过任何技术文档里的华丽描述。本文还有配套的精品资源点击获取简介这个资源包提供一套完整的研究生周报管理系统前端用Vue 2.x开发后端基于SpringBoot 2.x整合MyBatis操作MySQL数据库配套weekly.sql建表脚本和初始数据。系统划分为三个独立模块new_weekly_student学生提交周报、new_weekly_teacher教师审阅反馈、weekly_manage管理员后台管理每个模块都包含完整src代码、public静态资源和可运行结构。附带README.md详细启动说明、系统.txt功能清单、item.pdf系统设计文档、manualType.properties配置文件以及mvnw轻量级Maven包装脚本支持在IDEA或VS Code中一键导入、本地快速启动与调试。所有模块均适配标准Java和Node.js开发环境无需额外复杂配置适合高校课程设计、毕业设计实践或教学演示直接使用。本文还有配套的精品资源点击获取