海事部门档案管理系统:从零搭建高可用数字化档案平台实操指南

一、系统架构设计与技术选型

本系统采用前后端分离架构,后端使用Spring Boot 2.7.5,前端使用Vue 3.2 + Element Plus,数据库使用PostgreSQL 14,全文检索使用Elasticsearch 7.17,文件存储采用MinIO对象存储。

1.1 开发环境准备

安装Java 11:

```bash sudo apt update sudo apt install openjdk-11-jdk -y java -version ```

安装Node.js 16:

```bash curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - sudo apt-get install -y nodejs node --version npm --version ```

1.2 数据库初始化

创建PostgreSQL数据库和用户:

```sql -- 创建数据库 CREATE DATABASE maritime_archive; -- 创建用户 CREATE USER archive_admin WITH PASSWORD 'Archive@2023!'; -- 授权 GRANT ALL PRIVILEGES ON DATABASE maritime_archive TO archive_admin; -- 创建核心表结构 CREATE TABLE archive_category ( id SERIAL PRIMARY KEY, category_code VARCHAR(50) NOT NULL UNIQUE, category_name VARCHAR(100) NOT NULL, parent_id INTEGER DEFAULT 0, retention_years INTEGER NOT NULL, security_level INTEGER DEFAULT 1, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ```

二、后端服务搭建

2.1 Spring Boot项目初始化

使用Spring Initializr生成项目:

```bash curl https://start.spring.io/starter.zip \ -d type=maven-project \ -d language=java \ -d bootVersion=2.7.5 \ -d baseDir=maritime-archive-backend \ -d groupId=com.maritime \ -d artifactId=archive \ -d name=MaritimeArchive \ -d dependencies=web,data-jpa,postgresql,validation,security \ -o maritime-archive.zip unzip maritime-archive.zip ```

2.2 核心配置文件

application.yml完整配置:

```yaml server: port: 8080 spring: datasource: url: jdbc:postgresql://localhost:5432/maritime_archive username: archive_admin password: Archive@2023! driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: format_sql: true servlet: multipart: max-file-size: 500MB max-request-size: 500MB minio: endpoint: http://localhost:9000 accessKey: minioadmin secretKey: minioadmin bucketName: maritime-archives ```

2.3 档案上传接口实现

创建档案上传控制器:

```java @RestController @RequestMapping("/api/archive") public class ArchiveController { @PostMapping("/upload") public ResponseEntity uploadArchive( @RequestParam("file") MultipartFile file, @RequestParam("categoryCode") String categoryCode, @RequestParam("archiveNumber") String archiveNumber, @RequestParam("title") String title, @RequestParam(value = "confidentialLevel", defaultValue = "1") int confidentialLevel) { // 验证文件类型 String originalFilename = file.getOriginalFilename(); String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".")); List allowedExtensions = Arrays.asList(".pdf", ".doc", ".docx", ".jpg", ".png", ".tiff"); if (!allowedExtensions.contains(fileExtension.toLowerCase())) { throw new InvalidFileTypeException("不支持的文件类型"); } // 生成唯一文件名 String uniqueFileName = UUID.randomUUID().toString() + fileExtension; // 上传到MinIO minioClient.putObject( PutObjectArgs.builder() .bucket(minioConfig.getBucketName()) .object(uniqueFileName) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build() ); // 保存到数据库 ArchiveRecord record = new ArchiveRecord(); record.setArchiveNumber(archiveNumber); record.setTitle(title); record.setCategoryCode(categoryCode); record.setFileName(uniqueFileName); record.setOriginalFileName(originalFilename); record.setFileSize(file.getSize()); record.setConfidentialLevel(confidentialLevel); record.setUploadTime(LocalDateTime.now()); archiveRepository.save(record); // 建立ES索引 elasticsearchService.indexArchive(record); return ResponseEntity.ok(new ArchiveUploadResponse("上传成功", record.getId())); } } ```

三、前端界面开发

3.1 Vue项目初始化

创建Vue项目:

```bash npm init vue@3 maritime-archive-frontend cd maritime-archive-frontend npm install element-plus axios vue-router@4 npm install ```

3.2 档案上传组件实现

创建ArchiveUpload.vue:

```html ```

四、全文检索实现

4.1 Elasticsearch配置

海事部门档案管理系统:从零搭建高可用数字化档案平台实操指南

安装并启动Elasticsearch:

```bash 下载并安装 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.0-linux-x86_64.tar.gz tar -xzf elasticsearch-7.17.0-linux-x86_64.tar.gz cd elasticsearch-7.17.0/ 修改配置 echo "cluster.name: maritime-archive" >> config/elasticsearch.yml echo "network.host: 0.0.0.0" >> config/elasticsearch.yml echo "http.port: 9200" >> config/elasticsearch.yml echo "discovery.type: single-node" >> config/elasticsearch.yml 启动 ./bin/elasticsearch -d ```

4.2 创建档案索引

创建档案索引映射:

```json PUT /maritime_archives { "mappings": { "properties": { "archiveNumber": { "type": "keyword" }, "title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" }, "categoryCode": { "type": "keyword" }, "content": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" }, "confidentialLevel": { "type": "integer" }, "uploadTime": { "type": "date" } } } } ```

4.3 全文检索接口实现

创建搜索服务:

```java @Service public class ArchiveSearchService { public SearchResponse searchArchives(String keyword, String categoryCode, Integer confidentialLevel, DateRange dateRange, int page, int size) { SearchRequest searchRequest = new SearchRequest("maritime_archives"); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 关键词搜索 if (StringUtils.hasText(keyword)) { boolQuery.must(QueryBuilders.multiMatchQuery(keyword, "title", "content") .minimumShouldMatch("75%")); } // 类别过滤 if (StringUtils.hasText(categoryCode)) { boolQuery.filter(QueryBuilders.termQuery("categoryCode", categoryCode)); } // 密级过滤 if (confidentialLevel != null) { boolQuery.filter(QueryBuilders.rangeQuery("confidentialLevel") .lte(confidentialLevel)); } // 时间范围过滤 if (dateRange != null && dateRange.getStartDate() != null) { boolQuery.filter(QueryBuilders.rangeQuery("uploadTime") .gte(dateRange.getStartDate()) .lte(dateRange.getEndDate())); } sourceBuilder.query(boolQuery); sourceBuilder.from((page - 1) size); sourceBuilder.size(size); sourceBuilder.sort("uploadTime", SortOrder.DESC); searchRequest.source(sourceBuilder); return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); } } ```

五、系统部署与运维

5.1 Docker容器化部署

创建docker-compose.yml:

```yaml version: '3.8' services: postgres: image: postgres:14 environment: POSTGRES_DB: maritime_archive POSTGRES_USER: archive_admin POSTGRES_PASSWORD: Archive@2023! volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" minio: image: minio/minio command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin volumes: - minio_data:/data ports: - "9000:9000" - "9001:9001" elasticsearch: image: elasticsearch:7.17.0 environment: - discovery.type=single-node - "ES_JAVA_OPTS=-Xms512m -Xmx512m" volumes: - es_data:/usr/share/elasticsearch/data ports: - "9200:9200" backend: build: ./maritime-archive-backend depends_on: - postgres - minio - elasticsearch environment: SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/maritime_archive SPRING_DATASOURCE_USERNAME: archive_admin SPRING_DATASOURCE_PASSWORD: Archive@2023! MINIO_ENDPOINT: http://minio:9000 ports: - "8080:8080" frontend: build: ./maritime-archive-frontend ports: - "80:80" volumes: postgres_data: minio_data: es_data: ```

5.2 后端Dockerfile

创建Dockerfile:

```dockerfile FROM openjdk:11-jre-slim WORKDIR /app COPY target/maritime-archive-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"] ```

5.3 启动系统

执行部署命令:

```bash 启动所有服务 docker-compose up -d 查看服务状态 docker-compose ps 查看日志 docker-compose logs -f backend ```

5.4 数据备份策略

创建备份脚本backup.sh:

```bash !/bin/bash BACKUP_DIR="/backup/$(date +%Y%m%d)" mkdir -p $BACKUP_DIR 备份PostgreSQL docker exec maritime-archive-postgres-1 pg_dump -U archive_admin maritime_archive > $BACKUP_DIR/maritime_archive.sql 备份MinIO数据 mc mirror --overwrite minio/maritime-archives $BACKUP_DIR/minio/ 备份Elasticsearch索引 curl -X GET "localhost:9200/_snapshot/backup_repository/snapshot_$(date +%Y%m%d)?wait_for_completion=true" 压缩备份文件 tar -czf $BACKUP_DIR.tar.gz $BACKUP_DIR ```

六、权限控制与安全配置

6.1 基于角色的访问控制

创建权限表结构:

```sql CREATE TABLE user_role ( id SERIAL PRIMARY KEY, role_code VARCHAR(50) NOT NULL UNIQUE, role_name VARCHAR(100) NOT NULL, permissions TEXT[] ); INSERT INTO user_role (role_code, role_name, permissions) VALUES ('admin', '系统管理员', ARRAY['archive:read', 'archive:write', 'archive:delete', 'user:manage', 'system:config']), ('archivist', '档案管理员', ARRAY['archive:read', 'archive:write', 'archive:delete']), ('viewer', '档案查阅员', ARRAY['archive:read']), ('auditor', '审计员', ARRAY['archive:read', 'log:view']); CREATE TABLE user_account ( id SERIAL PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, full_name VARCHAR(100) NOT NULL, department VARCHAR(100), role_code VARCHAR(50) REFERENCES user_role(role_code), is_active BOOLEAN DEFAULT true, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ```

6.2 Spring Security配置

安全配置类:

```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/auth/").permitAll() .antMatchers("/api/archive/download/").hasAnyAuthority("archive:read") .antMatchers(HttpMethod.POST, "/api/archive/").hasAnyAuthority("archive:write") .antMatchers(HttpMethod.DELETE, "/api/archive/").hasAnyAuthority("archive:delete") .antMatchers("/api/admin/").hasAnyAuthority("user:manage", "system:config")
AI咨询
热线电话

028-85154420

15388110056

全国售前咨询电话

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

微信扫码关注安答联动

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

安答联动档案管理系统