构建高效档案整理系统:从零到一的技术实现指南
系统架构与核心技术选型
一个完整的档案整理系统通常由前端界面、后端服务、数据库和文件存储四个核心部分组成。对于中小型项目,推荐采用前后端分离的架构,这能保证系统的可维护性和扩展性。
技术栈选择
后端建议使用Python的Django框架,它自带强大的Admin管理后台和ORM,能快速处理档案的增删改查。数据库使用PostgreSQL,它对全文搜索和JSON字段的支持优于MySQL。前端使用Vue.js 3组合式API,配合Element Plus组件库,能快速搭建用户界面。文件存储使用MinIO,它是一个与Amazon S3 API兼容的开源对象存储服务,可以轻松部署在本地服务器。
环境准备
在开始之前,请确保你的开发环境已安装以下软件:
- Python 3.9或更高版本
- Node.js 18.x LTS版本
- PostgreSQL 14或更高版本
- Docker与Docker Compose(用于部署MinIO)
使用以下命令检查安装:
python --version
node --version
psql --version
docker --version
后端服务搭建
首先创建项目目录并初始化Python虚拟环境。
mkdir archive_system && cd archive_system
python -m venv venv
Windows系统激活命令:
venv\Scripts\activate
Linux/Mac系统激活命令:
source venv/bin/activate
安装依赖与创建Django项目
安装必要的Python包并创建Django项目和应用。
pip install django django-rest-framework psycopg2-binary python-dotenv
django-admin startproject backend .
python manage.py startapp archives
编辑backend/settings.py文件,配置数据库连接和基础设置。首先在项目根目录创建.env文件,用于存储敏感信息:
SECRET_KEY=your-django-secret-key-here
DB_NAME=archive_db
DB_USER=archive_user
DB_PASSWORD=your_secure_password
DB_HOST=localhost
DB_PORT=5432
然后在settings.py中读取这些配置:
import os
from dotenv import load_dotenv
load_dotenv()
SECRET_KEY = os.getenv('SECRET_KEY')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASSWORD'),
'HOST': os.getenv('DB_HOST'),
'PORT': os.getenv('DB_PORT'),
}
}
INSTALLED_APPS = [
...,
'rest_framework',
'archives',
]
文件上传设置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
设置允许上传的文件类型和大小
FILE_UPLOAD_MAX_MEMORY_SIZE = 10485760 10MB
DATA_UPLOAD_MAX_MEMORY_SIZE = 10485760
定义数据模型
在archives/models.py中定义档案的核心数据模型。

from django.db import models
from django.contrib.auth.models import User
class ArchiveCategory(models.Model):
"""档案分类"""
name = models.CharField(max_length=100, unique=True)
code = models.CharField(max_length=20, unique=True)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['code']
def __str__(self):
return f"{self.code} - {self.name}"
class ArchiveItem(models.Model):
"""档案条目"""
STATUS_CHOICES = [
('draft', '草稿'),
('active', '有效'),
('archived', '已归档'),
('destroyed', '已销毁'),
]
title = models.CharField(max_length=200)
archive_number = models.CharField(max_length=50, unique=True)
category = models.ForeignKey(ArchiveCategory, on_delete=models.PROTECT)
content = models.TextField()
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
created_by = models.ForeignKey(User, on_delete=models.PROTECT, related_name='created_archives')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
retention_years = models.IntegerField(default=10)
class Meta:
indexes = [
models.Index(fields=['archive_number']),
models.Index(fields=['status']),
models.Index(fields=['created_at']),
]
def __str__(self):
return self.archive_number
class ArchiveAttachment(models.Model):
"""档案附件"""
archive_item = models.ForeignKey(ArchiveItem, on_delete=models.CASCADE, related_name='attachments')
file = models.FileField(upload_to='archive_attachments/%Y/%m/%d/')
original_filename = models.CharField(max_length=255)
file_size = models.IntegerField()
uploaded_at = models.DateTimeField(auto_now_add=True)
def save(self, args, kwargs):
if not self.original_filename:
self.original_filename = self.file.name
self.file_size = self.file.size
super().save(args, kwargs)
创建并应用数据库迁移:
python manage.py makemigrations
python manage.py migrate
创建序列化器与API视图
在archives目录下创建serializers.py:
from rest_framework import serializers
from .models import ArchiveCategory, ArchiveItem, ArchiveAttachment
class ArchiveCategorySerializer(serializers.ModelSerializer):
class Meta:
model = ArchiveCategory
fields = '__all__'
class ArchiveAttachmentSerializer(serializers.ModelSerializer):
class Meta:
model = ArchiveAttachment
fields = ['id', 'original_filename', 'file_size', 'uploaded_at']
class ArchiveItemSerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.name', read_only=True)
attachments = ArchiveAttachmentSerializer(many=True, read_only=True)
class Meta:
model = ArchiveItem
fields = '__all__'
read_only_fields = ['created_by', 'created_at', 'updated_at']
创建views.py:
from rest_framework import viewsets, permissions, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from .models import ArchiveCategory, ArchiveItem
from .serializers import ArchiveCategorySerializer, ArchiveItemSerializer
class ArchiveCategoryViewSet(viewsets.ModelViewSet):
queryset = ArchiveCategory.objects.all()
serializer_class = ArchiveCategorySerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [filters.SearchFilter]
search_fields = ['name', 'code']
class ArchiveItemViewSet(viewsets.ModelViewSet):
queryset = ArchiveItem.objects.all()
serializer_class = ArchiveItemSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['category', 'status']
search_fields = ['title', 'archive_number', 'content']
ordering_fields = ['created_at', 'updated_at']
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
@action(detail=True, methods=['post'])
def change_status(self, request, pk=None):
item = self.get_object()
new_status = request.data.get('status')
if new_status in dict(ArchiveItem.STATUS_CHOICES):
item.status = new_status
item.save()
return Response({'status': 'success'})
return Response({'error': 'Invalid status'}, status=400)
配置URL路由,在archives/urls.py中:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r'categories', views.ArchiveCategoryViewSet)
router.register(r'items', views.ArchiveItemViewSet)
urlpatterns = [
path('', include(router.urls)),
]
在项目主urls.py中包含此路由:
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('archives.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
前端界面开发
在项目根目录创建前端应用。
cd ..
npx vue create frontend --no-git
选择手动配置,勾选:Router, Vuex, CSS Pre-processors (Sass/SCSS)
cd frontend
npm install element-plus axios vue-axios
配置Axios与API服务
创建src/services/api.js:
import axios from 'axios'
const apiClient = axios.create({
baseURL: 'http://localhost:8000/api/',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
}
})
// 请求拦截器,添加认证token
apiClient.interceptors.request.use(
config => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
export default {
// 档案分类相关
getCategories() {
return apiClient.get('/categories/')
},
createCategory(data) {
return apiClient.post('/categories/', data)
},
// 档案条目相关
getArchiveItems(params) {
return apiClient.get('/items/', { params })
},
getArchiveItem(id) {
return apiClient.get(`/items/${id}/`)
},
createArchiveItem(data) {
return apiClient.post('/items/', data)
},
updateArchiveItem(id, data) {
return apiClient.put(`/items/${id}/`, data)
},
deleteArchiveItem(id) {
return apiClient.delete(`/items/${id}/`)
},
changeItemStatus(id, status) {
return apiClient.post(`/items/${id}/change_status/`, { status })
}
}
创建档案管理组件
创建src/views/ArchiveList.vue:
档案管理
新建档案
搜索
重置
{{ getStatusText(scope.row.status) }}
查看
编辑
删除
文件存储服务部署
使用Docker Compose部署MinIO对象存储服务。创建docker-compose.yml:
version: '3.8'
services:
minio:
image: minio/minio:latest
container_name: archive_minio
ports:
- "9000:9000" API端口
- "9001:9001" 控制台端口
environment:
MINIO_ROOT_USER: archiveadmin
MINIO_ROOT_PASSWORD: your_minio_password_here