一个有效的会计师事务所档案管理系统,核心是解决审计底稿、报告、凭证等文件的电子化、结构化存储与权限控制。我们采用“前端+后端+存储”的轻量级架构,确保低成本、易部署。
你需要准备以下软件,所有工具均为开源或免费:
在服务器或本地电脑上安装Node.js。访问 https://nodejs.org 下载LTS版本并安装。安装后,打开终端(Windows为CMD或PowerShell,Mac/Linux为Terminal),运行以下命令验证安装:
``` node --version npm --version ```后端负责处理文件上传、信息存储和提供访问接口。
创建一个名为`audit-archive`的文件夹,并在其中初始化项目:
``` mkdir audit-archive cd audit-archive npm init -y ```然后安装必要的依赖包:
``` npm install express sqlite3 multer cors ```express用于创建Web服务,sqlite3用于操作数据库,multer用于处理文件上传,cors用于解决跨域访问问题。
在项目根目录下,创建一个名为`database.js`的文件,并写入以下完整代码来初始化数据库:
``` const sqlite3 = require('sqlite3').verbose(); const path = require('path'); // 连接数据库(如果不存在则会创建) const dbPath = path.join(__dirname, 'archive.db'); const db = new sqlite3.Database(dbPath, (err) => { if (err) { console.error('数据库连接失败:', err.message); } else { console.log('已连接到 SQLite 数据库。'); createTables(); } }); function createTables() { // 客户表 db.run(`CREATE TABLE IF NOT EXISTS clients ( id INTEGER PRIMARY KEY AUTOINCREMENT, client_code TEXT UNIQUE NOT NULL, -- 客户编号,如C2024001 client_name TEXT NOT NULL, -- 客户名称 contact_person TEXT, -- 联系人 contact_phone TEXT -- 联系电话 )`); // 项目表 db.run(`CREATE TABLE IF NOT EXISTS projects ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_code TEXT UNIQUE NOT NULL, -- 项目编号,如P2024-AUDIT-001 project_name TEXT NOT NULL, -- 项目名称 client_id INTEGER NOT NULL, -- 关联客户ID project_year INTEGER NOT NULL, -- 项目所属年度 FOREIGN KEY (client_id) REFERENCES clients (id) )`); // 档案文件表 db.run(`CREATE TABLE IF NOT EXISTS archives ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_name TEXT NOT NULL, -- 原始文件名 stored_path TEXT NOT NULL, -- 服务器存储路径 file_type TEXT, -- 文件类型,如:审计报告、底稿、凭证 project_id INTEGER NOT NULL, -- 关联项目ID upload_time DATETIME DEFAULT (datetime('now', 'localtime')), -- 上传时间 uploader TEXT, -- 上传人 FOREIGN KEY (project_id) REFERENCES projects (id) )`); console.log('数据表已就绪。'); } module.exports = db; ```在根目录创建`server.js`文件,这是服务的主入口。复制以下完整代码:
``` const express = require('express'); const multer = require('multer'); const cors = require('cors'); const path = require('path'); const fs = require('fs'); const db = require('./database.js'); const app = express(); const PORT = 3000; // 启用CORS和JSON解析 app.use(cors()); app.use(express.json()); // 配置multer文件存储 const storage = multer.diskStorage({ destination: function (req, file, cb) { // 根据前端传递的 projectId 和 year 动态创建存储目录 const { projectId, year } = req.body; const dirPath = path.join(__dirname, 'uploads', `Y${year}`, `P${projectId}`); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } cb(null, dirPath); }, filename: function (req, file, cb) { // 文件名格式:时间戳_原始文件名,防止重名 const timestamp = Date.now(); const originalName = file.originalname; cb(null, `${timestamp}_${originalName}`); } }); const upload = multer({ storage: storage }); // 1. 新增客户接口 app.post('/api/client', (req, res) => { const { client_code, client_name, contact_person, contact_phone } = req.body; const sql = `INSERT INTO clients (client_code, client_name, contact_person, contact_phone) VALUES (?, ?, ?, ?)`; db.run(sql, [client_code, client_name, contact_person, contact_phone], function(err) { if (err) { res.status(500).json({ error: err.message }); return; } res.json({ message: '客户创建成功', clientId: this.lastID }); }); }); // 2. 新增项目接口 app.post('/api/project', (req, res) => { const { project_code, project_name, client_id, project_year } = req.body; const sql = `INSERT INTO projects (project_code, project_name, client_id, project_year) VALUES (?, ?, ?, ?)`; db.run(sql, [project_code, project_name, client_id, project_year], function(err) { if (err) { res.status(500).json({ error: err.message }); return; } res.json({ message: '项目创建成功', projectId: this.lastID }); }); }); // 3. 文件上传接口 - 关键步骤 app.post('/api/upload', upload.single('archiveFile'), (req, res) => { // req.file 是 `archiveFile` 文件的信息 // req.body 将包含文本字段,如 projectId, fileType, uploader if (!req.file) { return res.status(400).json({ error: '未上传任何文件' }); } const { projectId, fileType, uploader } = req.body; const fileName = req.file.originalname; const storedPath = req.file.path; // multer存储的完整路径 const sql = `INSERT INTO archives (file_name, stored_path, file_type, project_id, uploader) VALUES (?, ?, ?, ?, ?)`; db.run(sql, [fileName, storedPath, fileType, projectId, uploader], function(err) { if (err) { // 如果数据库插入失败,尝试删除已上传的文件 fs.unlink(storedPath, (unlinkErr) => {}); res.status(500).json({ error: err.message }); return; } res.json({ message: '文件上传成功', fileId: this.lastID, path: storedPath }); }); }); // 4. 按客户查询项目接口 app.get('/api/projects/by-client/:clientId', (req, res) => { const clientId = req.params.clientId; const sql = `SELECT FROM projects WHERE client_id = ? ORDER BY project_year DESC`; db.all(sql, [clientId], (err, rows) => { if (err) { res.status(500).json({ error: err.message }); return; } res.json(rows); }); }); // 5. 按项目查询档案接口 app.get('/api/archives/by-project/:projectId', (req, res) => { const projectId = req.params.projectId; const sql = `SELECT id, file_name, file_type, upload_time, uploader FROM archives WHERE project_id = ? ORDER BY upload_time DESC`; db.all(sql, [projectId], (err, rows) => { if (err) { res.status(500).json({ error: err.message }); return; } res.json(rows); }); }); // 启动服务器 app.listen(PORT, () => { console.log(`档案管理后端服务已启动,运行在 http://localhost:${PORT}`); }); ```
保存文件后,在终端运行 `node server.js`。如果看到“档案管理后端服务已启动”的日志,说明后端服务已正常运行。
前端由三个核心页面组成:客户管理、项目管理和档案上传/查询。我们在项目根目录创建一个`public`文件夹,并在其中放置HTML文件。
在`public`文件夹下创建此文件,用于新增和关联客户与项目。
```| 项目编号 | 项目名称 | 年度 |
|---|
注意: 上面的`fetch(`${API_BASE}/client`)`接口在实际运行前并不存在,你需要先在`server.js`中简单添加一个获取客户列表的接口,代码如下,添加后重启服务:
``` // 在server.js的接口部分添加: app.get('/api/client', (req, res) => { const sql = `SELECT id, client_code, client_name FROM clients ORDER BY id DESC`; db.all(sql, [], (err, rows) => { if (err) { res.status(500).json({ error: err.message }); return; } res.json(rows); }); }); ```在`public`文件夹下创建此文件,用于上传文件并查询历史档案。
```| 文件名 | 类型 | 上传时间 | 上传人 |
|---|