一、核心架构设计
一个现代化的档案制度,其技术核心是构建一个稳定、可扩展且易于维护的数字化管理架构。本指南将采用“微服务+容器化”的主流架构,确保系统能够随着业务增长而平滑演进。
1.1 技术栈选型与基础环境搭建
我们选择成熟、开源的技术栈,以降低成本和避免供应商锁定。
- 后端: Java 17 + Spring Boot 3.1.5 + MyBatis-Plus 3.5.4。提供稳定的业务逻辑和API接口。
- 前端: Vue 3.3 + TypeScript + Element Plus 2.3.8。构建响应式、用户友好的管理界面。
- 数据库: MySQL 8.0.33。用于存储结构化元数据和业务数据。
- 文件存储: MinIO(兼容S3协议)。用于海量非结构化电子档案文件的存储,支持分布式扩展。
- 容器化: Docker 24.0.5 + Docker Compose v2.20.2。实现环境隔离与一键部署。
使用Docker Compose快速拉起基础服务。在项目根目录创建docker-compose.yml文件:
```
version: '3.8'
services:
mysql:
image: mysql:8.0.33
container_name: archive-mysql
environment:
MYSQL_ROOT_PASSWORD: YourStrong@Passw0rd
MYSQL_DATABASE: archive_db
ports:
- "3306:3306"
volumes:
- ./mysql_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
minio:
image: minio/minio:latest
container_name: archive-minio
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin123
volumes:
- ./minio_data:/data
command: server /data --console-address ":9001"
```
在终端中,进入该文件所在目录,执行:
```
docker-compose up -d
```
执行后,MySQL将在本机3306端口运行,MinIO管理控制台可通过 http://localhost:9001 访问(账号:minioadmin,密码:minioadmin123)。
二、档案元数据模型设计
元数据是描述档案内容、结构、背景和管理过程的数据,是数字化管理的基石。我们设计一个核心的档案条目表。
在MySQL的archive_db数据库中,执行以下SQL创建表:
```
CREATE TABLE `archive_item` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`archive_code` varchar(128) NOT NULL COMMENT '档号,唯一标识',
`title` varchar(512) NOT NULL COMMENT '题名',
`fonds` varchar(256) DEFAULT NULL COMMENT '全宗',
`category` varchar(128) NOT NULL COMMENT '档案门类(如:文书、科技、人事)',
`year` int NOT NULL COMMENT '年度',
`storage_location` varchar(256) DEFAULT NULL COMMENT '物理存放位置',
`digital_file_key` varchar(1024) DEFAULT NULL COMMENT '数字化文件在MinIO中的对象键',
`digital_file_size` bigint DEFAULT '0' COMMENT '数字化文件大小(字节)',
`digital_file_hash` varchar(64) DEFAULT NULL COMMENT '文件哈希值(SHA-256),用于校验完整性',
`security_level` tinyint DEFAULT '0' COMMENT '密级(0:公开,1:内部,2:秘密,3:机密)',
`retention_period` int DEFAULT NULL COMMENT '保管期限(年)',
`description` text COMMENT '备注描述',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_archive_code` (`archive_code`),
KEY `idx_category_year` (`category`,`year`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='档案条目核心表';
```
三、核心服务实现
3.1 文件上传与存储服务
此服务负责将纸质档案扫描件或其他电子文件安全、可靠地上传至MinIO对象存储,并记录元数据。
在Spring Boot项目的pom.xml中添加MinIO依赖:
```
io.minio
minio
8.5.4
```
创建MinIO配置类MinioConfig.java:
```
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
```
在application.yml中配置:
```
minio:
endpoint: http://localhost:9000
accessKey: minioadmin
secretKey: minioadmin123
bucket-name: archive-bucket
```
创建文件上传服务FileStorageService.java的核心方法:
```
import io.minio.;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
@Service
public class FileStorageService {
@Autowired
private MinioClient minioClient;
@Value("${minio.bucket-name}")
private String bucketName;
public String uploadFile(MultipartFile file, String archiveCode) throws Exception {
// 1. 确保存储桶存在
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!found) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
// 2. 生成唯一对象键:档号+时间戳+文件后缀
String originalFilename = file.getOriginalFilename();
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
String objectKey = String.format("%s/%s_%d%s", archiveCode.substring(0, 4), archiveCode, System.currentTimeMillis(), fileExtension);
// 3. 计算文件哈希值
InputStream fileStream = file.getInputStream();
String fileHash = DigestUtils.sha256Hex(fileStream);
fileStream.close(); // 关闭流,准备重新读取
// 4. 上传到MinIO
try (InputStream uploadStream = file.getInputStream()) {
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectKey)
.stream(uploadStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
}
// 5. 返回文件存储信息(对象键和哈希值)
return objectKey + "|" + fileHash;
}
}
```
3.2 档案条目管理API

创建RESTful API控制器,实现档案条目的增删改查,并与文件上传服务联动。
创建ArchiveItemController.java:
```
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/api/archive")
public class ArchiveItemController {
@Autowired
private ArchiveItemService itemService;
@Autowired
private FileStorageService fileStorageService;
@PostMapping("/create")
public Result createItem(@RequestPart ArchiveItemDTO itemDTO,
@RequestPart(value = "file", required = false) MultipartFile file) {
try {
// 1. 保存档案条目基础信息
ArchiveItem item = convertToEntity(itemDTO);
itemService.save(item);
// 2. 如果上传了文件,则处理文件存储
if (file != null && !file.isEmpty()) {
String storageResult = fileStorageService.uploadFile(file, item.getArchiveCode());
String[] parts = storageResult.split("\\|");
item.setDigitalFileKey(parts[0]);
item.setDigitalFileHash(parts[1]);
item.setDigitalFileSize(file.getSize());
itemService.updateById(item);
}
return Result.success("档案创建成功");
} catch (Exception e) {
return Result.error("创建失败:" + e.getMessage());
}
}
@GetMapping("/search")
public Result searchItems(@RequestParam(required = false) String keyword,
@RequestParam(required = false) String category,
@RequestParam(required = false) Integer year) {
// 构建查询条件,使用MyBatis-Plus的LambdaQueryWrapper
LambdaQueryWrapper
wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(keyword)) {
wrapper.like(ArchiveItem::getTitle, keyword).or().like(ArchiveItem::getArchiveCode, keyword);
}
if (StringUtils.hasText(category)) {
wrapper.eq(ArchiveItem::getCategory, category);
}
if (year != null) {
wrapper.eq(ArchiveItem::getYear, year);
}
wrapper.orderByDesc(ArchiveItem::getCreateTime);
List list = itemService.list(wrapper);
return Result.success(list);
}
}
```
四、前端管理界面快速搭建
使用Vue 3和Element Plus快速构建一个档案管理界面。
安装依赖:
```
npm install element-plus @element-plus/icons-vue
```
在src/main.js中全局引入:
```
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
app.use(ElementPlus)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('app')
```
创建档案列表组件ArchiveList.vue的核心部分:
```
查询
重置
{{ getSecLevelText(scope.row.securityLevel) }}
查看
下载
```
五、安全与权限控制
档案系统必须包含严格的安全控制。我们使用Spring Security实现基于角色的访问控制(RBAC)。
在pom.xml中添加依赖:
```
org.springframework.boot
spring-boot-starter-security
io.jsonwebtoken
jjwt-api
0.11.5
```
创建安全配置类SecurityConfig.java,核心是配置URL权限:
```
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable() // 根据实际情况选择开启或关闭
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/auth/").permitAll() // 认证接口公开
.requestMatchers("/api/archive/search").hasAnyRole("USER", "ADMIN") // 搜索需用户角色
.requestMatchers(HttpMethod.POST, "/api/archive/").hasRole("ADMIN") // 增删改需管理员
.anyRequest().authenticated()
)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 使用JWT,无状态
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
```
为档案实体添加访问日志注解,实现操作留痕:
```
@Aspect
@Component
public class ArchiveAccessLogAspect {
@Autowired
private HttpServletRequest request;
@AfterReturning(pointcut = "@annotation(com.yourpackage.annotation.AccessLog)", returning = "result")
public void logAfter(JoinPoint joinPoint, Object result) {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
String method = request.getMethod();
String uri = request.getRequestURI();
String ip = request.getRemoteAddr();
// 将日志(用户名、时间、IP、操作、结果)异步写入数据库或日志文件
log.info("用户[{}] IP[{}] 执行了[{} {}],结果: {}", username, ip, method, uri, result);
}
}
```
六、自动化与持续集成
使用GitHub Actions实现代码提交后自动构建、测试和部署。
在项目根目录创建.github/workflows/ci-cd.yml:
```
name: Archive System CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Run Tests
run: mvn test
- name: Build Docker Image
run: |
docker build -t your-registry/archive-system:${{ github.sha }} .
docker push your-registry/archive-system:${{ github.sha }}
- name: Deploy to Server (via SSH)
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
script: |
cd /opt/archive-system
docker-compose pull
docker-compose up -d --force-recreate backend
```
同时,在项目根目录创建Dockerfile用于构建后端镜像:
```
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
```
通过以上六个部分的详细步骤,一个具备现代化特征的档案管理系统核心框架即可搭建完成。此系统具备了元数据管理、数字化文件存储、安全权限控制、操作