简介这套基于Java的档案管理系统源码是面向计算机、电子信息工程、数学等专业毕业设计学生的高分项目资料适用于毕业设计答辩、课程设计、期末大作业等场景也适合需要项目实战练习的Java学习者。资源共440个文件压缩包大小约8.56MB主体包含131个Java后端源码、50个Vue前端页面以及JS、CSS、SVG等前后端静态资源与XML、properties等配置类文件另外还附有docx/doc项目说明文档整体目录结构清晰前后端分层明确便于快速把握系统从数据层到表现层的完整实现。包内提供1-install.bat、2-run.bat、3-build.bat等一键部署运行脚本配合Maven相关包装文件可在本地快速完成环境搭建与运行调试。目前已有531人学习下载项目经严格调试无明显Bug适合作为毕业设计参考也方便在此基础上进行二次开发或模块扩充尤其适合想学习企业级Java项目拆分思路的读者。1. 这套 Java 档案管理系统源码到底是什么一个能交差也能改的毕设底子很多同学从网上下载的“高分毕设源码”并不是解压就能跑的真正拿到手里经常是缺依赖、缺数据库脚本、一启动就红字。但档案管理系统是 Java 课程设计和毕业设计里最扎实的方向之一功能边界清晰用户登录、档案分类、档案增删改查、文件上传与预览、条件检索既能体现 CRUD 功底又能展示文件处理能力。这套基于 Java 的档案管理系统源码本质上是给你一个能运行的骨架让答辩前不用从零写起也能在现有代码上做二次开发。适合谁也很明确课程设计需要交代码和报告的同学毕业设计想选“管理类系统”又不想最后一个月才开始写代码的人刚学完 JavaWeb、想看看一个完整项目怎么分层的人。下面我按“跑起来 → 看懂核心逻辑 → 答辩必问 → 踩坑”的顺序讲最后给你三个改造加分项。2. 把源码跑起来导入、建库、改配置三板斧拿到源码后第一件事不是读代码而是让项目先在本机跑起来。一个跑不起来的项目后面谈什么都白搭。我先按最常见的工程形态来说Maven 管理的 Spring Boot 项目。如果你的压缩包里没有pom.xml而是webapp/WEB-INF/web.xml那种结构那就是传统的 SSH/SSM 项目思路一样只是启动方式不同后面我会专门提示。2.1 用什么技术栈接住这套源码Spring Boot MyBatis MySQL 是标配市面上的“java 档案管理系统源码”十套里有八套是 Spring Boot MyBatis MySQL剩下两套是老 JSP Servlet或者 Spring SpringMVC MyBatisSSM。我的建议是优先选 Spring Boot 版本因为启动简单内嵌了 Tomcat不用单独装Starter 机制把依赖冲突降到最低。答辩时老师问“为什么用这个架构”你可以答Spring Boot 降低配置成本MyBatis 让 SQL 可控、方便调优MySQL 免费且容易部署前端用 Vue 或 Thymeleaf 都能快速对接。拿到项目后先用编辑器看一眼pom.xml确认以下几个关键点spring-boot-starter-web决定是不是 Spring Boot 项目。mybatis-spring-boot-starter或mybatis-plus决定数据访问层写法。mysql-connector-java驱动版本是否和本机 MySQL 匹配。lombok如果存在IDE 需要启用注解处理器。pagehelper-spring-boot-starter是否有分页插件。这一步非常关键。很多源码运行不起来不是代码本身坏而是依赖版本和本机 JDK/MySQL 不匹配。JDK 8 配老的mysql-connector-java 5.x没问题但如果你本机是 MySQL 8驱动必须是com.mysql.cj.jdbc.Driver。这些细节我会在第 5 章展开。如果确认是 SSM 项目也不要慌。你可以在 IDEA 里配 Tomcat把 war 包部署进去但更推荐的做法是花一小时把配置迁移到 Spring Boot。常见做法是保留 Mapper XML 和 Service 代码不变只把web.xml换成SpringBootApplication启动类把spring相关 XML 里的 bean 定义用配置类重写。第一次做会有点绕但答辩时“升级了技术栈”本身就是亮点。2.2 本地运行最小步骤IDEA 导入到浏览器看到登录页先把项目完整跑通再看细节。下面这套步骤是 Spring Boot Maven 项目的通用流程按顺序执行即可。第一步用 IDEA 打开源码。注意不是“新建项目”而是File → New → Project from Existing Sources选择包含pom.xml的文件夹。如果源码是 Eclipse 结构IDEA 也能识别导入后耐心等 Maven 下载依赖。依赖下载慢的话在settings.xml里加阿里云镜像这一步几乎是所有初学者的后悔药。第二步准备数据库。先建库再导入源码里的 SQL 文件。SQL 文件通常放在db/、sql/或resources/下面。命令行方式如下# 确认项目根目录是 Maven 项目 ls pom.xml # 创建数据库注意字符集 mysql -uroot -p -e CREATE DATABASE archive_db DEFAULT CHARACTER SET utf8mb4; # 导入源码自带的 sql 脚本 mysql -uroot -p archive_db db/archive.sql这段命令的原理很简单先建一个空库再用mysql客户端执行 SQL 脚本创建表和初始数据。重点是utf8mb4而不是utf8因为utf8mb4才能正确存放中文、emoji 以及特殊字符。如果源码里的脚本没有建库语句你就得先手动建库再导入如果连 SQL 都没有那就得靠实体类反推建表这个我会在第 3 章讲表结构时帮你兜底。第三步改配置。找到src/main/resources/application.yml或application.properties把数据库账号密码改成你本机的。这一步最容易被漏掉我看到太多人卡在“数据库连接失败”上其实是配置文件里还是作者的密码。第四步运行启动类。在src/main/java下找到XxxApplication.java类上有SpringBootApplication注解。右键运行。启动日志出现Tomcat started on port(s): 8080后浏览器访问http://localhost:8080。如果项目设置了上下文路径访问地址会对不上这时候不要慌看启动日志里的端口和 context path 即可。默认管理员账号一般是admin / admin或admin / 123456具体在 SQL 脚本初始化数据里能查到。2.3 三张必改配置数据库连接、文件存储路径、端口与上下文项目跑起来之后你要立刻把三个配置改成自己的不然换一台电脑就废。第一个是数据库连接第二个是文件上传存放目录第三个是端口和上下文路径。server: port: 8080 servlet: context-path: / spring: datasource: url: jdbc:mysql://localhost:3306/archive_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 20MB max-request-size: 100MB jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 file: upload-path: D:/temp/archive/ access-path: /files/**这段配置里的参数我逐个解释一下答辩老师很喜欢问这里。server.servlet.context-path如果设置成/archive那么所有请求地址都要以/archive开头。很多源码的前端代码还是写死的/api一旦加了前缀就全部 404。所以本地调试时建议设置为/或者干脆删掉这一行。datasource.url里的serverTimezoneAsia/Shanghai必配否则 MySQL 8 驱动会报时区错误characterEncodingutf8写成utf8不要写utf-8否则会乱码。useSSLfalse是避免本地连接时 SSL 握手报警告。multipart.max-file-size控制单个附件大小。档案系统里如果传 PDF 扫描件20MB 够用如果传高清批文改成 50MB。max-request-size要大于单文件上限因为一次请求可能包含文件加表单。jackson.date-format是后端接口返回时间字段的格式如果不配前端拿到的是数组或者一串数字用户看不懂。file.upload-path是附件存到磁盘的位置。Windows 下要用盘符绝对路径比如D:/temp/archive/末尾一定要有分隔符。Linux 下建议放在/var/archive/并且保证启动进程有写权限。access-path是 URL 访问前缀这个要和后面的资源映射配置保持一致。记住Spring Boot 不会自动把磁盘目录暴露给浏览器必须额外配置addResourceHandlers否则上传成功也访问不了。这个坑我放在第 5 章讲。提示如果你拿到的是.properties文件配置项一模一样只是写法从冒号分隔改成点号分隔比如spring.datasource.url...。3. 档案管理系统的核心逻辑借一张表看懂增删改查怎么落地数据库跑通、页面能打开之后下一步要读懂“档案管理”这四个字到底是怎么变成代码的。别一上来就翻源码里的所有类那样很容易迷路。我的经验是先看数据库表再看一个完整的新增流程最后看前端怎么对接。一条线走通剩下的模块就都是套路。3.1 数据模型怎么设计档案表、分类表、用户表的关系档案管理系统的主角是“档案”围绕档案一定会扩展出用户和分类。最简的表结构可以做成三张表用户表、分类表、档案表。很多高分毕设在它们之上再加一张“借阅记录表”但核心还是这三张。CREATE TABLE archive_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50) DEFAULT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE archive_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, parent_id INT DEFAULT 0, INDEX idx_parent(parent_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE archive_doc ( id INT PRIMARY KEY AUTO_INCREMENT, doc_no VARCHAR(50) NOT NULL, title VARCHAR(200) NOT NULL, category_id INT NOT NULL, file_path VARCHAR(255) DEFAULT , create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, creator_id INT DEFAULT NULL, status TINYINT DEFAULT 1, UNIQUE KEY uk_doc_no(doc_no), INDEX idx_category(category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;为什么要这样设计我在这个项目上踩过最大的坑就是把文件本身存进数据库用longblob字段。虽然也能跑但档案一多每次列表查询都要把文件内容从磁盘读出来内存占用高响应速度慢得离谱。正确的做法是表里只存file_path文件放磁盘系统只需要把文件路径映射成 URL 返回给前端。这也是真实业务系统里最常见的做法。doc_no这一段交叉验证也很重要。档案编号要有唯一性所以建了唯一索引。分类表用parent_id实现父子关系根分类的parent_id为 0。这样既能支持一级分类也能扩展成树形分类。creator_id是外键逻辑不强制建物理外键保留字段即可。status用于表示在用/停用/已销毁删除操作尽量别物理删后面会讲。3.2 后端接口实现一个档案新增接口的完整链路看懂了表接下来看一个接口是怎么从浏览器走到数据库的。常见源码会分成 Controller、Service、Mapper 三层。Controller 负责接收参数和响应Service 负责业务校验Mapper 负责 SQL。下面是一段简化但完整的新增档案代码我故意把校验写在 Service 里因为答辩老师大概率会问“为什么不在 Controller 里直接判断”。RestController RequestMapping(/api/archive) public class ArchiveController { Resource private ArchiveService archiveService; PostMapping(/add) public Result add(RequestBody ArchiveDoc doc) { archiveService.insert(doc); return Result.success(新增成功); } }Service public class ArchiveServiceImpl implements ArchiveService { Resource private ArchiveDocMapper archiveDocMapper; Override public void insert(ArchiveDoc doc) { if (doc.getDocNo() null || doc.getDocNo().trim().isEmpty()) { throw new BusinessException(档案编号不能为空); } if (archiveDocMapper.countByDocNo(doc.getDocNo()) 0) { throw new BusinessException(档案编号已存在); } if (doc.getCategoryId() null) { throw new BusinessException(必须选择分类); } doc.setCreateTime(new Date()); doc.setUpdateTime(new Date()); archiveDocMapper.insert(doc); } }Controller 只有一行调用好处是接口层很薄业务逻辑都收敛在 Service。RequestBody说明前端传的是 JSON不是传统表单。如果前端用表单提交那这里就要换成RequestParam或直接用一个 VO 对象接收。很多源码混用这两种方式这是后端接口里最容易出现 415/400 错误的地方。Service 里的countByDocNo是 Mapper 提供的一个查询方法用来防重复。这里要注意一个并发边界如果两个请求同时插入相同的doc_no先查后插还是可能撞车。真正的兜底是数据库唯一索引所以我在建表 SQL 里加了UNIQUE KEY uk_doc_no。一旦唯一键冲突异常会被全局异常处理器统一捕获返回“档案编号已存在”。这一点你如果能在答辩时说出来老师会觉得你有生产意识。如果你拿到的源码把这段校验写成了“直接 insert报错就返回异常”也别慌。你可以照上面改改动量不大但代码质量一眼就能看出来。再往下看 Mapper 层就是一个正常的 insert SQLid自增create_time用 Java 传入的时间。这里有一个细节如果 MySQL 表字段已经有DEFAULT CURRENT_TIMESTAMPJava 端可以不传createTime但为了在不同数据库间保持一致显式传值更稳妥。3.3 前端页面与服务端对接列表渲染和上传组件的关键代码后端接口有了前端怎么调用如果源码用的是 JSP/Thymeleaf页面是服务端渲染如果用的是 Vue 或 Layui那就是前后端分离。这里以最常见的 Vue Element UI 为例一段新增档案的请求代码长这样axios.post(/api/archive/add, { docNo: form.docNo, title: form.title, categoryId: form.categoryId, filePath: fileUrl }).then(res { if (res.data.code 200) { this.$message.success(新增成功); this.loadTable(); } else { this.$message.error(res.data.msg); } }).catch(() { this.$message.error(网络异常请稍后再试); });注意这里filePath不是文件对象而是文件上传后返回的 URL。档案系统里通常先调上传接口拿到/files/xxx.pdf再和表单字段一起提交。上传接口在第 4 章会专门讲。这里的前端逻辑把“文件上传”和“档案新增”解耦好处是新增页面不会因为文件太大卡死上传失败时可以单独提示用户重传。请求里返回的结构统一是{code, msg, data}这是很多源码的通用约定。我看到过一些项目有的接口返回boolean有的返回Map导致前端要写一堆兼容代码。如果你要改造建议先设计一个Result类所有接口都返回Result.success(data)或Result.error(msg)。这样前端只要判断code 200就能进入成功分支后续加拦截器、加日志都方便。列表渲染就简单了loadTable方法调用/api/archive/list返回{list, total, page, pageSize}然后把list赋值给表格的data属性把total赋给分页组件。你可能会看到源码里用的是layui或jQuery的写法但思路完全一样前端按页码请求数据后端返回当前页的数据和一页总数前端渲染表格。提示如果你发现前端代码里的接口路径和后端RequestMapping对不上先看是否有全局前缀比如server.servlet.context-path。这是前后端分离项目最常见的黑匣子。4. 答辩必问的关键代码登录鉴权、文件上传、分页查询如果只把增删改查跑通这只能算“能运行”谈不上“高分”。答辩时老师最爱问的其实是三个横切点登录是怎么鉴权的文件是怎么存的列表数据多了怎么分页这三个功能几乎存在于每一套 Java 档案管理系统中也是最能看出你是否真懂源码的试金石。4.1 登录鉴权Session 与拦截器的写法先说明一点档案管理系统是内部系统最简单的鉴权方式是 Session。用户在登录接口验证通过后把用户对象放进 HttpSession之后每个请求进来都先经过拦截器检查 Session 里有没有这个对象。这样比 JWT 简单也不用处理 token 过期刷新。答辩时如果老师问“为什么不用 JWT”你可以说该系统是单机部署、用户量小Session 天然防止 token 被 XSS 窃取且实现成本低。拦截器代码Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录或登录已过期\}); return false; } return true; } }这段代码的核心是preHandle。它在 Controller 方法执行之前运行如果返回false请求就到此为止。返回true才会继续往下走。注意设置响应编码为UTF-8否则返回的 JSON 中文会乱码。拦截器注册Configuration public class WebConfig implements WebMvcConfigurer { Resource private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) .excludePathPatterns(/login, /api/login, /css/**, /js/**, /images/**, /files/**); } }这里的excludePathPatterns是很多源码翻车的重灾区。登录页本身和静态资源不能拦否则你连登录页都看不到。/files/**也要放行否则上传的附件在前端预览时也会被拦截报 401。还有/error最好也放行不然登录失败后的错误提示都展示不出来。如果源码里用的是 Spring Security那配置方式不同但原理一样写一个SecurityFilterChain放行/login和静态资源其余请求需要认证。一个常见的问题是拦截器排除路径支持/**通配但不支持正则。很多同学想写*.html结果完全失效。不要问为什么Spring 的路径匹配规则就是这样的——用/css/**这种 Ant 风格不要用*.html。4.2 文件上传与预览把附件存本地并回显文件上传是档案系统里最有区分度的功能。上传一个 PDF回显到页面能预览答辩效果比纯文字描述好很多。实现上分三步接收 MultipartFile、把文件写到磁盘、返回可访问的 URL。后端上传接口PostMapping(/api/file/upload) public Result upload(RequestParam(file) MultipartFile file) { if (file null || file.isEmpty()) { return Result.error(文件不能为空); } String originalFilename file.getOriginalFilename(); String ext ; if (originalFilename ! null originalFilename.contains(.)) { ext originalFilename.substring(originalFilename.lastIndexOf(.)); } String newFilename UUID.randomUUID().toString().replace(-, ) ext; File dir new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); } File dest new File(dir, newFilename); try { file.transferTo(dest); return Result.success(/files/ newFilename); } catch (IOException e) { log.error(文件上传失败, e); return Result.error(文件上传失败请重试); } }为什么文件名要改成 UUID因为两个用户可能上传同名文件比如都叫“扫描件.pdf”如果不重命名后上传的会覆盖先上传的或者因为带中文产生 URL 编码问题。UUID 能保证全局唯一。扩展名是从原始文件名里截取的不要用file.getContentType()来拼因为 Office 文件的 Content-Type 经常不准。uploadPath从哪里来就是从application.yml里的file.upload-path读取。如果路径不存在mkdirs()会创建多级目录。注意transferTo(dest)内部已经把临时文件移动到了目标位置不需要再调用file.save()之类的方法。文件落盘之后返回的 URL 是/files/新文件名这个 URL 还不能直接访问需要配置资源映射Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/files/**) .addResourceLocations(file: uploadPath); }addResourceHandler(/files/**)说的是 URL 以哪些路径开头addResourceLocations(file: uploadPath)说的是映射到磁盘哪个目录。这里的坑在file:前缀没有这个前缀Spring 会把路径当成 classpath 下的目录肯定 404。Windows 盘符写法是file:D:/temp/archive/Linux 是file:/var/archive/末尾都要带/。另外WebMvcConfigurer中有多个addResourceHandlers方法不要覆盖默认静态资源映射。你只要在方法里追加你自己的映射即可Spring Boot 会同时生效。如果你用了EnableWebMvc默认静态资源映射会被关掉很多人的 css/js 全部挂掉这是另一个隐藏坑。4.3 分页与条件检索PageHelper 还是手动 LIMIT档案列表页不能一次性把几万条数据查出来分页是必考问题。市面上源码常用 PageHelper也有老项目手写LIMIT。我先把 PageHelper 的典型写法写出来再对比一下手写方案。GetMapping(/api/archive/list) public Result list(RequestParam(defaultValue 1) int page, RequestParam(defaultValue 10) int pageSize, RequestParam(required false) String keyword) { PageHelper.startPage(page, pageSize); ListArchiveDoc list archiveDocMapper.selectByKeyword(keyword); PageInfoArchiveDoc pageInfo new PageInfo(list); MapString, Object data new HashMap(); data.put(list, pageInfo.getList()); data.put(total, pageInfo.getTotal()); data.put(page, pageInfo.getPageNum()); data.put(pageSize, pageInfo.getPageSize()); return Result.success(data); }PageHelper.startPage(page, pageSize)的作用是在当前线程的上下文里设置分页参数紧接着执行的第一次 Mapper 查询会自动被拦截拼上LIMIT并生成一条COUNT查询得到总数。注意它的生效范围是“紧接着的下一条查询”如果中间插了别的查询分页可能被下一条语句消费掉导致数据错乱。这就是 PageHelper 的 ThreadLocal 机制带来的双刃剑。select idselectByKeyword resultTypeArchiveDoc SELECT * FROM archive_doc where if testkeyword ! null and keyword ! doc_no LIKE CONCAT(%, #{keyword}, %) OR title LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY create_time DESC /select这里的where标签能自动去掉第一个AND避免手写WHERE 11的丑代码。LIKE CONCAT(%, #{keyword}, %)是参数化的模糊查询千万不要用%${keyword}%后者会拼 SQL存在注入风险。这是答辩时安全意识的加分点。如果源码没引入 PageHelper手写分页也很简单先SELECT COUNT(*)再SELECT ... LIMIT #{offset}, #{pageSize}。手写的优势是可控、没有 ThreadLocal 的副作用适合 SQL 复杂的报表页劣势是每个列表接口都要写两遍查询。PageHelper 更适合常规列表。我一般给档案系统改造时的建议是常规 CRUD 列表用 PageHelper统计数据报表用手写 SQL。注意PageHelper 的页码从 1 开始如果你从前端传 0会被当成第 0 页容易出现第一页空白。前端分页组件的current-page通常从 1 开始别把 page 初始值设成 0。5. 源码避坑指南Windows 路径、JDK 版本、MySQL 8 密码插件这一章写的都是我自己和身边同学在这个项目上真实翻车过的场景。很多问题看起来像是“代码坏了”其实是环境配置和源码里的隐藏设定。按照“现象 → 原因 → 解决”的格式写方便你遇到同样问题时直接对号入座。5.1 启动就报 java.sql.SQLNonTransientConnectionException现象项目启动时控制台刷出一大串红色异常核心提示是Cannot create PoolableConnectionException或SQLNonTransientConnectionException后面跟着Public Key Retrieval is not allowed或Unable to load authentication plugin caching_sha2_password。原因MySQL 8 默认的密码认证插件是caching_sha2_password老驱动不认它另一个常见原因是连接 URL 没配置allowPublicKeyRetrievaltrue。有的源码是两三年前写的用的还是com.mysql.jdbc.Driver老驱动和 MySQL 8 匹配不上。解决把驱动类改成com.mysql.cj.jdbc.Driver在 URL 上加三个参数useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai。如果还不行在 MySQL 里把 root 的密码插件改成老插件ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;这条 SQL 在普通黑框里执行会没用必须在mysql -uroot -p进入的命令行客户端里执行。改完之后重启 MySQL 服务再启动项目。如果是 MySQL 5.7基本不会遇到这个问题直接跳过。5.2 上传文件后访问 404路径映射没配现象文件上传接口返回成功返回了/files/xxx.pdf但把这个地址粘贴到浏览器里打开直接 404页面显示Whitelabel Error Page。原因/files/**这个 URL 没有对应到磁盘目录。Spring Boot 默认只映射 classpath 下的静态资源外部磁盘目录不会被自动识别。你必须在WebMvcConfigurer.addResourceHandlers里增加映射。解决按第 4 章 4.2 的配置写。同时检查file.upload-path的写法。我看到有同学写成D:/temp/archive末尾没有/然后资源映射写file:D:/temp/archive/结果拼接路径时出现archivexxx.pdf目录找错了。统一规范配置里的路径和资源映射里的路径都以/结尾。另外如果项目有上下文路径/files/**也要跟着变比如context-path/archive时访问地址是/archive/files/xxx.pdf。5.3 Lombok 没生效IDEA 没装插件 / 注解处理没开现象源码里的实体类和 DTO 只有字段没有 getter/setter但代码里到处用user.getUsername()。一编译就报“找不到符号 getUsername”或者提示“程序包 lombok 不存在”。原因项目用了 Lombok 注解在编译时自动生成 getter/setter。IDEA 默认开了注解处理但如果你用 Eclipse 或换了新电脑Lombok 插件没装或者 Maven 依赖没下载成功就会出现这种问题。解决先确认pom.xml里有 Lombok 依赖。然后在 IDEA 的Settings → Build, Execution, Deployment → Compiler → Annotation Processors勾选Enable annotation processing。如果你没有插件打开Settings → Plugins搜索 Lombok 安装重启 IDEA。还有一个冷门坑JDK 版本太高比如 JDK 17 配合过老的 Lombok 1.16.20会直接编译报错。这时候要么把 Lombok 版本升到 1.18.30 左右要么干脆把实体类的 getter/setter 手写完整。手写虽然多几行但对毕设来说反而不用依赖插件。5.4 时间字段显示乱码或格式不对JSON 序列化配置现象前端展示列表时createTime显示为2025-05-01T08:00:00.00000:00或者直接是一串数字1714560000000和数据库里的2025-05-01 16:00:00完全对不上。原因后端返回 Date 对象时默认的 Jackson 序列化格式是 ISO 格式而且时区是 UTC。北京时间比 UTC 早 8 小时所以看到的时间总是比数据库少 8 小时。解决在application.yml里加spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8如果你用的是 Spring Boot 2.7spring.jackson.date-format对java.util.Date生效对 Java 8 的LocalDateTime不生效后者需要单独加JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8) private LocalDateTime createTime;还有一个容易忽略的点数据库连接 URL 里的characterEncodingutf8不能写成utf-8否则中文会变成问号。MySQL 的字符集参数叫utf8不是utf-8。这个细节哪怕运行不报错也会让页面上的中文档案名变成???。5.5 端口占用和上下文路径导致页面白屏现象启动日志里写着Port 8080 was already in use或者项目启动成功但浏览器访问登录页时页面样式全部丢失接口请求全是 404。原因端口被其他进程占用了通常是之前崩溃的后台程序或另一个 Tomcat。上下文路径的问题则是context-path被设置成/archive但前端代码里的请求路径还是/api/...导致所有接口都错位。解决换端口最快。可以在application.yml改server.port: 8081或者启动时在 IDEA 的 Program arguments 加上--server.port8081。不要同时启动两个实例会互相干扰。上下文路径的解法是保持默认/。如果项目必须带前缀那前端每个 axios 请求的 baseURL 也要加上前缀比如axios.defaults.baseURL /archive同时静态资源引用也要用/archive/...开头。你可以在浏览器按 F12 看 Network 面板如果 HTML 加载成功但 CSS 404就是上下文路径没处理好。注意这个问题最容易发生在“源码自带 context-path 配置”的项目里。作者本机没问题是因为他所有请求路径都写了前缀你如果只改了数据库没改 context-path就会出现页面能看到但接口全挂的现象。6. 把这套源码改造成答辩高分项目的三个抓手项目能跑、能答得上核心代码已经及格了。但真要冲高分我会在现有骨架上做三个改动批量导入、操作日志、加上验证清单。改动都不大却能让演示流程完整很多。6.1 加一个批量导入 Excel 的功能档案管理员的日常操作里逐条录入最枯燥。你可以在“新增”旁边加一个“批量导入”用 EasyExcel 或者 Apache POI 读取 Excel逐行解析后复用archiveService.insert。代码核心就一行EasyExcel.read(inputStream, ArchiveExcel.class, new ArchiveReadListener(archiveService)).sheet().doRead();导入前先下载模板导入失败的行要把错误原因汇总返回不要一条错就全部回滚。答辩时演示一次 200 行数据的导入比手动点 20 次新增有说服力得多。6.2 用 AOP 记录操作日志老师常问“系统有没有审计功能”。给关键的增删改接口加一个OperLog注解再用 AOP 切面把操作人、操作类型、参数、耗时写进archive_log表。这样即兴演示“我删了一个档案”去日志列表里能看到记录闭环就成立了。Aspect Component public class OperLogAspect { Around(annotation(operLog)) public Object around(ProceedingJoinPoint pjp, OperLog operLog) throws Throwable { long start System.currentTimeMillis(); Object result pjp.proceed(); // 写日志当前登录用户 operLog.value() 耗时 return result; } }6.3 验证方法从登录到导出的端到端清单改造完不要只点一遍页面就完事。我一般会按这个顺序过一遍步骤操作预期1未登录直接访问首页返回 401 并跳登录2用管理员账号登录进入档案列表3新增档案重复编号再试一次第二次提示编号已存在4上传 PDF 并在新增页回显预览 URL 可打开5按关键词检索结果包含 doc_no 和 title 的命中6删除一条档案列表数量减一日志表多一条记录7重启项目再访问数据仍在附件仍能打开这套清单也是答辩前的自测脚本。这个项目我前后帮人调过不少次最大的教训是别迷信“高分源码”四个字拿到手先跑通、再逐行看最后把一两个亮点加进去比什么都有用。希望这些踩坑记录能帮到你愿你的档案管理系统一次启动成功。本文还有配套的精品资源点击获取
