
1. 为什么日报管理系统值得做以及题目的真正考察点每年毕业季都会有一大批Java方向的应届生围着毕设题目发愁其中基于Spring Boot的日报管理系统绝对算得上一个高频题目。我这个月刚好完整跟完了一个学弟的这个项目从选题、搭环境、写代码到部署上服务器、录演示视频全程没有落下所以想把整个实战过程拆开来聊聊。先直接说结论这个题目能从那么多选题里脱颖而出不是因为它花哨恰恰是因为它覆盖面足够广、难度足够可控、演示效果足够直观。它几乎把Java Web开发的主干知识点全部串了一遍后端用Spring Boot搭建RESTful API涉及Controller、Service、Mapper三层划分数据层用MyBatis Plus或者JPA操作MySQL涉及多表关联查询权限控制要区分普通员工、部门负责人、管理员三类角色涉及拦截器或Spring Security前端可以是Vue Element UI的分离式也可以直接用Thymeleaf模板两者做起来的体感完全不同。但同时这个题目的业务逻辑又不算复杂核心就是员工提交日报→负责人审批→统计汇总不需要电商系统那样复杂的订单状态机也没有支付、消息队列这类的重型组件。对毕设来说这是一个刚刚好的复杂度不会简单到显得没工作量也不至于复杂到三个月写不完。还有一个比较隐蔽的考察点就是日报这个词本身自带的时间维度。日报不是一次性提交就完了它牵扯到按日、按周、按月查询牵扯到补交、修改、审批状态的流转这些都是在需求分析阶段就要想清楚的东西。很多同学拿到题目就开写结果写到请假补交功能时发现表结构设计根本支撑不了只能回头改数据库非常痛苦。所以我这篇博文的核心思路是沿着一个能跑通全流程的毕设项目从需求拆解、表设计、核心代码、部署上线到答辩准备一条线走完。你可以把它当做一个参考实现也可以直接在此基础上做二次开发换成自己项目里需要的业务逻辑。2. 开工前的需求解剖日报系统到底需要哪些功能模块很多同学的第一个误区是拿到题目后直接打开IDEA开始建项目。但日报系统的需求虽然简单没有梳理清楚一样会翻车。我建议花半天左右的时间把角色、功能、状态流转画清楚。2.1 三类角色的权限边界日报管理系统几乎必然涉及三种角色这是这种内部管理系统的通病普通员工——填写自己的日报查看和修改自己的历史日报接收主管的审批反馈部门主管/负责人——查看本部门下属的日报进行审批通过或驳回查看本部门的日报统计系统管理员——管理用户账号、重置密码、维护部门结构查看全公司范围的统计报表。这里有一个设计上的隐藏考点权限控制是写死在代码里还是在数据库里做成角色的权限表。毕设答辩时老师非常喜欢问这个问题做死在代码里比如简单的if判断角色类型省事儿但不好看做成RBAC基于角色的访问控制模型又太复杂。日报系统这个规模其实用一张角色表 拦截器判断就够了只要把逻辑写得清晰答辩时能自圆其说就可以。2.2 日报的状态流转比想象中复杂一点日报从创建到归档中间经历的状态并不像提交/审批两个字看起来那么简单。一个相对完整的流程是这样的员工保存草稿暂存状态员工提交日报待审批状态主管审批通过已通过状态主管审批驳回已驳回状态此时员工可以修改后重新提交系统在每月末或每周日对已审批的日报进行归档生成月度汇总。细心的同学会发现这里还缺一个东西补交。如果员工某天忘了写日报第二天能不能补如果允许补交那么日期的唯一性校验每天只能提交一条日报就不能简单用当天日期来判断。我当时用的是一个比较灵活的策略日报表里存一个 business_date业务日期前端默认选中当前日期但允许修改为之前7天之内的日期提交时校验该员工在该业务日期下是否已有正式提交的日报。这样既解决了补交问题又不破坏每天一条的数据约束。2.3 统计报表的几种形态日报系统如果没有统计功能会显得项目完整度不够。毕设阶段不需要做得很重但需要做到够看。我当时实现了三种统计个人维度某个员工在指定日期范围内提交了多少条日报、通过率多少部门维度某个部门在某个月内的日报提交率、按时提交率、平均字数全公司维度一个简单的管理看板展示最近7天全公司日报提交数量的趋势图。这里有一个现实问题很多人做统计图喜欢用ECharts但ECharts是纯前端的图表库数据接口得你自己提供。我的做法是后端计算好统计数据返回JSON给前端前端用ECharts渲染折线图和柱状图。这样分工明确答辩时也能说清楚哪些是后端算的哪些是前端画的。2.4 功能清单速查表为了方便大家对照检查我整理了一份功能清单基本覆盖了毕设评分的各个维度功能模块核心功能点说明用户管理登录、注册或管理员创建、修改密码一般还需要个人资料编辑部门管理部门增删改查、员工部门分配需要预留部门负责人字段日报填报新建日报、保存草稿、提交日报、修改日报正文建议支持富文本或Markdown日报审批待审批列表、通过、驳回、驳回理由主管只能看到本部门日报查询按日期/关键词/状态筛选个人查自己的管理员查全部统计看板个人提交率、部门出勤率、活跃趋势图可以用ECharts画系统管理用户禁用/启用、重置密码、系统日志加分项量力而行这里想提醒一句不要试图做太多功能。毕设的评分重点之一是功能的完整性和稳定性而不是功能数量。一个只能看不能用的十个功能模块远不如五六个能流畅跑通的核心模块。日报管理系统的核心斩钉截铁就是填日报-审批-统计这三板斧其他都是加分项。3. 技术选型和项目搭建Spring Boot怎么配才能少走弯路技术选型这一步很多人以为无脑上Spring Boot最新版就行实际上这里是最容易埋坑的地方。因为我用的JDK版本、Spring Boot版本、MyBatis Plus版本之间如果相互不兼容报错能让人折腾一晚上。3.1 一套稳定可复现的开发环境组合我根据这个项目的实际需求对比了几个版本组合的稳定性最终推荐这套配置组件版本选择理由JDK1.8 或 11大多数学校机房和服务器默认支持的版本兼容性最好Spring Boot2.7.x稳定且资料多主流教程覆盖最全3.x虽然新但部分老代码不兼容MyBatis Plus3.5.x内置CRUD方法省去大量重复Mapper代码MySQL5.7 或 8.05.7兼容性好8.0功能新按老师要求选Maven3.8.x依赖管理必须用不要手动下jar包前端方案Vue 2 Element UI 或 Thymeleaf前后端分离加分模板引擎省事讲一个特别容易踩的坑Spring Boot 3.x直接使用javax.servlet包会报错因为已经从javax迁移到了jakarta。很多教程还是基于javax写的拦截器、过滤器代码如果你用的是3.x版本复制过来直接编译失败。毕设阶段没必要追新用2.7.x JDK 8是稳的。还有一个常见问题是Lombok依赖。日报系统的实体类字段不少如果手写getter/setter和构造器每天多写几百行毫无意义。Lombok的Data、Builder注解能极大压缩代码量。但Lombok和JDK版本之间有兼容性关系如果编译报错java: cannot find symbol大概率是Lombok版本和JDK不匹配。3.2 项目结构按包分层的标准姿势创建Spring Boot项目时包结构的规范性也是答辩的考察点。我个人推荐这样分com.example.dailyreport ├── DailyReportApplication.java // 启动类 ├── common/ // 公共模块统一返回结果、全局异常处理 │ ├── Result.java │ └── GlobalExceptionHandler.java ├── config/ // 配置类拦截器、跨域配置、MyBatisPlus配置 ├── controller/ // 控制层接收前端请求 │ ├── UserController.java │ ├── DailyReportController.java │ ├── ApprovalController.java │ └── StatisticsController.java ├── service/ // 业务层核心业务逻辑 │ ├── UserService.java │ ├── DailyReportService.java │ └── StatisticsService.java ├── mapper/ // 持久层数据库操作接口 ├── entity/ // 实体类对应数据库表 ├── dto/ // 数据传输对象接收前端参数 ├── vo/ // 视图对象返回给前端的封装数据 └── interceptor/ // 拦截器登录校验、权限校验这个结构是Java Web开发的经典分层优点非常明显每层职责清晰哪一层出了问题都能快速定位。比如哪天发现日报提交后状态不对你第一反应去Service层找逻辑而不是在Controller里满世界搜setStatus()。3.3 Maven依赖的精简原则不要一次性把所有依赖都加上用到一个加一个。日报管理系统核心依赖其实就这几个dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdcom.auth0/groupId artifactIdjava-jwt/artifactId version3.19.2/version /dependency这里额外解释一下JWT的引入。日报系统的登录方案最简单的是用Session但如果是前后端分离项目Session跨域处理比较麻烦。我用了JWTJSON Web Token做无状态登录用户登录成功后后端生成一个token返回给前端前端每次请求在Header里带上token后端用一个拦截器统一解析。这样既做了权限控制又避免了Session共享问题答辩时还能顺带讲一段无状态认证与JWT原理是一个非常划算的技术亮点。4. 数据库表结构设计的实践复盘这一部分我要重点讲因为日报系统的所有功能都建立在表结构之上表设计好坏直接决定后面代码写起来顺不顺畅。很多同学在这个环节只建了两三张表后面越写越别扭主要原因就是没提前想清楚哪些信息需要在不同角色之间共享。4.1 核心五张表的职责划分我最终使用的是五张核心表覆盖了日报系统的全部业务场景-- 1. 用户表 CREATE TABLE sys_user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录账号, password varchar(100) NOT NULL COMMENT 密码BCrypt加密, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名, role tinyint(4) NOT NULL DEFAULT 1 COMMENT 1-员工 2-主管 3-管理员, department_id bigint(20) DEFAULT NULL COMMENT 所属部门, status tinyint(4) DEFAULT 1 COMMENT 1-正常 0-禁用, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 2. 部门表 CREATE TABLE sys_department ( id bigint(20) NOT NULL AUTO_INCREMENT, dept_name varchar(100) NOT NULL COMMENT 部门名称, leader_id bigint(20) DEFAULT NULL COMMENT 部门负责人用户ID, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 3. 日报主表 CREATE TABLE daily_report ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL COMMENT 提交人ID, business_date date NOT NULL COMMENT 业务日期工作内容对应的日期, title varchar(100) DEFAULT NULL COMMENT 日报标题, content text COMMENT 日报正文, work_type tinyint(4) DEFAULT 1 COMMENT 工作类型1-日常工作 2-项目开发 3-会议培训 4-其他, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 0-草稿 1-待审批 2-已通过 3-已驳回, reject_reason varchar(255) DEFAULT NULL COMMENT 驳回理由, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_date (user_id, business_date), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 4. 审批记录表 CREATE TABLE approval_record ( id bigint(20) NOT NULL AUTO_INCREMENT, report_id bigint(20) NOT NULL COMMENT 日报ID, approver_id bigint(20) NOT NULL COMMENT 审批人ID, action tinyint(4) NOT NULL COMMENT 1-通过 2-驳回, comment varchar(255) DEFAULT NULL COMMENT 审批意见, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 5. 系统操作日志表 CREATE TABLE sys_log ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) DEFAULT NULL, operation varchar(100) DEFAULT NULL, method varchar(200) DEFAULT NULL, params text, ip varchar(50) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;4.2 为什么把审批记录单独拆成一张表很多简化版的日报系统会把审批结果直接写成日报表里的两个字段审批人和审批意见看起来省了一张表。但这种设计有个问题如果审批人想多次操作呢如果一位日报被驳回后重新提交又通过历史审批记录就丢了。拆出独立的审批记录表之后好处非常明显完整保存了每条日报的全生命周期审批轨迹日报表只需要保留当前状态status字段不需要冗余存历史审批意见答辩时你可以说这是符合数据库第三范式的冗余消除设计。还有一个细节审批表里的comment字段其实可以在日报主表里冗余一份。实际开发中我确实在日报表也存了一份reject_reason目的是让员工端查询我的日报为什么被驳回时少一次连表查询。这就是典型的以空间换时间的冗余设计在答辩时是一个可以主动讲出来的亮点。4.3 日期字段设计的一个小坑日报表的business_date是date类型而不是datetime类型。这看起来是个小事实际影响很大。如果你用datetime存业务日期查询某一天的日报时要么用DATE(business_date)函数包一层导致索引失效要么用between 2024-05-20 00:00:00 and 2024-05-20 23:59:59这种写法。我见过太多人在这里翻车所以强烈建议直接用date类型。实体类里对应的是LocalDate而非LocalDateTime前端传参数时用yyyy-MM-dd格式即可MyBatis Plus会自动完成转换省掉很多解析上的麻烦。另外日报的唯一性约束建议加在(user_id, business_date)联合字段上。注意这里的前提是同一天只能有一条正式提交或草稿的日报那么草稿和正式提交应该怎么处理我的处理方式是不区分草稿和提交的唯一性无论什么状态同一个用户同一个业务日期只能有一条记录。草稿本质上就是status0的记录修改时直接update而不是insert。如果你允许一个用户同一天存草稿再正式提交多条记录表结构上会非常混乱。5. 核心业务代码的实现思路与关键代码解析表结构有了接下来就是代码实现。这一章我不会贴全部代码那样太冗长只挑三个最核心、也是答辩必问的部分拆开讲登录认证、日报提交/审批的状态流转、统计查询。5.1 基于JWT的登录认证登录流程是这样的用户提交用户名密码后端校验成功后返回token。这个token里封装了用户ID和角色信息前端存储在localStorage里之后每次请求在Header里带Authorization: Bearer token。Service public class UserService { Autowired private SysUserMapper userMapper; public String login(String username, String password) { LambdaQueryWrapperSysUser wrapper new LambdaQueryWrapper(); wrapper.eq(SysUser::getUsername, username); SysUser user userMapper.selectOne(wrapper); if (user null) { throw new BusinessException(用户不存在); } // BCrypt密码校验 if (!BCrypt.checkpw(password, user.getPassword())) { throw new BusinessException(密码错误); } if (user.getStatus() 0) { throw new BusinessException(账号已被禁用请联系管理员); } // 生成JWT有效期24小时 String token JWT.create() .withClaim(userId, user.getId()) .withClaim(role, user.getRole()) .withClaim(deptId, user.getDepartmentId()) .withExpiresAt(new Date(System.currentTimeMillis() 86400000)) .sign(Algorithm.HMAC256(daily-report-secret-key)); return token; } }对应的拦截器核心逻辑Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口 String uri request.getRequestURI(); if (uri.contains(/api/user/login)) { return true; } String authHeader request.getHeader(Authorization); if (authHeader null || !authHeader.startsWith(Bearer )) { throw new BusinessException(401, 未登录或登录已过期); } String token authHeader.substring(7); try { DecodedJWT jwt JWT.require(Algorithm.HMAC256(daily-report-secret-key)).build().verify(token); // 把用户信息存入ThreadLocal上下文 UserContext.set(jwt.getClaim(userId).asLong(), jwt.getClaim(role).asInt(), jwt.getClaim(deptId).asLong()); return true; } catch (JWTVerificationException e) { throw new BusinessException(401, token无效或已过期); } } }这里有个经验值得分享密钥不要硬编码在代码里放到application.yml里。虽然毕设项目规模小但写代码的习惯要正确jwt: secret: your-own-random-secret-key-here expire-hours: 24从拦截器获取当前登录用户的ID是整个权限控制的基础。后面的日报提交、审批操作全部通过UserContext.get()拿到当前操作人而不是相信前端传来的userId参数。这个设计能防止越权操作答辩时也是一个加分的细节。5.2 日报提交时的状态流转日报提交的核心逻辑很直接但有两个边界情况必须处理重复提交校验和非本人操作校验。public DailyReport submitReport(DailyReportSubmitDTO dto, Long currentUserId) { // 1. 校验业务日期的合法性不能是未来的日期 if (dto.getBusinessDate().isAfter(LocalDate.now())) { throw new BusinessException(业务日期不能晚于今天); } // 2. 查询是否已有该日期的日报 LambdaQueryWrapperDailyReport wrapper new LambdaQueryWrapper(); wrapper.eq(DailyReport::getUserId, currentUserId) .eq(DailyReport::getBusinessDate, dto.getBusinessDate()); DailyReport existing dailyReportMapper.selectOne(wrapper); if (existing null) { // 新建提交 DailyReport report new DailyReport(); report.setUserId(currentUserId); report.setBusinessDate(dto.getBusinessDate()); report.setTitle(dto.getTitle()); report.setContent(dto.getContent()); report.setWorkType(dto.getWorkType()); report.setStatus(1); // 待审批 dailyReportMapper.insert(report); return report; } else { // 草稿更新或驳回后重新提交 if (existing.getStatus() 0 || existing.getStatus() 3) { existing.setTitle(dto.getTitle()); existing.setContent(dto.getContent()); existing.setWorkType(dto.getWorkType()); existing.setStatus(1); existing.setRejectReason(null); dailyReportMapper.updateById(existing); return existing; } else { throw new BusinessException(该日期的日报已提交无法重复提交); } } }这段代码最核心的思路是用status字段驱动流程而不是用多张表模拟状态。草稿改提交、驳回改重提本质都是同一张表只是status值变了。这种设计大大简化了代码逻辑也是日报系统这类CMS系统的常规做法。5.3 审批环节的权限控制主管审批日报的前提是日报的提交人必须是当前主管所在部门的员工。所以审批接口的第一步不是查日报而是查提交人所属部门public void approveReport(Long reportId, Long approverId) { // 1. 查日报 DailyReport report dailyReportMapper.selectById(reportId); if (report null) { throw new BusinessException(日报不存在); } // 2. 查提交人信息 SysUser submitter userMapper.selectById(report.getUserId()); if (submitter null) { throw new BusinessException(提交人不存在); } // 3. 查审批人信息 SysUser approver userMapper.selectById(approverId); if (approver null || approver.getRole() ! 2) { throw new BusinessException(当前用户没有审批权限); } // 4. 部门校验审批人必须是报告提交人的直属主管 if (!approver.getDepartmentId().equals(submitter.getDepartmentId())) { throw new BusinessException(只能审批本部门员工的日报); } // 5. 状态校验 if (report.getStatus() ! 1) { throw new BusinessException(当前日报不在待审批状态); } // 6. 更新状态 report.setStatus(2); dailyReportMapper.updateById(report); // 7. 写审批记录 ApprovalRecord record new ApprovalRecord(); record.setReportId(reportId); record.setApproverId(approverId); record.setAction(1); record.setComment(同意); approvalRecordMapper.insert(record); }有没有发现审批的逻辑是层层校验、最后落笔。先确认人对不对再确认单子对不对最后才改状态。实际企业开发里这种先校验后操作的模式是最稳妥的哪怕将来功能多了也不容易改出问题。5.4 统计查询MyBatis Plus的聚合能力统计模块我用的是MyBatis Plus的QueryWrapper加select统计函数没有写复杂的原生SQLpublic ListMapString, Object getDailyTrend(Integer days, Long userId) { LocalDate startDate LocalDate.now().minusDays(days - 1); LambdaQueryWrapperDailyReport wrapper new LambdaQueryWrapper(); wrapper.select( DailyReport::getBusinessDate, ApplicationField.as(COUNT(*), count) ) .eq(userId ! null, DailyReport::getUserId, userId) .ge(DailyReport::getBusinessDate, startDate) .le(DailyReport::getBusinessDate, LocalDate.now()) .groupBy(DailyReport::getBusinessDate); return dailyReportMapper.selectMaps(wrapper); }如果遇到group by和按天统计的需求MySQL的DATE_FORMAT函数也很重要。比如按月统计每天的提交条数SELECT DATE_FORMAT(business_date, %Y-%m-%d) AS day, COUNT(*) AS cnt FROM daily_report WHERE status 2 GROUP BY DATE_FORMAT(business_date, %Y-%m-%d) ORDER BY day;这里想单独提醒一句*不要用select去取统计数据的字段数据量大了性能很差。MyBatis Plus里用select指定列或者在SQL层面明确字段列表这是写业务代码的良好习惯也是毕设代码评分时会看的一个细节。6. 部署上线的完整流程从本地到服务器代码写完了本地跑通了接下来是部署环节。这是很多同学的短板但也是演示视频里最加分的地方——老师要看到你的系统不只是本地能跑而是可以在服务器上被其他人通过浏览器访问。6.1 本地打包的两种方式日报管理系统打jar包只需要一行命令mvn clean package -DskipTests执行完了在target目录下会生成一个daily-report-0.0.1-SNAPSHOT.jar。这里有两个注意点-DskipTests必须加。如果你的项目里写了测试类但配置不对打包时会跑去跑测试不仅慢还可能失败。如果用到外部配置文件或者application.yml里配了服务器地址注意打包时要确认配置文件打进jar包里了。本地验证打包是否成功java -jar target/daily-report-0.0.1-SNAPSHOT.jar看到类似Started DailyReportApplication in X.XX seconds的日志就说明启动成功然后浏览器访问http://localhost:8080即可。6.2 服务器部署的完整步骤服务器我用的是CentOS 7系统部署步骤大致如下# 1. 安装JDK如果服务器没装 yum install -y java-1.8.0-openjdk # 2. 上传jar包到服务器比如 /opt/daily-report/ mkdir -p /opt/daily-report # 用scp或xftp上传jar包到该目录 # 3. 安装MySQL如果服务器没装 # 参考官方文档安装即可注意修改root密码 # 4. 创建数据库和数据表 mysql -uroot -p CREATE DATABASE daily_report DEFAULT CHARACTER SET utf8mb4; USE daily_report; source /opt/daily-report/schema.sql; # 5. 导入初始数据比如管理员账号 source /opt/daily-report/init_data.sql; # 6. 启动项目首次建议前台启动方便看日志 cd /opt/daily-report java -jar daily-report-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod看到启动成功后可以CtrlC退出然后用nohup方式后台运行nohup java -jar daily-report-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod app.log 21 这里强烈建议在项目的resources目录下维护多套环境的配置文件# application-dev.yml本地开发 server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/daily_report?useUnicodetruecharacterEncodingutf8useSSLfalse username: root password: 123456 # application-prod.yml服务器部署 server: port: 8080 spring: datasource: url: jdbc:mysql://你的服务器IP:3306/daily_report?useUnicodetruecharacterEncodingutf8useSSLfalse username: 你的数据库用户名 password: 你的数据库密码通过--spring.profiles.activeprod切换环境避免每次部署都改代码里的配置这是正规项目的基本要求也是答辩的一项加分项。6.3 服务器防火墙和安全组这一步最容易卡住人。很多同学服务器上项目启动成功了但浏览器访问不了。原因几乎总是出在这两个地方云服务器安全组——在云控制台阿里云/腾讯云里给入方向规则放行8080端口。服务器防火墙——CentOS 7用firewalld管理# 查看防火墙状态 systemctl status firewalld # 永久放行8080端口 firewall-cmd --zonepublic --add-port8080/tcp --permanent firewall-cmd --reload # 或者直接关防火墙不推荐仅限测试环境 systemctl stop firewalld systemctl disable firewalld6.4 Nginx反向代理可选但推荐如果想让系统用80端口访问不需要带端口号可以用Nginx做反向代理server { listen 80; server_name yourdomain.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }配置后重启Nginx浏览器直接输入服务器IP或域名就能访问系统体验和专业度都会提升不少。7. 一套能直接上手的初始化数据方案系统跑起来了但页面上空空如也演示效果差很多。日报系统需要一些初始数据来支撑功能演示。我建议的初始化数据如下-- 管理员账号admin / admin123 INSERT INTO sys_user (username, password, real_name, role, department_id, status) VALUES (admin, $2a$10$7JBVBwqGfQkDZQ9b8TysI.k7Yd5QH0nDOlWTAThKhaY2GcHhC48ym, 系统管理员, 3, NULL, 1); -- 主管账号manager / 123456 -- 部门技术部负责人是manager INSERT INTO sys_department (dept_name, leader_id) VALUES (技术部, 2); INSERT INTO sys_user (username, password, real_name, role, department_id, status) VALUES (manager, $2a$10$mVmYHtGafZ6FNT7l6M7NyOD0DmVNONe2Hf2lQcyY49KPpKxOuBBnO, 张主管, 2, 1, 1); -- 员工账号zhangsan / 123456 INSERT INTO sys_user (username, password, real_name, role, department_id, status) VALUES (zhangsan, $2a$10$mVmYHtGafZ6FNT7l6M7NyOD0DmVNONe2Hf2lQcyY49KPpKxOuBBnO, 张三, 1, 1, 1);这里的密码是用BCrypt加密的。很多人不知道在哪里生成加密串我推荐两个方式在Spring Boot项目里写一个简单的测试类调用BCrypt.hashpw(123456, BCrypt.gensalt())在线的BCrypt生成器网页注意别用明文密码用一次性的。初始化一些日报数据用一条SQL循环插入即可INSERT INTO daily_report (user_id, business_date, title, content, work_type, status) VALUES (3, 2024-06-01, 完成登录模块开发, 今天完成了JWT登录认证模块包括token生成和拦截器配置。, 2, 2), (3, 2024-06-02, 完成日报提交接口, 实现了日报的增删改查接口并进行了Postman自测。, 2, 2), (3, 2024-06-03, 完善统计图表接口, 开发了个人日报提交趋势统计接口前端ECharts联调完成。, 2, 1);有了这些数据演示时你点进系统就能看到丰富的内容而不是干巴巴的空页面。8. 演示视频的录制思路和答辩准备最后一个部分是很多同学忽略但实际很关键的演示视频怎么录答辩怎么讲。8.1 演示视频脚本不要打开录屏软件想到哪里录哪里。提前写一个脚本按以下顺序走开场15秒启动项目登录管理员账号系统管理30秒展示用户管理、部门管理新建一个部门添加一个用户员工端60秒登录员工账号创建日报、提交日报、查看已提交列表主管端45秒切换主管账号审批日报通过一条、驳回一条查看部门统计管理员端30秒切换到管理员查看全公司统计看板展示折线图和部门提交率。每个环节结束前停留2秒确保画面清晰。视频总时长控制在3-4分钟即可太长老师反而不愿意看。8.2 答辩时的高频问题清单根据我的经验日报管理系统在答辩时老师最常问的问题如下问题参考回答思路为什么选择Spring Boot简化配置、内嵌Tomcat、生态成熟、社区文档丰富权限控制是怎么实现的JWT无状态认证 拦截器 角色字段判断三层配合如果用户越权访问怎么办后端每个接口都校验当前登录用户的角色与数据归属数据库表为什么这样设计基于第三范式消除冗余审批记录单独成表保留轨迹日期字段用date类型保证查询效率系统遇到了哪些难点跨天补交的日期约束、不同部门主管的数据隔离、ECharts统计数据的接口设计如何保证数据一致性事务注解Transactional在Service层控制这里特别叮嘱一句不要背稿。老师问什么就用自己的话讲什么哪怕回答得朴素一点也比机械重复强。如果你真的把项目每一行代码都自己写过一遍这些问题自然都有话说。8.3 关于一条龙资料的个人建议标题里提到的完整源码LW部署说明演示视频实际落地的重点是LW论文至少包含绪论、需求分析、系统设计、系统实现、系统测试、总结展望这几章每个章节都有对应的核心内容支撑论文里的截图和代码片段必须是从你当前实际运行的系统里截图的和系统完全一致部署说明建议写一个DOC/PDF按环境要求-数据库初始化-配置文件修改-启动步骤-浏览器访问五步走附带每一步的截图演示视频对应前面讲到的脚本同时录屏幕的时候保证字体大小清晰可见尤其是代码区域。8.4 收尾的一点真话日报管理系统这个题目与其说是在考察你会不会用Spring Boot不如说是在考察你有没有把一个小型业务系统从零做到上线的完整工程经验。它涵盖了需求分析、表结构设计、分层编码、权限控制、统计报表、打包部署这一整条链路而这些恰好是Java后端日常开发中最常用的能力。我的个人建议是拿到这个题目后先别急着找人要一份源码。哪怕参考别人的代码也要至少自己动手重写一遍——先把登录认证写了再写日报提交再写审批再写统计。等你把这条主线走通你对Spring Boot的理解会比看十遍教程都深。等真到了答辩那天老师问你什么问题你都不会心虚。如果后面的时间允许我会再单独写一篇针对日报系统前端Vue部分的实战拆解把登录页、日报列表页、审批页和ECharts看板的数据联调过程完整铺开。需要的同学可以先收藏回头对照着做。