环保局数字档案馆搭建指南:从零构建可扩展的文档管理系统

系统架构与核心技术选型

本系统采用B/S架构,分为前端展示层、业务逻辑层和数据存储层。前端使用Vue 3 + Element Plus构建交互界面,后端采用Spring Boot框架,数据库使用PostgreSQL 15,全文检索使用Elasticsearch 8.11。

硬件与软件环境要求

服务器最低配置:4核CPU,8GB内存,500GB SSD硬盘。操作系统推荐使用Ubuntu Server 22.04 LTS。需要安装的依赖软件包如下:

  • Java Development Kit 17
  • Node.js 18 LTS
  • PostgreSQL 15
  • Elasticsearch 8.11
  • Nginx 1.24

环境配置与依赖安装

操作系统与基础环境

通过SSH连接到服务器,执行以下命令更新系统并安装基础工具:

``` sudo apt update && sudo apt upgrade -y sudo apt install -y curl wget git vim ```

Java环境安装

安装OpenJDK 17:

``` sudo apt install -y openjdk-17-jdk java -version 验证安装,应显示"openjdk version 17" ```

数据库安装与配置

安装PostgreSQL 15:

``` sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo apt update sudo apt install -y postgresql-15 postgresql-client-15 ```

创建数据库和用户:

``` sudo -u postgres psql CREATE DATABASE eco_archive; CREATE USER archive_admin WITH PASSWORD 'YourSecurePassword123!'; GRANT ALL PRIVILEGES ON DATABASE eco_archive TO archive_admin; \q ```

Elasticsearch安装

安装Elasticsearch 8.11:

``` wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.11.1-amd64.deb sudo dpkg -i elasticsearch-8.11.1-amd64.deb sudo systemctl enable elasticsearch sudo systemctl start elasticsearch ```

后端系统搭建

项目初始化

使用Spring Initializr创建项目,选择以下依赖:Spring Web, Spring Data JPA, Spring Security, PostgreSQL Driver。

核心配置文件

创建application.yml配置文件:

``` server: port: 8080 spring: datasource: url: jdbc:postgresql://localhost:5432/eco_archive username: archive_admin password: YourSecurePassword123! driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect elasticsearch: uris: http://localhost:9200 servlet: multipart: max-file-size: 100MB max-request-size: 100MB ```

实体类设计

创建档案文档实体类:

``` @Entity @Table(name = "archive_documents") public class ArchiveDocument { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String documentNumber; @Column(nullable = false) private String title; @Column(nullable = false) private String documentType; // 环评报告、监测数据、批复文件等 @Column(nullable = false) private LocalDate createDate; @Column(nullable = false) private String department; @Column(nullable = false) private String filePath; @Column(length = 2000) private String description; @ElementCollection private Set tags = new HashSet<>(); // getters and setters } ```

文件存储服务

创建文件上传服务类:

``` @Service public class FileStorageService { private final Path fileStorageLocation; public FileStorageService() { this.fileStorageLocation = Paths.get("/var/eco-archive/files") .toAbsolutePath().normalize(); try { Files.createDirectories(this.fileStorageLocation); } catch (Exception ex) { throw new RuntimeException("无法创建文件存储目录", ex); } } public String storeFile(MultipartFile file, String documentNumber) { String originalFileName = StringUtils.cleanPath(file.getOriginalFilename()); String fileExtension = originalFileName.substring(originalFileName.lastIndexOf(".")); String fileName = documentNumber + "_" + System.currentTimeMillis() + fileExtension; try { Path targetLocation = this.fileStorageLocation.resolve(fileName); Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING); return fileName; } catch (IOException ex) { throw new RuntimeException("文件存储失败: " + fileName, ex); } } } ```

前端系统开发

Vue项目初始化

创建Vue项目并安装依赖:

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

路由配置

配置路由文件:

``` import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/', name: 'Home', component: () => import('../views/HomeView.vue') }, { path: '/upload', name: 'Upload', component: () => import('../views/UploadView.vue') }, { path: '/search', name: 'Search', component: () => import('../views/SearchView.vue') }, { path: '/document/:id', name: 'DocumentDetail', component: () => import('../views/DocumentDetail.vue') } ] const router = createRouter({ history: createWebHistory(), routes }) export default router ```

文件上传组件

环保局数字档案馆搭建指南:从零构建可扩展的文档管理系统

创建文件上传组件:

``` ```

全文检索功能实现

Elasticsearch索引配置

创建文档索引映射:

``` PUT /eco_documents { "mappings": { "properties": { "documentNumber": { "type": "keyword" }, "title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" }, "content": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" }, "documentType": { "type": "keyword" }, "createDate": { "type": "date", "format": "yyyy-MM-dd" }, "department": { "type": "keyword" }, "tags": { "type": "keyword" } } } } ```

搜索服务实现

创建Elasticsearch搜索服务:

``` @Service public class DocumentSearchService { @Autowired private ElasticsearchRestTemplate elasticsearchTemplate; public SearchHits searchDocuments(String keyword, String documentType, LocalDate startDate, LocalDate endDate, int page, int size) { NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); if (StringUtils.hasText(keyword)) { boolQuery.must(QueryBuilders.multiMatchQuery(keyword, "title", "content")); } if (StringUtils.hasText(documentType)) { boolQuery.filter(QueryBuilders.termQuery("documentType", documentType)); } if (startDate != null && endDate != null) { boolQuery.filter(QueryBuilders.rangeQuery("createDate") .gte(startDate) .lte(endDate)); } queryBuilder.withQuery(boolQuery); queryBuilder.withPageable(PageRequest.of(page, size)); queryBuilder.withSort(Sort.by(Sort.Direction.DESC, "createDate")); return elasticsearchTemplate.search(queryBuilder.build(), ArchiveDocument.class); } } ```

权限控制与安全配置

Spring Security配置

配置安全策略:

``` @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/documents/search").permitAll() .requestMatchers("/api/documents/upload").hasRole("ADMIN") .requestMatchers("/api/documents/").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated() ) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } } ```

系统部署与运维

Nginx反向代理配置

配置Nginx作为反向代理:

``` server { listen 80; server_name archive.example.com; location / { root /var/www/eco-archive-frontend/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /files/ { alias /var/eco-archive/files/; expires 30d; add_header Cache-Control "public, immutable"; } } ```

系统监控配置

配置Prometheus监控:

``` management: endpoints: web: exposure: include: health,metrics,prometheus metrics: export: prometheus: enabled: true ```

数据备份脚本

创建数据库备份脚本:

``` !/bin/bash BACKUP_DIR="/backup/eco-archive" DATE=$(date +%Y%m%d_%H%M%S) DB_NAME="eco_archive" 备份数据库 pg_dump -U archive_admin $DB_NAME > $BACKUP_DIR/db_backup_$DATE.sql 备份上传的文件 tar -czf $BACKUP_DIR/files_backup_$DATE.tar.gz /var/eco-archive/files/ 删除7天前的备份 find $BACKUP_DIR -type f -mtime +7 -delete echo "备份完成: $DATE" ```

将脚本加入crontab,每天凌晨2点执行:

``` 0 2 /opt/eco-archive/backup.sh ```

常见问题排查

文件上传失败

检查文件存储目录权限:

``` sudo chown -R www-data:www-data /var/eco-archive/files sudo chmod -R 755 /var/eco-archive/files ```

搜索功能无结果

检查Elasticsearch连接状态:

``` curl http://localhost:9200/_cluster/health ```

确保状态显示为green,检查索引是否存在:

``` curl http://localhost:9200/_cat/indices?v ```

数据库连接失败

检查PostgreSQL服务状态和连接配置:

``` sudo systemctl status postgresql sudo -u postgres psql -c "\l" ```

修改pg_hba.conf允许连接:

``` /etc/postgresql/15/main/pg_hba.conf host all all 127.0.0.1/32 md5 host all all ::1/128 md5 ```
AI咨询
热线电话

028-85154420

15388110056

全国售前咨询电话

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

微信扫码关注安答联动

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

安答联动档案管理系统