安顺数字档案馆系统:让档案管理从“老黄牛”变“智能管家”
档案管理软件厂商通常通过高昂的集成费用来封锁接口,或者仅提供封闭的客户端。为了绕过这一限制,我们将采用中间件代理模式。核心思路是在内网服务器搭建一个基于Node.js的轻量级API网关,该网关负责模拟厂商客户端的请求行为,将OA或ERP系统的标准指令转换为档案软件能识别的私有协议,从而实现零成本对接。
本方案选用Node.js作为开发语言,利用其强大的非阻塞I/O处理能力,配合Express框架构建Web服务,使用Axios库处理复杂的HTTP请求转发及文件流操作。该方案无需采购任何商业中间件,部署在任意一台能访问档案系统的服务器即可。
首先需要在服务器端安装Node.js运行环境。以下操作以CentOS 7系统为例,其他Linux发行版命令类似。请直接复制以下命令执行,确保安装Node.js 18.x版本以获得最佳性能支持。
1. 安装Node.js环境
使用curl命令下载NodeSource仓库配置并安装:
curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo -E bash -
sudo yum install -y nodejs
安装完成后,验证版本:
node -v
npm -v
2. 初始化项目目录
创建一个名为archive-gateway的目录,并进入该目录进行初始化:
mkdir /opt/archive-gateway
cd /opt/archive-gateway
npm init -y
3. 安装核心依赖包
我们需要安装Express用于搭建服务,Axios用于请求转发,Form-Data用于处理档案文件上传,Cors用于处理跨域问题,Dotenv用于管理环境变量:
npm install express axios form-data cors dotenv multer --save
在项目根目录下创建index.js文件。该文件是整个系统的核心,包含了鉴权、文件流处理和请求转发的完整逻辑。请直接复制以下代码覆盖文件内容,无需修改。
const express = require('express');
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
const cors = require('cors');
const multer = require('multer');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// 启用跨域支持,允许OA系统调用
app.use(cors());
// 解析JSON请求体
app.use(express.json({ limit: '50mb' }));
// 解析URL编码请求体
app.use(express.urlencoded({ extended: true, limit: '50mb' }));
// 配置文件上传中间件,使用内存存储以提高处理速度
const upload = multer({ storage: multer.memoryStorage() });
// 档案系统基础配置(请在.env文件中配置具体值)
const ARCHIVE_SYSTEM_BASE_URL = process.env.ARCHIVE_BASE_URL;
const ARCHIVE_USERNAME = process.env.ARCHIVE_USER;
const ARCHIVE_PASSWORD = process.env.ARCHIVE_PASS;
// 模拟登录获取Token的缓存机制
let cachedToken = null;
let tokenExpiry = null;
// 获取档案系统Session/Token的函数
async function getArchiveToken() {
// 如果Token未过期,直接返回
if (cachedToken && tokenExpiry && Date.now() < tokenExpiry) {
return cachedToken;
}
try {
// 此处模拟大多数档案系统的登录接口
// 实际场景需根据厂商具体的抓包分析替换loginUrl和字段名
const loginUrl = `${ARCHIVE_SYSTEM_BASE_URL}/api/login`;
const response = await axios.post(loginUrl, {
username: ARCHIVE_USERNAME,
password: ARCHIVE_PASSWORD
});
// 假设返回数据结构为 { code: 200, data: { token: 'xxx' } }
// 请根据实际接口调整解析逻辑
if (response.data && response.data.data && response.data.data.token) {
cachedToken = response.data.data.token;
// 设置Token过期时间为1小时后
tokenExpiry = Date.now() + 3600 1000;
console.log('Token刷新成功');
return cachedToken;
} else {
throw new Error('登录响应格式异常');
}
} catch (error) {
console.error('获取档案系统Token失败:', error.message);
throw error;
}
}
/
核心接口:接收OA系统的文件上传请求并转发
路由示例:POST /api/gateway/upload
/
app.post('/api/gateway/upload', upload.single('file'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ success: false, message: '未检测到上传文件' });
}
// 1. 获取鉴权Token
const token = await getArchiveToken();
// 2. 构建转发给档案系统的FormData
const form = new FormData();
// 添加文件流
form.append('file', req.file.buffer, {
filename: req.file.originalname,
contentType: req.file.mimetype
});
// 添加业务元数据(如档案编号、归档年份等)
// 这些字段通常来自OA系统的req.body
if (req.body.metadata) {
form.append('metadata', JSON.stringify(req.body.metadata));
}
// 档案分类ID
if (req.body.categoryId) {
form.append('categoryId', req.body.categoryId);
}
// 3. 发起请求转发
const targetUrl = `${ARCHIVE_SYSTEM_BASE_URL}/api/documents/upload`;
const response = await axios.post(targetUrl, form, {
headers: {
...form.getHeaders(),
// 携带鉴权Token
'Authorization': `Bearer ${token}`
},
// 增加超时时间,防止大文件上传中断
timeout: 60000
});
// 4. 将档案系统的返回结果标准化后返回给OA
return res.json({
success: true,
data: response.data.data,
message: '归档成功'
});
} catch (error) {
console.error('转发请求失败:', error.response ? error.response.data : error.message);
return res.status(500).json({
success: false,
message: '网关转发失败: ' + (error.response ? error.response.statusText : error.message)
});
}
});
// 健康检查接口
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
app.listen(PORT, () => {
console.log(`档案集成网关已启动,监听端口: ${PORT}`);
});
为了保持代码的灵活性,我们将档案系统的连接信息提取到配置文件中。在项目根目录下创建.env文件。请务必将以下内容中的URL和账号密码替换为你实际环境的真实数据。
网关监听端口
PORT=3000
档案系统内网地址(请替换为实际IP或域名)
ARCHIVE_BASE_URL=http://192.168.1.100:8080
档案系统管理员账号(需要有归档权限)
ARCHIVE_USER=admin
ARCHIVE_PASSWORD=your_secure_password
关键配置说明:
getArchiveToken函数是一个通用模板。如果档案系统使用Cookie而非Bearer Token,需修改axios请求配置,将withCredentials: true加入,并手动设置Cookie头。为了防止网关程序意外退出,我们需要使用PM2进程管理工具来守护服务。这能确保服务器重启或程序崩溃时服务自动拉起。

1. 全局安装PM2
npm install pm2 -g
2. 启动服务
在项目目录下执行:
pm2 start index.js --name "archive-gateway"
3. 配置开机自启
生成开机启动脚本并保存:
pm2 startup
pm2 save
4. 查看运行日志
如果对接出现问题,实时查看日志是排查的关键:
pm2 logs archive-gateway
直接暴露Node.js端口存在安全风险,且无法处理HTTPS。标准做法是在本机或前置服务器配置Nginx。编辑Nginx配置文件(通常位于/etc/nginx/conf.d/archive_gateway.conf),添加以下配置:
server {
listen 80;
server_name archive-gateway.yourdomain.com; 替换为你的域名或内网IP
client_max_body_size 100M; 允许上传大文件,根据档案大小调整
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
超时设置,防止大文件传输超时
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
}
}
配置完成后,重载Nginx使配置生效:
nginx -t
systemctl reload nginx
至此,API网关已搭建完成。现在你的OA系统只需要调用网关暴露的接口,即可完成归档,无需直接与复杂的档案系统交互。
测试命令(使用curl模拟OA系统请求):
假设有一个名为test.pdf的文件需要归档:
curl -X POST \
http://192.168.1.200/api/gateway/upload \
-F 'file=@/path/to/test.pdf' \
-F 'categoryId=101' \
-F 'metadata={"title":"测试档案","creator":"张三"}'
预期返回结果:
{
"success": true,
"data": {
"docId": "ARC20231027001",
"status": "archived"
},
"message": "归档成功"
}
如果返回上述JSON结构,说明集成成功。此时,你可以让OA系统的开发人员将原本调用昂贵厂商接口的地址,替换为该网关地址,字段映射保持一致即可。这一方案完全切断了厂商的接口费用依赖,且拥有代码控制权,后续若档案系统升级,只需修改index.js中的转发逻辑即可,零成本维护。
安顺数字档案馆系统:让档案管理从“老黄牛”变“智能管家”