☰
基于RuoYi的Java档案管理系统源码:权限、借阅与避坑实战
2026/9/29 18:32:33 网站建设 项目流程

简介:这份资源是基于RuoYi框架搭建的Java档案管理系统设计源码,面向具备一定Java与Vue基础、希望深入实践企业级权限管理与档案业务开发的开发者,可用于课程设计、毕业设计或个人技术练手。压缩包共812个文件,约99.27MB,以367个Java源文件、117个Vue组件、101个JavaScript脚本为主体,辅以XML配置、SCSS样式、SQL脚本及BAT批处理文件,覆盖后端业务逻辑、前端页面渲染与项目构建部署等环节,目录结构完整,便于按模块拆解学习。目前已有970人学习下载,说明其在同类RuoYi二次开发案例中具有一定参考价值。读者可从中获取基于RuoYi的档案管理功能实现思路、前后端分离的工程组织方式,以及权限控制、数据字典、代码生成等通用模块的落地写法,适合对照源码理解框架扩展与业务定制流程。

1. 档案管理系统为什么选 RuoYi:从一份能跑通的 Java 源码说起

很多做 Java 课程设计或企业内训的同行,一提到"档案管理系统"就头疼:权限模型要自己搭、菜单要自己写、部门数据要自己做隔离,光是把 RBAC 跑通就得耗掉一周。这份基于 RuoYi 的 Java 档案管理系统设计源码,解决的正是这个痛点——它把 RuoYi 这套成熟的 Spring Boot 后台脚手架直接落到档案业务上,登录、菜单、角色、部门、数据权限这些通用能力开箱即用,你只需要专注档案著录、借阅、归档、销毁这几条真正的业务线。适合谁?一是做 Java 课程设计、需要一套结构完整且能讲清楚技术栈的在校生;二是想快速验证档案类需求、不想从零搭后台的初中级后端;三是拿 RuoYi 做项目实训、想找一个真实业务场景练手的开发者。它不是一个玩具 Demo,而是一套按企业后台分层规范组织的工程,能不能用、怎么改、坑在哪,下面拆开讲。

2. RuoYi 档案系统的技术底座:分层结构与档案业务怎么落

2.1 为什么档案业务适合架在 RuoYi 上

档案管理的本质是"受控的增删改查 + 严格的权限 + 可追溯的操作记录",这三件事恰好是 RuoYi 的强项。RuoYi 默认提供了用户、角色、菜单、部门、岗位、字典、参数配置、操作日志、登录日志这一整套基础模块,档案系统只需要在此基础上扩展业务表即可。举个具体例子:档案的密级(公开、内部、秘密)可以直接复用 RuoYi 的字典管理(sys_dict_type / sys_dict_data),不用自己再建一张密级表;档案的归属部门直接挂到 sys_dept,数据权限用 RuoYi 自带的 @DataScope 注解就能实现"本部门只能看本部门档案"。这就是选 RuoYi 而不是从零搭 Spring Boot 的核心理由——省下的不是几百行代码,而是一整套已经踩过坑的权限与日志体系。

从技术栈看,这套源码是典型的 RuoYi 前后端分离或单体结构(取决于你拿到的分支),后端 Spring Boot + MyBatis + Shiro/Spring Security,前端如果是分离版则是 Vue + Element UI。档案业务通常落在ruoyi-system或独立新建的ruoyi-archive模块里,遵循 Controller → Service → Mapper → XML 的标准分层。理解这个分层,是后面所有改动的前提。

2.2 档案核心表结构与代码生成器的用法

档案系统绕不开几张核心表:档案主表(archive_info)、档案分类表(archive_category)、借阅记录表(archive_borrow)、归档/销毁记录表。RuoYi 自带代码生成器,能根据表结构一键生成 Controller、Service、Mapper、XML 和前端页面,这是这套源码最省力的地方。下面是我一般会走的建表到生成流程。

先建档案主表,字段设计要预留扩展:

CREATE TABLE archive_info ( archive_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '档案ID', archive_no VARCHAR(64) NOT NULL COMMENT '档案编号', archive_title VARCHAR(255) NOT NULL COMMENT '档案题名', category_id BIGINT(20) NOT NULL COMMENT '档案分类ID', secret_level CHAR(1) DEFAULT '0' COMMENT '密级 0公开 1内部 2秘密', dept_id BIGINT(20) DEFAULT NULL COMMENT '归属部门', storage_location VARCHAR(128) DEFAULT NULL COMMENT '存放位置', status CHAR(1) DEFAULT '0' COMMENT '状态 0在库 1借出 2销毁', create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', create_time DATETIME DEFAULT NULL COMMENT '创建时间', update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', update_time DATETIME DEFAULT NULL COMMENT '更新时间', remark VARCHAR(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (archive_id), UNIQUE KEY uk_archive_no (archive_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='档案信息表';

建表时几个参数要留意:archive_no加了唯一索引,因为档案编号是业务主键,重复录入是档案系统最常见的脏数据来源;secret_level用 CHAR(1) 而不是 INT,是为了和 RuoYi 字典的 value 类型对齐,避免前端回显时类型不匹配;dept_id是后面做数据权限的关键字段,一定要留。建完表后进 RuoYi 后台的"系统工具 → 代码生成",导入这张表,配置好字段的显示类型(密级选下拉、部门选部门树、时间选日期控件),生成后把 zip 里的文件按目录贴进工程。

生成出来的代码不是终点,而是起点。默认生成的查询是等值匹配,档案题名这种需要模糊查询的字段,要手动改 XML:

<if test="archiveTitle != null and archiveTitle != ''"> AND archive_title LIKE CONCAT('%', #{archiveTitle}, '%') </if>

逻辑说明:RuoYi 生成的<if test="xxx != null and xxx != ''">是 MyBatis 动态 SQL 的标准写法,CONCAT('%', #{archiveTitle}, '%')用#{}预编译占位,能防 SQL 注入,别图省事写成${}。参数说明:archiveTitle对应实体类字段,前端传参名要和它一致,否则条件不生效——这是新手最常翻车的地方,查不出数据先看这里。

2.3 数据权限与档案密级的双重控制

档案系统比普通后台多一层要求:不仅要控制"哪个部门能看",还要控制"哪个密级能看"。RuoYi 的 @DataScope 解决前者,密级过滤需要自己加。常见做法是在 Service 层拼一个密级条件,或者在 Mapper 里根据当前登录用户的密级上限过滤。

@Override @DataScope(deptAlias = "d", userAlias = "u") public List<ArchiveInfo> selectArchiveList(ArchiveInfo archive) { // 密级过滤:只返回当前用户密级权限范围内的档案 Long userId = SecurityUtils.getUserId(); SysUser user = userService.selectUserById(userId); // 假设用户表扩展了 max_secret_level 字段 archive.getParams().put("maxSecretLevel", user.getMaxSecretLevel()); return archiveInfoMapper.selectArchiveList(archive); }

逻辑说明:@DataScope注解会在 MyBatis 执行前动态拼接部门数据权限 SQL,deptAlias和userAlias要和 XML 里表的别名对上,否则拼接出来的 SQL 会报字段找不到。密级这块,getParams()是 RuoYi BaseEntity 提供的扩展参数 Map,把密级上限塞进去,XML 里用AND secret_level &lt;= #{params.maxSecretLevel}过滤。参数说明:max_secret_level不是 RuoYi 原生字段,需要你在 sys_user 表扩展,或者在关联的岗位/角色上定义,选哪种取决于你的权限模型粒度。这一步是档案系统区别于普通 CRUD 的关键,做不对就会出现"内部人员看到秘密档案"的越权问题。

3. 把源码跑起来:环境、配置与档案模块接入

3.1 环境准备与依赖版本对齐

拿到源码第一件事不是急着改代码,而是把环境跑通。RuoYi 对 JDK 和 MySQL 版本有隐性要求,版本错配是启动失败的头号原因。我一般按下面的清单核对:

组件推荐版本说明
JDK1.8 或 17老版 RuoYi 用 1.8,新版支持 17,看 pom 里的编译级别
MySQL5.7 / 8.08.0 要注意驱动类名和时区参数
Maven3.6+低了会拉不到某些依赖
Redis5.0+RuoYi 用 Redis 存 token 和缓存,不启动会登录失败
Node.js14/16仅前后端分离版需要,版本过高 Element UI 编译会报错

数据库脚本一般在sql目录下,先执行ry_20xxx.sql建基础表,再执行档案业务表的脚本。执行顺序不能反,因为档案表可能引用了 sys_dept 的外键或字典数据。Redis 这块很多人忽略,RuoYi 默认把登录 token 存 Redis,如果 Redis 没起或者密码不对,表现是"登录成功但立刻掉线",别去查代码,先看 Redis 连接。

3.2 配置文件里必须改的几个参数

application.yml和application-druid.yml是启动的关键。下面是我每次必改的几处:

spring: datasource: druid: master: url: jdbc:mysql://localhost:3306/ry_archive?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8 username: root password: 你的密码 redis: host: localhost port: 6379 password: 你的redis密码 database: 0

逻辑说明:serverTimezone=GMT%2B8是东八区,不写会报时区错误或时间差 8 小时;useSSL=false本地开发关掉,避免证书告警;zeroDateTimeBehavior=convertToNull处理 MySQL 的 0000-00-00 日期,档案系统里日期字段多,这个参数能省不少异常。参数说明:Redis 的database建议单独用一个库(比如 0 或 1),别和别的项目共用,否则 key 冲突会导致莫名其妙的登录态丢失。改完配置先别急着加业务代码,用默认账号 admin/admin123 登录一次,确认基础框架没问题,再动档案模块。

3.3 档案模块接入主工程的两种方式

生成的档案代码有两种接法:一是直接放进ruoyi-system模块,省事但耦合高;二是新建ruoyi-archive模块,在父 pom 和ruoyi-admin的 pom 里加依赖。我倾向第二种,尤其当档案业务会持续扩展时。

<!-- ruoyi-admin/pom.xml 中引入档案模块 --> <dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-archive</artifactId> <version>${ruoyi.version}</version> </dependency>

逻辑说明:新建模块要在根 pom 的<modules>里注册,否则 Maven 不认;ruoyi-archive自己的 pom 要继承父 pom 并引入ruoyi-common,这样 SecurityUtils、BaseEntity 这些基础类才能用。参数说明:${ruoyi.version}用属性占位,和父 pom 保持一致,别写死版本号,否则升级时到处改。模块化之后,档案的 Controller 要确保被 Spring 扫描到——RuoYi 默认扫描com.ruoyi包,只要你的包名在这个前缀下就没问题,包名起成com.archive就会 404,这是模块化最常见的坑。

4. 档案业务功能实现:著录、借阅、归档的代码落地

4.1 档案著录与编号自动生成

档案著录是录入环节,核心难点是档案编号的自动生成和唯一性校验。编号规则通常是"分类码 + 年份 + 流水号",比如WS-2024-0001。我一般放在 Service 层生成,用数据库唯一索引兜底。

@Override public String generateArchiveNo(Long categoryId) { // 查分类编码 ArchiveCategory category = categoryMapper.selectById(categoryId); String year = DateUtils.dateTime(new Date(), "yyyy"); // 查当年该分类最大流水号,加锁防并发 String maxNo = archiveInfoMapper.selectMaxNoByCategory(categoryId, year); int seq = 1; if (StringUtils.isNotEmpty(maxNo)) { seq = Integer.parseInt(maxNo.substring(maxNo.lastIndexOf("-") + 1)) + 1; } return category.getCategoryCode() + "-" + year + "-" + String.format("%04d", seq); }

逻辑说明:selectMaxNoByCategory要在 XML 里用ORDER BY archive_no DESC LIMIT 1取最大号,注意并发场景下两个请求可能拿到同一个 maxNo,所以要么在方法上加锁,要么依赖archive_no的唯一索引,插入失败后重试。参数说明:String.format("%04d", seq)保证流水号补零到 4 位,超过 9999 会自动变 5 位,如果业务上不允许,要在这里加判断。DateUtils.dateTime是 RuoYi 工具类,别自己造轮子。这一步的血泪经验是:编号生成一定要有唯一索引兜底,光靠代码查重,高并发下必出重复。

4.2 借阅流程与状态流转

借阅是档案系统里状态最多、最容易出 bug 的环节。一个完整的借阅要走"申请 → 审批 → 借出 → 归还"四步,档案主表的 status 要跟着变。常见做法是把借阅记录单独建表,档案状态只在借出和归还时更新。

@Override @Transactional(rollbackFor = Exception.class) public int borrowArchive(ArchiveBorrow borrow) { // 1. 校验档案是否在库 ArchiveInfo archive = archiveInfoMapper.selectById(borrow.getArchiveId()); if (!"0".equals(archive.getStatus())) { throw new ServiceException("档案当前不可借阅"); } // 2. 写入借阅记录 borrow.setBorrowTime(new Date()); borrow.setStatus("1"); // 1借出中 borrowMapper.insert(borrow); // 3. 更新档案状态为借出 archive.setStatus("1"); return archiveInfoMapper.updateById(archive); }

逻辑说明:@Transactional(rollbackFor = Exception.class)保证借阅记录写入和档案状态更新要么都成功要么都回滚,缺了它会出现"记录写了但档案还在库"的不一致。参数说明:status的取值要和字典对齐,0 在库、1 借出、2 销毁,别在代码里散落魔法值,建议定义常量类。ServiceException是 RuoYi 的全局异常,抛出去会被统一异常处理器捕获并返回友好提示,比返回 null 让前端猜要靠谱。归还逻辑类似,但要额外判断是否逾期,逾期可以算罚金或记入信用,这块按业务需求扩展。

4.3 归档与销毁的审批留痕

档案的归档和销毁属于不可逆操作,必须留痕。RuoYi 自带操作日志(@Log 注解),但业务级的审批留痕要自己设计。我的做法是建一张archive_operate_log表,记录操作类型、操作人、操作时间、审批意见。

@Log(title = "档案销毁", businessType = BusinessType.DELETE) @PostMapping("/destroy") public AjaxResult destroy(@RequestBody ArchiveDestroy destroy) { // 校验是否有销毁权限(通常只有档案管理员有) if (!SecurityUtils.hasPermi("archive:info:destroy")) { return AjaxResult.error("无销毁权限"); } return toAjax(archiveService.destroyArchive(destroy)); }

逻辑说明:@Log注解会把这次操作写进 sys_oper_log,属于系统级审计;业务级的销毁审批则写进自己的 log 表,两者互补。SecurityUtils.hasPermi做按钮级权限校验,权限字符串要和菜单里配置的权限标识一致,否则永远返回无权限。参数说明:businessType = BusinessType.DELETE只是日志分类,销毁在业务上是逻辑删除还是物理删除,取决于你的合规要求——档案行业通常要求逻辑删除并保留原始数据,别直接DELETE FROM。

5. 避坑与排查:档案系统上线前必须过的几道坎

5.1 登录成功却立刻掉线

现象:输入账号密码提示登录成功,页面跳转后马上退回登录页。原因:九成是 Redis 没连上或 token 存不进去。RuoYi 把登录 token 存 Redis,如果 Redis 密码错、端口不通、或者 database 选了个被占用的库,token 写入失败,前端拿不到有效凭证。解决:先redis-cli手动连一下确认能通,再核对application.yml里的 redis 配置,特别注意密码有没有被 yml 的缩进吃掉。还有一种情况是系统时间不对,JWT 的签发时间和校验时间对不上,也会导致 token 立即失效。

5.2 数据权限不生效,越权看到全部档案

现象:给用户配了部门数据权限,但登录后还是能看到所有部门的档案。原因:@DataScope 注解没生效,通常是 Mapper XML 里没有引用params.dataScope,或者deptAlias写错了。解决:检查 XML 的查询语句里有没有${params.dataScope}(注意这里是${}不是#{},因为它是拼接 SQL 片段),再核对注解里的别名和 XML 里表的别名是否一致。另外,超级管理员 admin 默认绕过数据权限,测试时别用 admin 账号验证,要新建一个普通角色用户。

5.3 代码生成后菜单不显示

现象:代码生成的文件都贴进工程了,数据库菜单也插入了,但登录后左侧菜单看不到档案模块。原因:菜单的权限标识、路由地址和前端页面的路径对不上,或者菜单类型配错(目录/菜单/按钮)。解决:进"系统管理 → 菜单管理",确认档案菜单的组件路径和实际 Vue 文件路径一致,比如archive/info/index对应views/archive/info/index.vue。如果是前后端分离版,还要确认路由是否被前端动态路由正确加载,有时候是菜单的"是否缓存""显示状态"配错了。

5.4 档案编号并发重复

现象:压测或多人同时录入时,出现两条档案编号相同的记录。原因:编号生成逻辑是"查最大值 + 1",两个请求同时查到同一个最大值。解决:短期方案是在生成方法上加synchronized或分布式锁,长期方案是给archive_no加唯一索引,插入冲突时捕获异常重试。别指望前端做校验,前端校验只能防手误,防不了并发。

5.5 时间字段差 8 小时

现象:录入的档案时间比实际时间少 8 小时,或者查询时日期对不上。原因:JDBC 连接串没配时区,或者 MySQL 服务端时区和应用不一致。解决:连接串加serverTimezone=GMT%2B8,同时确认 MySQL 的time_zone参数。如果用的是 MySQL 8.0,驱动类名是com.mysql.cj.jdbc.Driver,老驱动类名会报错。这个坑很隐蔽,因为数据能存能取,只是时间不对,容易拖到上线后才被发现。

6. 进阶技巧:用 RuoYi 的扩展点把档案系统做扎实

把基础功能跑通只是及格线,真正让这套源码值钱的是 RuoYi 留出的扩展点。第一个技巧是善用字典和参数配置做业务解耦。档案的密级、状态、分类这些枚举值,别硬编码在 Java 里,全部走sys_dict_type和sys_dict_data,前端用dict标签渲染,后端用DictUtils.getDictLabel转换。这样业务方要加一个密级,改字典就行,不用重新发版。我见过太多项目把状态写死在枚举里,结果每次调整都要改代码、走发布流程,纯属自找麻烦。

第二个技巧是操作日志和业务留痕分离。RuoYi 的@Log注解负责系统级审计,记录谁在什么时候调了哪个接口;档案的借阅、销毁这类业务动作,要单独建表记录业务语义。两者不要混用,系统日志是给运维排查用的,业务留痕是给档案合规检查用的,混在一起查起来很痛苦。我一般会在业务表里冗余操作人姓名和部门,而不是只存 user_id,因为档案记录可能要保存几十年,那时候用户表可能早就变了,冗余字段反而更可靠。

第三个技巧是导出功能。档案系统经常要导出 Excel 或 Word,RuoYi 自带 ExcelUtil,但档案题名、密级这些字段导出时要转成中文标签,别直接导原始值。如果要做 Word 模板导出,常见做法是用 poi-tl 或 freemarker 做模板填充,注意档案数据量大时用 SXSSFWorkbook 流式导出,别用 XSSF 一次性加载,否则几万条档案能把内存撑爆。

扩展点用途注意
字典管理密级、状态、分类枚举别硬编码,改字典即可生效
@DataScope部门数据权限别名要和 XML 对齐,admin 会绕过
@Log系统操作审计和业务留痕分表存
ExcelUtil列表导出大数量用流式,字段要转标签
代码生成器快速生成 CRUD生成后必须手改模糊查询和权限

最后说个验证方法:改完档案模块后,别只用 admin 测。新建一个普通角色用户,配好部门数据权限和菜单权限,用它走一遍"著录 → 借阅 → 归还 → 归档"全流程,再故意用另一个部门的账号登录,确认看不到不属于自己的档案。这套回归流程我每次改权限相关代码都会走一遍,因为权限的 bug 最隐蔽,admin 账号永远测不出来。从那以后我每次动数据权限,都强制用普通账号验证一遍,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询