档案管理系统最新规定下的技术落地实操指南

一、环境准备与核心依赖配置

根据最新档案管理规范,系统必须支持OFD格式、具备不可篡改的审计日志以及三员管理权限控制。本指南基于Spring Boot 3.x + Vue 3技术栈,确保所有操作可直接落地。

在服务器端安装必要的文档转换工具。最新规定要求电子档案长期保存格式需符合国家标准,通常依赖LibreOffice进行格式转换。

1. 安装LibreOffice(用于Office转OFD/PDF)

在CentOS/Ubuntu系统下执行以下命令:

 Ubuntu/Debian
sudo apt-get update
sudo apt-get install libreoffice
CentOS/RHEL
sudo yum install libreoffice

2. Maven项目核心依赖(pom.xml)

引入文件处理、安全框架及OFD处理相关的依赖。请确保版本号兼容性。




org.springframework.boot
spring-boot-starter-web



org.springframework.boot
spring-boot-starter-security



org.ofdrw
ofdrw-full
2.0.0



com.itextpdf
itext7-core
7.2.5
pom



org.mybatis.spring.boot
mybatis-spring-boot-starter
3.0.3


二、数据库表结构设计(符合元数据标准)

最新规定要求档案元数据必须包含特定的业务实体标识和保管期限字段。以下是核心档案表的建表SQL,直接在MySQL 8.0+中执行。

档案管理系统最新规定下的技术落地实操指南

CREATE DATABASE IF NOT EXISTS archive_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE archive_db;
-- 档案主表
CREATE TABLE `t_archive_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`archive_code` varchar(64) NOT NULL COMMENT '档号(唯一标识)',
`title` varchar(255) NOT NULL COMMENT '题名',
`file_path` varchar(512) NOT NULL COMMENT '物理存储路径',
`file_format` varchar(10) NOT NULL COMMENT '文件格式:OFD/PDF/XML',
`retention_period` varchar(20) NOT NULL COMMENT '保管期限:永久/30年/10年',
`security_level` varchar(10) NOT NULL COMMENT '密级:公开/内部/机密',
`create_by` varchar(64) NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '归档时间',
`digest_hash` varchar(64) NOT NULL COMMENT '文件摘要(SHA-256),用于防篡改',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_archive_code` (`archive_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='档案管理主表';
-- 不可篡改审计日志表(建议追加写入,限制Update权限)
CREATE TABLE `t_audit_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(64) NOT NULL,
`operation` varchar(50) NOT NULL COMMENT '操作类型:UPLOAD/VIEW/DOWNLOAD',
`target_id` bigint(20) NOT NULL COMMENT '操作对象ID',
`ip_address` varchar(50) NOT NULL,
`operation_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`result` varchar(20) NOT NULL COMMENT 'SUCCESS/FAILURE',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表';

三、实现不可篡改的审计日志AOP

最新规定强调“四性”检测,其中真实性依赖于日志的不可篡改。使用Spring AOP将关键操作日志记录到数据库或独立的日志文件中。以下为切面实现代码。

1. 定义自定义注解

package com.archive.annotation;
import java.lang.annotation.;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ArchiveLog {
String operation() default "";
}

2. 实现AOP切面类

package com.archive.aspect;
import com.archive.annotation.ArchiveLog;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
@Aspect
@Component
public class AuditLogAspect {
private static final Logger logger = LoggerFactory.getLogger("AUDIT_LOG");
private final ObjectMapper objectMapper = new ObjectMapper();
@Around("@annotation(com.archive.annotation.ArchiveLog)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
ArchiveLog archiveLog = method.getAnnotation(ArchiveLog.class);
long startTime = System.currentTimeMillis();
Object result = null;
String status = "FAILURE";
try {
result = joinPoint.proceed();
status = "SUCCESS";
return result;
} finally {
long duration = System.currentTimeMillis() - startTime;
String logJson = buildLogJson(request, archiveLog.operation(), joinPoint.getArgs(), status, duration);
// 强制写入日志文件,实际项目中应配合Logback配置单独的Appender
logger.info(logJson);
}
}
private String buildLogJson(HttpServletRequest request, String operation, Object[] args, String status, long duration) {
try {
// 简化的日志结构,实际应包含用户ID、IP等
return objectMapper.writeValueAsString(new AuditLogEntry(
request.getRemoteAddr(),
operation,
status,
System.currentTimeMillis(),
duration
));
} catch (Exception e) {
return "{}";
}
}
record AuditLogEntry(String ip, String operation, String status, long timestamp, long duration) {}
}

四、OFD文件转换与预览服务实现

根据规定,电子公文归档必须转换为OFD版式文件。以下提供将上传的文件转换为OFD的核心工具类。

package com.archive.service;
import org.ofdrw.converter.OfdConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Service
public class OfdConversionService {
@Value("${archive.storage.path:/data/archive}")
private String storagePath;
/
将上传的文件转换为OFD并存储
@param sourcePath 源文件路径
@return OFD文件存储路径
/
public String convertToOfd(String sourcePath) throws IOException {
Path source = Paths.get(sourcePath);
if (!Files.exists(source)) {
throw new IOException("源文件不存在");
}
String ofdFileName = source.getFileName().toString().replaceAll("\\.[^.]+$", ".ofd");
Path targetDir = Paths.get(storagePath, "ofd");
if (!Files.exists(targetDir)) {
Files.createDirectories(targetDir);
}
Path targetPath = targetDir.resolve(ofdFileName);
// 使用 Ofdrw 工具进行转换
// 注意:实际生产环境建议使用异步线程池处理转换,避免阻塞主线程
try {
// 如果是PDF转OFD
if (sourcePath.toLowerCase().endsWith(".pdf")) {
OfdConverter.converter()
.from(source.toFile())
.to(targetPath.toFile())
.convert();
} else {
// 其他格式(如Word/Excel)建议先通过LibreOffice转PDF,再转OFD
// 此处省略中间步骤,直接抛出异常提示需先转PDF
throw new UnsupportedOperationException("暂不支持直接转换,请先转为PDF");
}
} catch (Exception e) {
throw new IOException("OFD转换失败: " + e.getMessage(), e);
}
return targetPath.toString();
}
}

五、实现动态水印(防泄漏)

最新规定要求对敏感档案进行在线浏览时必须叠加包含用户信息的动态水印。以下是基于iText 7对PDF添加水印的工具类(OFD同理需使用OFD SDK)。

package com.archive.util;
import com.itextpdf.kernel.color.Color;
import com.itextpdf.kernel.color.DeviceRgb;
import com.itextpdf.kernel.pdf.;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import java.io.FileOutputStream;
import java.io.IOException;
public class WatermarkUtil {
public static void addTextWatermark(String srcPdf, String destPdf, String watermarkText) throws IOException {
PdfReader reader = new PdfReader(srcPdf);
PdfWriter writer = new PdfWriter(destPdf);
PdfDocument pdfDoc = new PdfDocument(reader, writer);
int pageCount = pdfDoc.getNumberOfPages();
// 设置水印样式
DeviceRgb fontColor = new DeviceRgb(200, 200, 200); // 浅灰色
for (int i = 1; i <= pageCount; i++) {
PdfPage page = pdfDoc.getPage(i);
PdfCanvas canvas = new PdfCanvas(page);
Rectangle pageSize = page.getPageSize();
// 在页面中心绘制水印
Canvas layoutCanvas = new Canvas(canvas, pageSize);
float x = pageSize.getWidth() / 2;
float y = pageSize.getHeight() / 2;
Paragraph p = new Paragraph(watermarkText)
.setFontColor(fontColor)
.setFontSize(40)
.setTextAlignment(TextAlignment.CENTER)
.setMargin(0);
// 旋转水印45度
layoutCanvas.showTextAligned(p, x, y, i, TextAlignment.CENTER,
com.itextpdf.layout.property.VerticalAlignment.MIDDLE,
(float) Math.toRadians(45));
}
pdfDoc.close();
}
}

六、三员管理权限配置

规定要求系统管理员、安全保密员、安全审计员权限必须分离。以下是基于Spring Security的配置片段。

package com.archive.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// 系统管理员:只能配置系统,不能看档案
.requestMatchers("/system/").hasRole("ADMIN")
// 安全保密员:管理档案元数据,不能操作日志
.requestMatchers("/archive/").hasRole("SECRETARY")
// 安全审计员:只能查看日志
.requestMatchers("/audit/").hasRole("AUDITOR")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
// 实际生产中请从数据库加载用户
UserDetails admin = User.withUsername("admin")
.password("{noop}admin123")
.roles("ADMIN")
.build();
UserDetails secretary = User.withUsername("secretary")
.password("{noop}sec123")
.roles("SECRETARY")
.build();
UserDetails auditor = User.withUsername("auditor")
.password("{noop}audit123")
.roles("AUDITOR")
.build();
return new InMemoryUserDetailsManager(admin, secretary, auditor);
}
}

七、文件上传与归档Controller

将上述组件串联起来,提供一个完整的文件上传入口。该接口包含文件接收、格式转换、水印处理及日志记录。

package com.archive.controller;
import com.archive.annotation.ArchiveLog;
import com.archive.service.OfdConversionService;
import com.archive.util.WatermarkUtil;
import org.springframework.beans.factory.annotation.Autowired;
org.springframework.web.bind.annotation.;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@RestController
@RequestMapping("/archive")
public class ArchiveController {
@Autowired
private OfdConversionService ofdConversionService;
@PostMapping("/upload")
@ArchiveLog(operation = "UPLOAD_ARCHIVE")
public String uploadArchive(@RequestParam("file") MultipartFile file) {
try {
// 1. 保存原始文件
Path tempDir = Files.createTempDirectory("archive_upload");
File originalFile = tempDir.resolve(file.getOriginalFilename()).toFile();
file.transferTo(originalFile);
// 2. 转换为OFD
String ofdPath = ofdConversionService.convertToOfd(originalFile.getAbsolutePath());
// 3. 对预览用的PDF副本添加水印(假设ofdPath是生成的OFD,此处演示逻辑)
// 实际业务中,原始文件归档保存,预览文件另存并加水印
// String previewPath = ...;
// WatermarkUtil.addTextWatermark(ofdPath, previewPath, "内部资料 - " + SecurityContextHolder.getContext().getAuthentication().getName());
// 4. 保存元数据到数据库(省略DAO调用)
// archiveDao.save(...);
return "归档成功,OFD路径: " + ofdPath;
} catch (Exception e) {
return "归档失败: " + e.getMessage();
}
}
}
AI咨询
热线电话

028-85154420

15388110056

全国售前咨询电话

扫码咨询
安答联动微信公众号二维码

微信扫码关注安答联动

申请试用
热线电话
申请试用

安答联动档案管理系统