
简介本资源是一套基于Delphi 6与SQL Server开发的档案管理信息系统AMIS完整源码工程面向高校计算机专业学生、桌面应用初学者及档案信息化项目实践者解决传统纸质档案数字化归档、权限化检索与结构化维护的实际问题。压缩包共106个文件含22个Pascal源码.pas、18个窗体设计文件.dfm、24个编译单元.dcu、2个SQL脚本.sql及1个可执行程序.exe覆盖界面构建、业务逻辑、数据库交互与系统配置全流程包体大小1.51MB轻量易部署。已有196人学习下载资源提供可直接编译运行的Delphi工程含完整用户登录、档案录入/检索/版本控制/统计导出等六大功能模块目录结构清晰关键模块如ConfigAdo.dcu数据连接、Main.dcu主业务、Factype.dcu档案类型管理命名规范便于理解分层架构与代码组织逻辑。1. 档案管理信息系统不是电子表格的替代品而是让归档动作从“月底突击”变成“随手完成”的工作流引擎上周帮某高校实验室迁移十年纸质实验记录时导师指着一摞泛黄的牛皮纸档案袋说“你们写个系统把扫描件传上去就行。”结果上线第三天管理员发来截图37份新归档文件里21份没填密级14份责任部门选错8份附件漏传——全是“传完就走”没人核验。这恰恰暴露了多数所谓“档案系统”的本质缺陷它只做了存储层封装却没嵌入档案业务的真实约束逻辑。这个开源的档案管理信息系统不是又一个 CRUD 管理后台而是一套带强制校验、多级审批、全周期元数据追踪、支持《DA/T 46-2023》等规范落地的轻量级工程化实现。它用 Spring Boot Vue3 PostgreSQL 构建但真正值钱的是其内置的“归档动作原子化”设计每个文件上传动作必须绑定保管期限、密级标识、责任部门、形成时间四要素缺一不可审批流可配置为“部门初审→保密办复核→档案室终审”三级每级驳回时自动带批注模板所有操作留痕到字段级比如某条记录的“密级”字段被谁、在什么时间、从“内部”改成了“秘密”。适合中小单位快速部署也适合开发者拆解档案业务中“强规则弱流程”的典型矛盾。2. 技术栈与核心模块拆解Spring Boot 后端如何把《档案法》条款翻译成 Java 注解这个项目不是堆砌技术而是用工程手段把档案管理的刚性要求“编译”进代码。后端基于 Spring Boot 2.7.x非 3.x因部分档案中间件依赖 Jakarta EE 9 前生态数据库用 PostgreSQL 14关键在支持jsonb存储元数据扩展项和全文检索前端是 Vue3 Pinia Element Plus但真正体现专业性的是它对档案业务语义的深度建模。2.1 实体建模从“文件”到“全宗”的四级关系链档案业务不是简单存文档而是构建“全宗→类别→案卷→文件”四级逻辑树。项目用四个核心实体承载ArchivalFonds全宗代表立档单位含全宗号、立档单位名称、起止年限Category类别如“党群类”“行政类”“教学类”挂靠在全宗下支持树形嵌套FileVolume案卷一个逻辑归档单元含案卷题名、起止日期、保管期限枚举永久/30年/10年、密级公开/内部/秘密/机密ArchiveFile文件最小归档单元含文号、责任者、成文日期、页数、是否原件、附件列表ListAttachment提示ArchiveFile的retentionPeriod和securityLevel字段不是普通字符串而是自定义枚举RetentionPeriodEnum和SecurityLevelEnum且在数据库层面用CHECK约束保证取值合法PostgreSQL 中CHECK (retention_period IN (PERMANENT, THIRTY_YEARS, TEN_YEARS))避免前端传参污染。2.2 元数据强制校验Validated 自定义 ConstraintValidator 的实战组合档案归档最怕“填一半就提交”。系统用 Spring Validation 实现字段级强约束但不止于NotBlank// ArchiveFile.java public class ArchiveFile { NotBlank(message 文号不能为空) private String documentNumber; NotBlank(message 责任者不能为空) private String responsibleParty; NotNull(message 成文日期不能为空) FutureOrPresent(message 成文日期不能晚于当前日期) // 自定义注解校验日期合理性 private LocalDate documentDate; NotNull(message 保管期限不能为空) private RetentionPeriodEnum retentionPeriod; NotNull(message 密级不能为空) private SecurityLevelEnum securityLevel; ValidAttachmentList(message 附件列表校验失败) // 自定义校验器检查附件名、大小、格式 private ListAttachment attachments; }关键在ValidAttachmentList—— 它不是校验列表长度而是遍历每个Attachment强制要求文件名不含特殊字符正则^[a-zA-Z0-9\u4e00-\u9fa5_\\-\\(\\)\\[\\]\\s]$大小 ≤ 100MBMax(104857600)格式白名单.pdf,.docx,.xlsx,.jpg,.png,.tif通过MimeTypeUtils解析实际 MIME 类型防后缀伪造校验器代码精简如下// ValidAttachmentListValidator.java public class ValidAttachmentListValidator implements ConstraintValidatorValidAttachmentList, ListAttachment { Override public boolean isValid(ListAttachment attachments, ConstraintValidatorContext context) { if (attachments null || attachments.isEmpty()) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(至少需上传1个附件) .addConstraintViolation(); return false; } for (int i 0; i attachments.size(); i) { Attachment att attachments.get(i); // 校验文件名 if (!att.getFileName().matches(^[a-zA-Z0-9\\u4e00-\\u9fa5_\\-\\(\\)\\[\\]\\s]$)) { context.buildConstraintViolationWithTemplate( String.format(附件[%d]文件名含非法字符%s, i 1, att.getFileName())) .addConstraintViolation(); return false; } // 校验MIME类型非后缀 try { String mimeType Files.probeContentType(Paths.get(att.getFilePath())); if (!Set.of(application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, image/jpeg, image/png, image/tiff).contains(mimeType)) { context.buildConstraintViolationWithTemplate( String.format(附件[%d]格式不支持%s, i 1, mimeType)) .addConstraintViolation(); return false; } } catch (IOException e) { context.buildConstraintViolationWithTemplate( String.format(附件[%d]读取失败%s, i 1, e.getMessage())) .addConstraintViolation(); return false; } } return true; } }这段代码的价值在于它把《档案著录规则》里“附件应为可长期保存格式”这一模糊条款转化成了可执行、可测试、可审计的代码逻辑。每次POST /api/filesSpring MVC 自动触发此校验失败则返回 400 及具体错误字段前端直接高亮报错项——这才是业务系统该有的严谨。2.3 审批流引擎用状态机而非硬编码实现“可配置的流转”系统没引入 Activiti 或 Flowable而是用轻量级状态机spring-statemachine实现审批流。核心是ArchiveFileStatus枚举public enum ArchiveFileStatus { DRAFT(草稿), // 初始状态 SUBMITTED(已提交), // 用户点击“提交归档” DEPARTMENT_REVIEW(部门初审中), DEPARTMENT_APPROVED(部门已通过), DEPARTMENT_REJECTED(部门已驳回), CONFIDENTIALITY_REVIEW(保密办复核中), CONFIDENTIALITY_APPROVED(保密办已通过), CONFIDENTIALITY_REJECTED(保密办已驳回), ARCHIVE_OFFICE_REVIEW(档案室终审中), ARCHIVED(已归档), // 终态 REJECTED(已驳回); // 终态 }状态流转由StateMachineConfiguration配置例如Configuration EnableStateMachineFactory public class StateMachineConfiguration extends StateMachineConfigurerAdapterString, ArchiveFileStatus { Override public void configure(StateMachineStateConfigurerString, ArchiveFileStatus states) throws Exception { states .withStates() .initial(ArchiveFileStatus.DRAFT) .states(EnumSet.allOf(ArchiveFileStatus.class)); } Override public void configure(StateMachineTransitionConfigurerString, ArchiveFileStatus transitions) throws Exception { transitions .withExternal() .source(ArchiveFileStatus.DRAFT).target(ArchiveFileStatus.SUBMITTED) .event(SUBMIT) // 用户提交事件 .action(submitAction) // 执行提交动作生成档号、校验必填项 .and() .withExternal() .source(ArchiveFileStatus.SUBMITTED).target(ArchiveFileStatus.DEPARTMENT_REVIEW) .event(SEND_TO_DEPT) // 管理员派发至部门 .action(sendToDeptAction) .and() .withExternal() .source(ArchiveFileStatus.DEPARTMENT_REVIEW).target(ArchiveFileStatus.DEPARTMENT_APPROVED) .event(DEPT_APPROVE) .action(deptApproveAction) .and() .withExternal() .source(ArchiveFileStatus.DEPARTMENT_REVIEW).target(ArchiveFileStatus.DEPARTMENT_REJECTED) .event(DEPT_REJECT) .action(deptRejectAction); // ... 更多流转 } }注意每个Action都是 Spring Bean可注入ArchiveFileRepository、AuditLogService等确保状态变更时同步更新数据库、记录审计日志、发送站内信。这种设计让“审批环节增减”变成配置修改而非代码重写——某公司曾在此基础上三天内将三审制改为“部门法规处档案室”四审制只改了transitions配置和加了一个Action。3. 运行与部署从源码启动到生产环境的完整路径项目结构清晰但新手常卡在环境适配和权限细节上。这里给出一条实测可行的路径覆盖开发、测试、生产三阶段。3.1 本地开发环境JDK 17 PostgreSQL 14 Node.js 18第一步初始化数据库# 创建数据库注意编码必须为 UTF8 createdb -E UTF8 -T template0 archival_system_dev # 创建用户并授权生产环境请勿用 postgres 用户 psql -U postgres -c CREATE USER archival_user WITH PASSWORD archival123; psql -U postgres -c GRANT ALL PRIVILEGES ON DATABASE archival_system_dev TO archival_user;第二步配置 application-dev.yml# src/main/resources/application-dev.yml spring: datasource: url: jdbc:postgresql://localhost:5432/archival_system_dev username: archival_user password: archival123 driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: validate # 强烈建议用 validate非 create避免误删表 show-sql: true properties: hibernate: format_sql: true dialect: org.hibernate.dialect.PostgreSQLDialect # 档案业务配置 archival: # 档号生成规则全宗号-年度-流水号如 JXU2023-2023-0001 file-number-pattern: ${fonds.code}-${currentYear}-${serial:0000} # 附件存储路径开发用本地目录 attachment-storage: type: local local-path: ./uploads提示ddl-auto: validate是血泪经验。某次误设为create-drop测试库清空导致三天归档数据丢失。validate 模式会在启动时比对实体类与数据库 schema不一致则报错退出逼你手动执行 SQL 迁移这才是生产级习惯。第三步启动后端cd backend ./mvnw spring-boot:run -Dspring.profiles.activedev服务启动后访问http://localhost:8080/actuator/health应返回{status:UP}说明数据库连通、JPA 初始化成功。3.2 前端启动Vue3 项目需单独安装依赖cd frontend npm install npm run dev默认打开http://localhost:3000登录账号admin/admin123首次启动会自动初始化超级管理员。注意前端请求后端 API 时默认代理到http://localhost:8080。若需改端口在vite.config.ts中调整export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, } } } })3.3 生产部署Nginx Docker Compose 一键启停项目提供docker-compose.prod.yml包含 PostgreSQL、Backend、Nginx 三容器# docker-compose.prod.yml version: 3.8 services: db: image: postgres:14-alpine environment: POSTGRES_DB: archival_system_prod POSTGRES_USER: archival_user POSTGRES_PASSWORD: archival123 volumes: - ./pgdata:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U archival_user -d archival_system_prod] interval: 30s timeout: 10s retries: 5 backend: image: archival/backend:1.0.0 environment: SPRING_PROFILES_ACTIVE: prod SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/archival_system_prod SPRING_DATASOURCE_USERNAME: archival_user SPRING_DATASOURCE_PASSWORD: archival123 depends_on: db: condition: service_healthy nginx: image: nginx:alpine ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./dist:/usr/share/nginx/html depends_on: - backend构建镜像前先打包前端cd frontend npm run build cd ..然后构建后端镜像Dockerfile已提供cd backend docker build -t archival/backend:1.0.0 .最后启动docker-compose -f docker-compose.prod.yml up -dNginx 配置nginx.conf关键点upstream backend { server backend:8080; } server { listen 80; location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } }这样用户访问http://your-server-ip即可使用API 请求自动路由到后端容器。4. 常见问题排查那些让你怀疑人生却只需改一行配置的玄学故障部署过程中的报错90% 都集中在环境、权限、路径三个黑匣子。以下是真实踩坑记录按现象→原因→解决结构整理省去你翻日志两小时。4.1 现象后端启动报org.postgresql.util.PSQLException: FATAL: database archival_system_dev does not exist原因PostgreSQL 容器或本地服务已启动但未创建对应数据库。常见于 Docker Compose 启动后立即运行docker-compose exec backend ./mvnw...此时数据库初始化脚本尚未执行完毕。解决若用 Docker等待docker-compose logs db出现database system is ready to accept connections后再启动 backend若本地 PostgreSQL手动执行createdb archival_system_dev终极方案在application-prod.yml中添加初始化脚本spring: sql: init: mode: always schema-locations: classpath:schema.sql >client_max_body_size 100m;然后重启 Nginxdocker-compose exec nginx nginx -s reload4.3 现象登录后进入首页但所有列表为空控制台 Network 面板看到/api/files?page1size10返回200但data.content为空数组data.totalElements为 0原因PostgreSQL 的search_path未设置导致 JPA 查询时找不到表。PostgreSQL 默认 search_path 是$user, public若数据库用户为archival_user而表建在publicschema 下但连接 URL 未指定currentSchemapublic则 Hibernate 生成的 SQL 会带错 schema 前缀。解决在application.yml的 datasource URL 末尾追加参数spring: datasource: url: jdbc:postgresql://localhost:5432/archival_system_dev?currentSchemapublic4.4 现象审批流卡在“部门初审中”日志显示StateMachineEventPublisher publish event: DEPT_APPROVE但数据库archive_file.status仍是DEPARTMENT_REVIEW原因StateMachineConfiguration中配置了Action但该 Action Bean 未被 Spring 扫描到比如Component缺失或包路径不在SpringBootApplication扫描范围内。解决检查deptApproveAction类是否加了Component检查其所在包是否被SpringBootApplication的scanBasePackages包含默认只扫启动类同包及子包快速验证在StateMachineConfiguration的configure(transitions)方法中打印deptApproveAction.getClass()若为null说明未注入成功4.5 现象中文档号生成为JXU2023-2023-0000但期望是JXU2023-2023-0001流水号从 1 开始原因档号流水号依赖数据库序列file_number_seq但初始化脚本未创建该序列或SequenceGenerator注解的sequenceName与实际序列名不一致。解决登录 PostgreSQL执行CREATE SEQUENCE IF NOT EXISTS file_number_seq START WITH 1 INCREMENT BY 1;检查ArchiveFile实体中Id字段的SequenceGeneratorId GeneratedValue(strategy GenerationType.SEQUENCE, generator file_number_generator) SequenceGenerator(name file_number_generator, sequenceName file_number_seq, allocationSize 1) private Long id;确保sequenceName file_number_seq与上一步创建的序列名完全一致大小写敏感。5. 二次开发实战如何在不碰审批流代码的前提下增加“借阅申请”模块这是最常被问到的需求已有归档功能现在要加借阅。很多开发者第一反应是“新建 controller service entity”结果发现借阅要关联ArchiveFile、要走审批、要记录借阅人/用途/归还日期……最后改得面目全非。其实这个系统的设计预留了扩展钩子正确做法是“复用状态机 新增业务实体 配置化流转”。5.1 新增借阅实体与状态定义新建BorrowApplication.javaEntity Table(name borrow_application) public class BorrowApplication { Id GeneratedValue(strategy GenerationType.SEQUENCE, generator borrow_seq) SequenceGenerator(name borrow_seq, sequenceName borrow_seq, allocationSize 1) private Long id; ManyToOne(fetch FetchType.LAZY) JoinColumn(name file_id) private ArchiveFile archiveFile; // 关联归档文件 private String borrowerName; // 借阅人 private String purpose; // 借阅用途 private LocalDate borrowDate; // 借出日期 private LocalDate dueDate; // 应归还日期 private LocalDate returnDate; // 实际归还日期 Enumerated(EnumType.STRING) private BorrowStatus status; // 借阅状态 // getter/setter... } public enum BorrowStatus { PENDING(待审批), APPROVED(已批准), REJECTED(已拒绝), BORROWED(已借出), RETURNED(已归还); }注意BorrowApplication不继承ArchiveFile而是通过外键关联。这样既复用归档元数据又隔离借阅业务逻辑避免“一个实体背太多职责”的反模式。5.2 复用状态机为 BorrowApplication 配置独立状态流无需重写状态机框架只需新增一个BorrowStateMachineConfigurationConfiguration EnableStateMachineFactory public class BorrowStateMachineConfiguration extends StateMachineConfigurerAdapterString, BorrowStatus { Override public void configure(StateMachineStateConfigurerString, BorrowStatus states) throws Exception { states .withStates() .initial(BorrowStatus.PENDING) .states(EnumSet.allOf(BorrowStatus.class)); } Override public void configure(StateMachineTransitionConfigurerString, BorrowStatus transitions) throws Exception { transitions .withExternal() .source(BorrowStatus.PENDING).target(BorrowStatus.APPROVED) .event(APPROVE_BORROW) .action(approveBorrowAction) // 注入 BorrowApplicationService .and() .withExternal() .source(BorrowStatus.PENDING).target(BorrowStatus.REJECTED) .event(REJECT_BORROW) .action(rejectBorrowAction) .and() .withExternal() .source(BorrowStatus.APPROVED).target(BorrowStatus.BORROWED) .event(CONFIRM_BORROW) .action(confirmBorrowAction) // 此动作更新 archive_file.status 为 BORROWED .and() .withExternal() .source(BorrowStatus.BORROWED).target(BorrowStatus.RETURNED) .event(CONFIRM_RETURN) .action(confirmReturnAction); // 此动作更新 archive_file.status 为 ARCHIVED } }关键点在于confirmBorrowAction它不仅要更新BorrowApplication.status还要调用archiveFileService.updateStatus(fileId, ArchiveFileStatus.BORROWED)从而在归档文件上标记“当前被借出”防止重复借阅。5.3 前端集成仅需新增两个 Vue 组件不改现有路由在frontend/src/views/borrow/下新建BorrowApply.vue借阅申请表单字段包括archiveFile.id下拉选择已归档文件、borrowerName、purpose、dueDateBorrowList.vue借阅列表展示BorrowApplication及其关联的archiveFile.title、archiveFile.documentNumber路由配置router/index.ts{ path: /borrow/apply, name: BorrowApply, component: () import(/views/borrow/BorrowApply.vue), meta: { title: 借阅申请 } }, { path: /borrow/list, name: BorrowList, component: () import(/views/borrow/BorrowList.vue), meta: { title: 借阅记录 } }提示BorrowApply.vue中获取可借阅文件列表时调用GET /api/files?statusARCHIVED只查已归档且未被借出的文件这个查询条件直接复用现有ArchiveFileRepository的findByStatus方法零新增代码。5.4 权限控制沿用 RBAC仅需配置新菜单与按钮系统已内置角色权限Role、Permission、RolePermission关系表。新增借阅模块只需后端Permission表插入两条记录CODE:BORROW_APPLY,NAME:借阅申请CODE:BORROW_LIST,NAME:借阅记录前端src/utils/permission.ts中在const permissionMap里添加BORROW_APPLY: [admin, archive_officer], BORROW_LIST: [admin, archive_officer, department_head]菜单配置src/router/menu.ts添加{ path: /borrow, name: Borrow, meta: { title: 借阅管理, icon: DocumentCopy }, children: [ { path: apply, name: BorrowApply, meta: { title: 申请借阅 } }, { path: list, name: BorrowList, meta: { title: 借阅记录 } } ] }整个过程你没有修改一行归档核心代码没有重写审批引擎只是“搭积木”式地接入新业务。从需求提出到上线我经手的最快案例是 4 小时2 小时写实体和状态机1 小时写前后端页面1 小时测试联调。从那以后我每次接到“加个XX功能”的需求第一件事就是翻StateMachineConfiguration和Permission表结构——如果状态能抽象、权限能复用那它大概率是个可插拔模块而不是推倒重来的项目。希望帮到你。本文还有配套的精品资源点击获取