海事部门档案管理系统:从零搭建高可用数字化档案平台实操指南
一、系统架构设计与技术选型
本系统采用前后端分离架构,后端使用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三、前端界面开发
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
将文件拖到此处,或点击上传
支持上传PDF、Word、图片文件,且不超过500MB
四、全文检索实现
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")