从零搭建研发项目数字档案馆系统实操全流程

系统架构与技术选型

本系统采用前后端分离架构,旨在解决研发过程中文档版本混乱、检索困难的问题。核心组件包括:Spring Boot 3.2作为后端框架,Vue 3 + Element Plus作为前端界面,MinIO负责私有化文件存储,MySQL存储元数据,Elasticsearch实现毫秒级全文检索。

第一步:基础设施环境搭建

为了确保环境一致性,我们使用Docker Compose一次性启动MySQL、Redis、MinIO和Elasticsearch。请在服务器本地创建docker-compose.yml文件,并将以下内容完整复制进去。

1. 编写Docker编排文件

此配置文件包含了所有中间件的初始化设置,包括MinIO的默认AccessKey和SecretKey,以及Elasticsearch的内存分配。

```yaml version: '3.8' services: mysql: image: mysql:8.0 container_name: archive_mysql environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: dev_archive ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql redis: image: redis:7-alpine container_name: archive_redis ports: - "6379:6379" minio: image: minio/minio:latest container_name: archive_minio environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin123 command: server /data --console-address ":9001" ports: - "9000:9000" - "9001:9001" volumes: - minio_data:/data elasticsearch: image: elasticsearch:8.11.0 container_name: archive_es environment: - discovery.type=single-node - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - xpack.security.enabled=false ports: - "9200:9200" volumes: - es_data:/usr/share/elasticsearch/data volumes: mysql_data: minio_data: es_data: ```

2. 启动服务

docker-compose.yml同级目录下执行以下命令启动所有容器:

```bash docker-compose up -d ``>

执行docker ps确认所有容器状态为Up。注意:Elasticsearch启动较慢,需等待约30秒直至9200端口可连通。

第二步:数据库设计与初始化

我们需要两张核心表:t_project用于关联研发项目,t_archive用于存储档案文件的具体信息。请连接MySQL(端口3306,用户root,密码root123456),选中数据库dev_archive并执行以下SQL。

```sql CREATE TABLE IF NOT EXISTS `t_project` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '项目ID', `project_name` varchar(100) NOT NULL COMMENT '项目名称', `project_code` varchar(50) NOT NULL COMMENT '项目编码', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_code` (`project_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='研发项目表'; CREATE TABLE IF NOT EXISTS `t_archive` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '档案ID', `project_id` bigint NOT NULL COMMENT '关联项目ID', `file_name` varchar(255) NOT NULL COMMENT '原始文件名', `file_type` varchar(20) NOT NULL COMMENT '文件类型后缀', `file_size` bigint NOT NULL COMMENT '文件大小(字节)', `object_name` varchar(500) NOT NULL COMMENT 'MinIO对象存储路径', `uploader` varchar(50) DEFAULT NULL COMMENT '上传人', `upload_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', `file_status` tinyint DEFAULT 1 COMMENT '状态 1:正常 0:删除', PRIMARY KEY (`id`), KEY `idx_project` (`project_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数字档案表'; ```

第三步:后端核心功能开发

后端使用Spring Boot 3.2.1,集成JPA、MinIO Client和Elasticsearch Repository。

1. Maven依赖配置

pom.xml中添加以下关键依赖,版本号由Spring Boot父工程管理:

```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-data-elasticsearch mysql mysql-connector-java 8.0.33 io.minio minio 8.5.7 org.projectlombok lombok true ```

2. application.yml完整配置

请务必修改server.port和数据库连接信息,MinIO地址填写Docker容器IP或主机IP。

```yaml server: port: 8080 spring: application: name: archive-system datasource: url: jdbc:mysql://localhost:3306/dev_archive?useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: root123456 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true elasticsearch: uris: http://localhost:9200 minio: endpoint: http://localhost:9000 accessKey: minioadmin secretKey: minioadmin123 bucketName: dev-archives ```

3. 核心实体类与Repository

创建Archive.java,使用JPA注解映射MySQL,同时使用@Document映射ES索引。

```java package com.dev.archive.entity; import jakarta.persistence.; import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @Data @Entity @Table(name = "t_archive") @Document(indexName = "archive_index") public class Archive { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long projectId; @Field(type = FieldType.Text, analyzer = "ik_max_word") private String fileName; private String fileType; private Long fileSize; private String objectName; private String uploader; } ```

创建ArchiveRepository.java继承JPA和ES的Repository接口以同时支持两种存储操作。

```java package com.dev.archive.repository; import com.dev.archive.entity.Archive; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Repository; @Repository public interface ArchiveRepository extends JpaRepository, ElasticsearchRepository { } ```

4. 文件上传与索引业务逻辑

从零搭建研发项目数字档案馆系统实操全流程

ArchiveService.java中实现MinIO上传和双写(MySQL+ES)逻辑。这里包含了一个重要的初始化Bucket的操作。

```java package com.dev.archive.service; import com.dev.archive.entity.Archive; import com.dev.archive.repository.ArchiveRepository; import io.minio.MinioClient; import io.minio.PutObjectArgs; import io.minio.MakeBucketArgs; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.time.LocalDateTime; import java.util.UUID; @Service public class ArchiveService { private final MinioClient minioClient; private final ArchiveRepository archiveRepository; @Value("${minio.bucketName}") private String bucketName; public ArchiveService(MinioClient minioClient, ArchiveRepository archiveRepository) { this.minioClient = minioClient; this.archiveRepository = archiveRepository; initBucket(); } private void initBucket() { try { boolean found = minioClient.bucketExists(bucketName); if (!found) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); } } catch (Exception e) { throw new RuntimeException("MinIO Bucket初始化失败", e); } } public Archive uploadArchive(Long projectId, MultipartFile file, String uploader) { try { String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); String objectName = UUID.randomUUID() + "." + suffix; // 上传至MinIO try (InputStream inputStream = file.getInputStream()) { minioClient.putObject( PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .stream(inputStream, file.getSize(), -1) .contentType(file.getContentType()) .build() ); } // 保存元数据到MySQL并同步索引到ES Archive archive = new Archive(); archive.setProjectId(projectId); archive.setFileName(originalFilename); archive.setFileType(suffix); archive.setFileSize(file.getSize()); archive.setObjectName(objectName); archive.setUploader(uploader); return archiveRepository.save(archive); } catch (Exception e) { throw new RuntimeException("文件上传失败: " + e.getMessage(), e); } } } ```

5. 控制器层实现

提供上传接口和搜索接口,确保跨域配置正确以便前端调用。

```java package com.dev.archive.controller; import com.dev.archive.entity.Archive; import com.dev.archive.repository.ArchiveRepository; import com.dev.archive.service.ArchiveService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.web.bind.annotation.; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/api/archives") @CrossOrigin(origins = "") public class ArchiveController { @Autowired private ArchiveService archiveService; @Autowired private ArchiveRepository archiveRepository; @PostMapping("/upload") public String upload(@RequestParam Long projectId, @RequestParam("file") MultipartFile file, @RequestParam String uploader) { Archive archive = archiveService.uploadArchive(projectId, file, uploader); return "上传成功,档案ID: " + archive.getId(); } @GetMapping("/search") public Page search(@RequestParam String keyword, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { // 简单的ES查询,生产环境建议使用NativeSearchQueryBuilder构建复杂Query return archiveRepository.findByFileNameContaining(keyword, PageRequest.of(page, size)); } } ``>

注意:需要在ArchiveRepository中添加ES查询方法定义:Page findByFileNameContaining(String keyword, Pageable pageable);

第四步:前端界面开发

使用Vue 3 Vite版构建,Element Plus提供UI组件,Axios处理HTTP请求。

1. 项目初始化

执行命令创建项目并安装依赖:

```bash npm create vue@latest archive-frontend cd archive-frontend npm install element-plus axios ```

2. 配置Axios与Element Plus

src/main.js中全局引入Element Plus:

```javascript import { createApp } from 'vue' import App from './App.vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' const app = createApp(App) app.use(ElementPlus) app.mount('app') ```

3. 核心页面组件实现

修改src/App.vue,实现文件上传和列表搜索功能。这是一个包含完整逻辑的单文件组件。

```html ```

第五步:运行与验证

完成所有代码编写后,按以下顺序启动系统进行验证。

1. 启动后端

在IDEA中运行ArchiveApplication主类,或使用Maven命令打包运行:

```bash mvn clean package java -jar target/archive-system-0.0.1-SNAPSHOT.jar ``>

观察控制台日志,确认MinIO Bucket初始化成功且ES连接正常。

2. 启动前端

在Vue项目根目录下执行:

```bash npm run dev ``>

终端会输出本地访问地址,通常是http://localhost:5173

3. 功能验证步骤

  1. 文件上传:在浏览器打开前端页面,输入项目ID(如100)和上传人,将一个本地的PDF或Word文档拖入上传框。等待提示“上传成功”。
  2. 数据校验:登录MySQL,查询select from t_archive;,确认新增了一条记录。
  3. 存储校验:访问MinIO控制台http://localhost:9001,使用minioadmin/minioadmin123登录,查看dev-archives桶中是否存在文件。
  4. 检索测试:在前端搜索框输入刚才上传文件名中的关键字,按回车。下方表格应能即时展示出匹配的文档记录,验证Elasticsearch索引生效。
AI咨询
热线电话

028-85154420

15388110056

全国售前咨询电话

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

微信扫码关注安答联动

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

安答联动档案管理系统