当企业档案管理软件的企业版报价超出预算时,技术团队完全可以主导构建低成本、高可控的内部解决方案。本文将提供三套从轻量到专业的实操方案,覆盖不同规模与需求,确保每一步都可直接复制执行。
此方案适合中小型团队,利用成熟的开源项目,在2小时内完成基础部署。
我们选用 Mayan EDMS,它是一个功能齐全的文档管理系统。
以 Ubuntu 22.04 LTS 为例,执行以下命令:
更新系统并安装 Docker:
``` sudo apt update && sudo apt upgrade -y sudo apt install -y apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io ```安装 Docker Compose:
``` sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose ```创建项目目录并编写配置文件:
``` mkdir ~/mayan-edms && cd ~/mayan-edms ```创建 docker-compose.yml 文件,内容如下:
``` version: '3.4' services: redis: image: redis:7-alpine restart: unless-stopped postgres: image: postgres:15-alpine restart: unless-stopped environment: POSTGRES_DB: mayan POSTGRES_USER: mayan POSTGRES_PASSWORD: your_secure_password_here volumes: - postgres_data:/var/lib/postgresql/data app: image: mayanedms/mayanedms:latest restart: unless-stopped depends_on: - redis - postgres ports: - "80:8000" environment: MAYAN_DATABASE_ENGINE: django.db.backends.postgresql MAYAN_DATABASE_NAME: mayan MAYAN_DATABASE_USER: mayan MAYAN_DATABASE_PASSWORD: your_secure_password_here MAYAN_DATABASE_HOST: postgres MAYAN_DATABASE_PORT: "5432" MAYAN_REDIS_HOST: redis volumes: - app_media:/var/lib/mayan - app_data:/opt/mayan-edms/media volumes: postgres_data: app_media: app_data: ```注意:请务必将 your_secure_password_here 替换为高强度密码。
启动服务:
``` sudo docker-compose up -d ```等待约1分钟,在浏览器访问服务器IP地址。首次访问将进入管理员创建页面,按提示完成设置。
此方案适合需要与现有业务系统(如OA、CRM)深度集成,或对流程有特殊定制的团队。
使用Docker快速部署MinIO,用于存储档案文件:
``` docker run -d \ -p 9000:9000 \ -p 9001:9001 \ --name minio \ -v ~/minio-data:/data \ -e "MINIO_ROOT_USER=your_access_key" \ -e "MINIO_ROOT_PASSWORD=your_secret_key" \ quay.io/minio/minio server /data --console-address ":9001" ```访问 http://服务器IP:9001,使用上述设置的账号密码登录,创建一个名为 doc-archive 的存储桶。
以下是一个Flask的文档上传接口核心代码,展示如何与MinIO集成:
``` from flask import Flask, request, jsonify from minio import Minio from minio.error import S3Error import uuid app = Flask(__name__) 初始化MinIO客户端 minio_client = Minio( 'your_server_ip:9000', access_key='your_access_key', secret_key='your_secret_key', secure=False 生产环境应使用HTTPS ) @app.route('/api/document/upload', methods=['POST']) def upload_document(): if 'file' not in request.files: return jsonify({'error': 'No file part'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': 'No selected file'}), 400 生成唯一文件名并提取元数据 original_filename = file.filename file_extension = original_filename.rsplit('.', 1)[1].lower() if '.' in original_filename else '' unique_filename = f"{uuid.uuid4().hex}.{file_extension}" if file_extension else uuid.uuid4().hex 定义元数据(可从表单获取) user_metadata = { 'x-amz-meta-original-filename': original_filename, 'x-amz-meta-uploader': request.form.get('uploader', 'anonymous'), 'x-amz-meta-department': request.form.get('department', '') } try: 上传到MinIO minio_client.put_object( 'doc-archive', unique_filename, file, length=-1, part_size=1010241024, metadata=user_metadata ) 此处应将文件信息(如unique_filename, original_filename, 元数据)存入数据库 return jsonify({ 'message': 'Upload successful', 'file_id': unique_filename, 'original_name': original_filename }), 200 except S3Error as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000) ```
关键操作:将此代码保存为 app.py,安装依赖 flask minio 后运行。前端通过表单提交文件及元数据到此接口。
此方案适合非技术主导或希望快速实现复杂业务流程的团队,利用低代码平台构建应用。
选用 Budibase(开源版),它可以连接多种数据库并快速构建内部工具。
使用Docker一键部署Budibase平台:
``` docker run -d \ --name budibase \ -p 10000:10000 \ -v budibase_data:/data \ -e JWT_SECRET=your_super_secret_jwt_key_change_this \ -e PORT=10000 \ -e REDIS_URL=redis://redis:6379 \ -e MINIO_URL=http://minio:9000 \ -e MINIO_ACCESS_KEY=your_access_key \ -e MINIO_SECRET_KEY=your_secret_key \ --restart unless-stopped \ budibase/budibase:latest ```访问 http://服务器IP:10000 注册第一个管理员账户。
在Budibase中创建新应用,选择“从外部数据源开始”。
无论选择以上哪种方案,以下配置对生产环境至关重要。
编写备份脚本 backup.sh,并加入crontab定时任务。
``` !/bin/bash 备份数据库(以PostgreSQL为例) BACKUP_DIR="/opt/archive_backups" DATE=$(date +%Y%m%d_%H%M%S) 备份数据库 docker exec -t your_postgres_container pg_dump -U mayan mayan > $BACKUP_DIR/mayan_db_$DATE.sql 备份上传的文件(MinIO存储桶) mc mirror --overwrite minio/doc-archive $BACKUP_DIR/minio_files_$DATE/ 保留最近7天的备份 find $BACKUP_DIR -name ".sql" -mtime +7 -delete find $BACKUP_DIR -type d -name "minio_files_" -mtime +7 -exec rm -rf {} \; ```设置每天凌晨2点执行:0 2 /bin/bash /path/to/backup.sh
在自建方案中,必须在业务逻辑层实现基于角色的访问控制(RBAC)。
定义一个简单的权限检查装饰器(Python Flask示例):
``` from functools import wraps from flask import request, jsonify 模拟用户角色和权限 USER_PERMISSIONS = { 'admin': ['view', 'upload', 'delete', 'manage_user'], 'department_head': ['view', 'upload', 'delete_department'], 'staff': ['view', 'upload'] } def permission_required(permission): def decorator(f): @wraps(f) def decorated_function(args, kwargs): 假设从JWT或Session中获取当前用户角色 current_user_role = get_current_user_role() 需自行实现此函数 if permission not in USER_PERMISSIONS.get(current_user_role, []): return jsonify({'error': 'Permission denied'}), 403 return f(args, kwargs) return decorated_function return decorator 在API上使用 @app.route('/api/document/delete/对于方案二,集成轻量级全文检索引擎Whoosh。
安装与初始化:
``` pip install whoosh ```创建索引并编写搜索函数:
``` from whoosh.index import create_in, open_dir from whoosh.fields import Schema, TEXT, ID, DATETIME from whoosh.qparser import QueryParser import os 定义索引结构 schema = Schema( doc_id=ID(stored=True, unique=True), title=TEXT(stored=True), content=TEXT, 存储从文件提取的文本 uploader=TEXT(stored=True), upload_date=DATETIME(stored=True) ) 创建索引目录 if not os.path.exists("indexdir"): os.mkdir("indexdir") ix = create_in("indexdir", schema) 添加文档到索引的函数 def index_document(doc_id, title, content_text, uploader, upload_date): writer = ix.writer() writer.update_document( doc_id=doc_id, title=title, content=content_text, 需预先从PDF/Word等文件中提取文本 uploader=uploader, upload_date=upload_date ) writer.commit() 搜索函数 def search_documents(query_str, limit=10): with ix.searcher() as searcher: parser = QueryParser("content", ix.schema) query = parser.parse(query_str) results = searcher.search(query, limit=limit) return [{"title": r['title'], "doc_id": r['doc_id']} for r in results] ```在文档上传流程中,调用 index_document 函数将文档内容加入索引。提供搜索API供前端调用。