一、核心需求分析与技术选型
集团档案管理面临档案分散、格式不一、检索困难、权限复杂、安全要求高等核心痛点。本方案旨在构建一个统一、安全、高效的数字化档案中心。
1.1 基础技术栈确定
基于稳定性、可扩展性及开源生态,选择以下技术栈:
- 后端:Java 17 + Spring Boot 3.x。提供成熟的企业级开发框架和稳定的运行环境。
- 前端:Vue 3 + Element Plus。构建响应式管理界面,提升开发效率。
- 数据库:PostgreSQL 15。强大的关系型数据库,支持JSON类型,适合存储结构化元数据。
- 文件存储:MinIO。高性能、兼容S3协议的对象存储,用于存储档案原文。
- 搜索引擎:Elasticsearch 8.x。提供档案内容的全文检索和高性能查询。
- 工作流引擎:Flowable。处理档案的归档、借阅、审批等流程。
二、基础环境与依赖部署
2.1 使用Docker Compose一键部署基础设施
在服务器上创建docker-compose.yml文件,内容如下:
```
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: archive-postgres
environment:
POSTGRES_DB: archive_db
POSTGRES_USER: archive_admin
POSTGRES_PASSWORD: YourStrongPassword123!
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
restart: unless-stopped
minio:
image: minio/minio:latest
container_name: archive-minio
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin123
volumes:
- minio_data:/data
ports:
- "9000:9000"
- "9001:9001"
restart: unless-stopped
elasticsearch:
image: elasticsearch:8.11.1
container_name: archive-es
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- xpack.security.enabled=false
volumes:
- es_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
restart: unless-stopped
volumes:
postgres_data:
minio_data:
es_data:
```
在文件所在目录执行命令:docker-compose up -d。等待所有容器状态为“running”。
2.2 初始化MinIO存储桶
浏览器访问 http://你的服务器IP:9001,使用账号minioadmin和密码minioadmin123登录。
- 点击左侧菜单“Buckets”,然后点击“Create Bucket”。
- 输入Bucket名称为
group-archive。
- 点击“Create Bucket”完成创建。
三、核心模块开发与配置
3.1 Spring Boot项目初始化与依赖
使用Spring Initializr创建项目,核心依赖如下(pom.xml部分内容):
```
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
org.postgresql
postgresql
runtime
io.minio
minio
8.5.2
co.elastic.clients
elasticsearch-java
8.11.1
org.flowable
flowable-spring-boot-starter
7.0.0
```
3.2 配置文件(application.yml)
在src/main/resources/application.yml中配置所有连接信息:
```
spring:
datasource:
url: jdbc:postgresql://localhost:5432/archive_db
username: archive_admin
password: YourStrongPassword123!
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
minio:
endpoint: http://localhost:9000
accessKey: minioadmin
secretKey: minioadmin123
bucket: group-archive
elasticsearch:
host: localhost
port: 9200
```
3.3 档案实体与存储服务

创建档案核心实体类Archive.java:
```
@Entity
public class Archive {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String archiveNumber; // 档案编号
private String title; // 标题
private String category; // 分类(人事、财务、项目等)
private String fileName; // 原始文件名
private String fileKey; // 存储在MinIO中的唯一标识
private String fileType; // 文件类型
private Long fileSize; // 文件大小(字节)
private String uploadUser; // 上传人
private LocalDateTime uploadTime; // 上传时间
// 省略getter/setter
}
```
创建MinIO文件服务类MinioService.java,核心上传方法:
```
@Service
public class MinioService {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Value("${minio.bucket}")
private String bucket;
private MinioClient client;
@PostConstruct
public void init() {
client = MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
public String uploadFile(MultipartFile file, String objectName) throws Exception {
// 检查存储桶是否存在
boolean found = client.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
if (!found) {
client.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
}
// 上传文件
client.putObject(
PutObjectArgs.builder()
.bucket(bucket)
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build());
// 返回文件访问URL(生产环境应配置为内网域名或通过网关转发)
return endpoint + "/" + bucket + "/" + objectName;
}
}
```
3.4 档案索引与全文检索
创建Elasticsearch服务类ElasticsearchService.java,实现档案索引创建:
```
@Service
public class ElasticsearchService {
private final ElasticsearchClient client;
public ElasticsearchService() {
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)).build();
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
this.client = new ElasticsearchClient(transport);
}
public void indexArchive(Archive archive) throws IOException {
// 构建索引文档
IndexResponse response = client.index(i -> i
.index("archives")
.id(archive.getId().toString())
.document(archive));
}
public List
search(String keyword) throws IOException {
SearchResponse response = client.search(s -> s
.index("archives")
.query(q -> q
.bool(b -> b
.should(sh -> sh
.match(m -> m
.field("title")
.query(keyword)))
.should(sh -> sh
.match(m -> m
.field("content") // 假设有OCR提取的文本内容字段
.query(keyword)))
)),
Archive.class);
// 处理并返回结果列表
return response.hits().hits().stream()
.map(hit -> hit.source())
.collect(Collectors.toList());
}
}
```
四、关键业务流程实现
4.1 档案上传与归档接口
创建REST控制器ArchiveController.java,实现上传接口:
```
@RestController
@RequestMapping("/api/archive")
public class ArchiveController {
@Autowired
private ArchiveRepository archiveRepo;
@Autowired
private MinioService minioService;
@Autowired
private ElasticsearchService esService;
@PostMapping("/upload")
public ResponseEntity uploadArchive(
@RequestParam("file") MultipartFile file,
@RequestParam("title") String title,
@RequestParam("category") String category,
@RequestParam("uploadUser") String uploadUser) {
try {
// 1. 生成唯一文件标识
String fileKey = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
// 2. 上传文件到MinIO
String fileUrl = minioService.uploadFile(file, fileKey);
// 3. 保存元数据到数据库
Archive archive = new Archive();
archive.setArchiveNumber("ARC" + System.currentTimeMillis());
archive.setTitle(title);
archive.setCategory(category);
archive.setFileName(file.getOriginalFilename());
archive.setFileKey(fileKey);
archive.setFileType(file.getContentType());
archive.setFileSize(file.getSize());
archive.setUploadUser(uploadUser);
archive.setUploadTime(LocalDateTime.now());
Archive savedArchive = archiveRepo.save(archive);
// 4. 建立全文检索索引
esService.indexArchive(savedArchive);
return ResponseEntity.ok("档案上传成功,编号:" + savedArchive.getArchiveNumber());
} catch (Exception e) {
return ResponseEntity.status(500).body("上传失败:" + e.getMessage());
}
}
}
```
4.2 基于RBAC的权限控制
创建权限注解和拦截器,实现部门级数据隔离。在实体类上添加部门字段,并在数据查询时自动过滤:
```
@Entity
public class Archive {
// ... 其他字段
private String departmentCode; // 所属部门编码
}
@Repository
public interface ArchiveRepository extends JpaRepository {
// 仅查询当前用户所属部门的档案
@Query("SELECT a FROM Archive a WHERE a.departmentCode = :deptCode")
List findAllByDepartment(@Param("deptCode") String deptCode);
}
```
五、前端管理界面快速搭建
5.1 使用Vue CLI创建项目并安装依赖
执行以下命令:
```
npm create vue@latest group-archive-frontend
cd group-archive-frontend
npm install element-plus axios
npm run dev
```
5.2 档案上传组件关键代码
创建ArchiveUpload.vue组件:
```
```
六、系统部署与优化建议
6.1 生产环境部署配置
修改application-prod.yml,关键配置如下:
```
spring:
datasource:
url: jdbc:postgresql://生产数据库IP:5432/archive_db
使用更安全的密码管理方式,如环境变量或配置中心
servlet:
multipart:
max-file-size: 2GB
max-request-size: 2GB
minio:
endpoint: http://生产MinIO IP:9000
生产环境务必更换默认密钥
logging:
level:
com.yourcompany: DEBUG
file:
name: /var/log/archive/application.log
```
6.2 安全加固措施
- 启用HTTPS:为所有服务配置SSL证书。
- 数据库连接加密:在PostgreSQL配置中启用SSL连接。
- MinIO访问策略:为
group-archive存储桶设置严格的Bucket Policy,仅允许应用服务器访问。
- API网关:使用Nginx或Spring Cloud Gateway作为统一入口,配置速率限制和IP黑白名单。
6.3 性能与可用性优化
- 文件分片上传:对于大文件(>100MB),实现前端分片、后端合并的上传逻辑。
- Elasticsearch集群:生产环境部署3节点集群,配置分片和副本。
- 缓存策略:对频繁访问的档案元数据(如分类列表、部门信息)使用Redis缓存。
- 定时备份:编写脚本,每天凌晨对PostgreSQL数据库和MinIO存储桶进行增量备份。