资讯详情

基于Vue+Node.js的档案室管理系统开发实践

📅 2026/10/17 16:46:12 | 华诺云谱 👁 阅读
基于Vue+Node.js的档案室管理系统开发实践
做档案室管理系统这个需求这几年我前后接触过好几次。场景基本都一样单位里纸质档案堆满几个房间借阅登记靠一个本子手写谁借走了哪卷档案什么时候还管理员心里得靠记忆去翻有时候换个管理员整个档案底账都理不清。Excel里倒是录了一部分可字段混乱、缺档号、重复录入也时有发生。我这次接手的这个项目就是给某单位档案室做一套覆盖档案登记、检索、借阅、归还、统计全流程的管理系统技术栈锁定了前端Vue Element UI后端Node.js数据库用MySQL。整个系统从需求梳理到上线部署大概六周时间完成这篇文章就把过程和一些关键实现拆开讲清楚包括项目初期的架构设计、数据库的表结构规划、前后端联调时的坑以及上线之后暴露的问题和补救办法。如果你现在也准备做一个类似的内部管理系统或者刚接触Vue Node.js这个组合又不想去看那种又长又散的教程这篇文章应该能帮你省不少时间。我会把整个系统的骨架、每一步的设计逻辑、以及我怎么排查问题的思路都放出来。1. 需求拆解与整体设计思路1.1 档案室管理到底在管什么别一上来就谈技术先弄明白业务。我最初接触需求时对方负责档案管理的同事给了我一份纸质表格上面写着档案编号、题名、责任者、形成日期、保管期限、密级、页数、存放位置。就这么一张表加上一个借阅登记簿就是他们档案室的全部家当。实际跟了几圈流程之后我发现核心痛点其实是四件事第一档案检索效率低。很多档案是纸质件电子台账零散检索的时候既不知道Excel里有没有记录也不知道档案实体放在哪个房间哪组柜子的第几层。如果台账登记不全就只能靠人翻一等就是半天。第二借阅过程完全靠自觉。档案借出去之后借阅人、借出时间、审批人这几条关键信息全部依赖手工经常出现档案“借出容易归还难”的情况档案室到底有多少卷在外头谁也说不清。第三权限管理非常粗放。哪个员工能看普通档案哪些档案涉及单位内部事项甚至密级没有机制去区分。纸质时代靠锁柜子电子化之后如果用一套系统就必须有角色和审批流程的约束。第四盘点与统计基本是做样子。上级检查要求提供档案数量、借阅频次、当前在借列表一堆报表手工去抄得花一天。所以我们最终确认的业务范围就是档案目录管理、多条件检索、借阅申请与审批、归还登记、用户与角色管理、分类字典管理、统计看板。这个范围做出来已经能覆盖九成以上档案室的日常需求。1.2 技术选型为什么是Vue Element UI Node.js关于技术选型我倒是想多写几句因为这个组合经常被初学者当成“培训班标配”容易被低估。但实际上它特别适合做这类内部管理系统。Vue端选Vue 2还是Vue 3我这次实际用的是Vue 2 Element UI原因很直接团队对这个组合最熟悉而且Element UI的表格、表单、弹窗、分页组件都内置了完整交互做中后台管理系统是出了名的省力。如果你是现在新起一个项目我建议直接上Vue 3 Element Plus组件API更统一响应式性能也更好思路是完全一样的。考虑到很多存量项目还停留在Vue 2生态这里我用Vue 2语法来讲你迁移过去不会有什么障碍。Element UI这个组件库的价值在于它替你解决了一整套“看起来不丑且能干活”的界面基础。你自己写一个带搜索表单、操作按钮、分页的资产负债表格通常要两三天用Element UI拼装一个下午就能搭完而且交互细节、浏览器兼容性都已经被大量项目踩过坑。做内部系统最重要的不是炫技而是稳定、清晰、改动成本低。后端选Node.js一方面是因为前后端统一使用JavaScript上下文切换成本很低另一方面是Node.js的异步模型处理JSON请求性能很足够。像档案管理系统这种场景并发量根本到不了十万级别难的是业务状态多、流程长、数据校验琐碎。Node.js加上Express框架中间件的组织方式非常直观要加鉴权就在路由前面插一个中间件要加日志就再插一个调试起来特别顺手。数据库选MySQL没那么多花哨原因档案数据是强关系结构卷宗、分类、借阅记录、用户之间互相引用用关系型数据库天然严谨。MySQL加上一张表一张表的规范化设计后续统计查询写SQL也非常直观。1.3 系统模块规划与页面草图和负责人对齐需求后我把系统拆成了六个核心模块这里直接列出来你可以对照自己的项目看模块核心功能涉及角色首页看板档案总数、本月借出、待审批数量、即将到期提醒所有登录用户档案管理档案列表、新增/编辑/删除、多条件检索、分类筛选、批量导入管理员、档案员借阅管理借阅申请、审批通过/驳回、登记归还、借阅历史所有用户分类字典档案分类、密级、保管期限选项维护管理员用户管理用户新增、角色分配、重置密码管理员系统设置登录日志、操作日志查询管理员角色先定为三种管理员、档案员、普通用户。管理员能管理用户和系统配置档案员负责档案登记上架、处理借阅普通用户只能申请借阅和查看档案目录信息。这样三个角色对应的菜单权限就清楚多了。第一版页面设计的时候我直接在纸上画了草图没有先进原型工具。每张清单页都遵循同一个骨架顶部筛选栏下拉选择器 搜索输入框 查询/重置按钮、中部表格列内容按业务字段定、底部为分页器新增、导入操作放右上角。这个骨架用户接受度很高因为不管进入哪个模块第一眼就知道到哪里搜、到哪里操作。2. 数据库设计与核心表结构2.1 实体关系图与表清单系统的数据关系不算复杂但每一步都要想清楚。我总共设计了五张主要表用户表、角色权限表简化为在用户表里加角色字段、档案分类表、档案主表、借阅记录表。先看建表SQL中几个关键表的设计思路。用户表核心字段字段类型说明idINT AUTO_INCREMENT主键usernameVARCHAR(50)登录名唯一passwordVARCHAR(100)加密后的密码nicknameVARCHAR(50)显示名称roleVARCHAR(20)admin / archivist / userstatusTINYINT1启用0禁用create_timeDATETIME创建时间密码我用了bcryptjs做不可逆加密字段长度直接留100避免后续改加密方式时又要扩字段。档案分类表设计得短小精悍CREATE TABLE t_category ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL COMMENT 分类名称如文书档案、人事档案, parent_id INT DEFAULT 0 COMMENT 父分类0为顶级, sort_order INT DEFAULT 0 COMMENT 排序号 );分类支持两级就够了没必要无限层级档案分类在绝大多数单位内都是有限枚举比如文书、人事、财务、科技、音像、实物这么几类。档案主表是整个系统的核心字段需要仔细设计。我挑几个容易踩坑的说明CREATE TABLE t_archive ( id INT AUTO_INCREMENT PRIMARY KEY, file_no VARCHAR(30) NOT NULL COMMENT 档号业务唯一标识, title VARCHAR(200) NOT NULL COMMENT 题名, category_id INT NOT NULL COMMENT 所属分类, author VARCHAR(100) COMMENT 责任者, file_date DATE COMMENT 形成日期, retention_period VARCHAR(20) DEFAULT 长期 COMMENT 保管期限, secret_level VARCHAR(10) DEFAULT 普通 COMMENT 密级, page_count INT DEFAULT 1 COMMENT 页数, location_desc VARCHAR(200) COMMENT 存放位置如A区-03柜-02层, status TINYINT DEFAULT 1 COMMENT 1在库2借出0下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_file_no (file_no) );这里我把“存放位置”作为一个普通字符串字段而不是拆成几个字段。因为档案室的物理位置描述往往非常本土化有的是“第3排第2列第5格”有的是“A1-3-02”拆成三个字段反而画蛇添足还是用一个文本字段让管理员自己填最灵活。借阅记录表是这个系统里状态变化最多的一张表CREATE TABLE t_borrow ( id INT AUTO_INCREMENT PRIMARY KEY, archive_id INT NOT NULL COMMENT 档案ID, archive_file_no VARCHAR(30) NOT NULL COMMENT 冗余档号方便查询, user_id INT NOT NULL COMMENT 借阅人ID, apply_reason VARCHAR(500) COMMENT 借阅事由, apply_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 申请时间, status TINYINT DEFAULT 0 COMMENT 0待审批1借出中2已归还3已驳回4已过期, approver_id INT DEFAULT NULL COMMENT 审批人ID, approve_time DATETIME DEFAULT NULL COMMENT 审批时间, borrow_time DATETIME DEFAULT NULL COMMENT 实际借出时间, return_time DATETIME DEFAULT NULL COMMENT 归还时间, remark VARCHAR(500) COMMENT 备注 );为什么借阅表里要冗余一个file_no字段这是我觉得值得分享的一个小经验。业务人员查借阅记录时最常用的搜索条件是档号如果每次都要join档案主表去取档号虽然也能查但写SQL麻烦、索引利用也不直接。冗余一个字段查询借阅记录时就能直接where archive_file_no like %xxx%不用join数据量大了之后性能优势更明显。2.2 档号规则怎么设计档号是档案系统的命脉它是业务上用来定位一卷档案的唯一钥匙。设计档号规则前我们专门跟档案管理员聊了很久对方现有的编号方式。现成方式有两种一是纯流水号比如“A-592”二是分类加年度加流水比如“文书档案2024-001”。后者明显更好用因为它一看到档号就能知道这是哪一类档案、哪一年归档的检索上也有天然的前缀筛选效果。我最终采用了组合编码分类代码-年份-四位流水号。分类代码在分类表里维护一个短编码字段比如文书档案用WS人事档案用RS。这样生成的档号形如RS-2024-0017。唯一约束用SQL建在file_no字段上后端插入前再做一次重复校验。这样既防止了并发情况下偶发的重复插入又不至于把所有校验都压在数据库错误上。2.3 借阅状态流转设计借阅流程我设计了五个状态这条状态机是整个系统逻辑里最关键的部分待审批 → 借出中 → 已归还待审批 → 已驳回借出中 → 已过期管理员手动标记或定时任务扫描用户提交申请后状态为0待审批档案员进入系统看到待审批列表可以点通过或驳回点通过后状态变为1借出中同时给档案主表的status字段置为2借出中归还登记后状态变为2已归还同时把档案主表status置回1在库。这里有一个我建议你特别注意的地方档案主表的status和借阅记录的状态必须同步更新而且必须在同一个数据库事务里执行。否则就会出现“借阅记录显示已归还但档案状态还是借出中”的数据不一致。我就是第一次上线之后才在一次人工盘点中发现的这个漏洞修复时查了半天历史数据。3. 前端页面的搭建与核心交互3.1 工程结构与登录拦截前端我用Vue CLI老一套方式初始化项目目录结构大概是这样的src/ api/ # 按模块拆分的接口请求 archive.js borrow.js login.js components/ # 共用组件 Pagination.vue router/ index.js # 路由配置 导航守卫 store/ index.js # Vuex存用户信息 modules/ user.js views/ Login.vue Dashboard.vue archive/ ArchiveList.vue ArchiveForm.vue borrow/ BorrowList.vue BorrowApply.vue manager/ UserList.vue路由这边我重点想做的是登录拦截。系统要求未登录不能访问任何页面这个效果用Vue Router的全局前置守卫实现router.beforeEach((to, from, next) { const token sessionStorage.getItem(token); if (to.path ! /login !token) { next(/login); } else { next(); } });token存sessionStorage关掉浏览器就失效对内部系统来说这种策略简洁可靠。用户信息放在Vuex里store初始化时从sessionStorage重新读取避免刷新页面后用户信息丢失。另外每个页面的菜单是根据角色生成的不应该在前端写死。我做法是后端登录接口返回用户信息时带一个permissions数组前端根据这个数组动态渲染菜单项。比如普通用户的菜单里没有档案分类管理这一项即便手动输URL进入对应页面前端也会在守卫里再拦一道。这一块其实还藏着一个很多人忽略的问题前端菜单隐藏只是体验上的真正的安全防线必须做在后端。前端隐藏菜单、路由守卫拦一下只能防普通用户误操作不该让普通用户看到的东西后端必须同样拦截。我在后端每个管理接口上都加了角色判断中间件比如管理员、档案员才能调用档案新增和修改接口。这一点务必记得。3.2 档案列表页的表格、搜索与分页档案列表页是整个系统使用频率最高的页面交互设计要求就是快和顺手。搜索区我放了一排控件关键字输入框匹配档号或题名、分类下拉选择、密级下拉、保管期限下拉、一个查询按钮一个重置按钮。表单提交后重新拉第一页数据。表格列设置了档号、题名、分类、形成日期、密级、保管期限、存放位置、状态、操作。状态列用el-tag展示绿色“在库”橙色“借出”红色“下架”。操作列根据档案状态动态显示在库的显示“借阅”按钮借出的显示“查看借阅记录”管理员和档案员多一个“编辑”和“删除”。分页用的el-pagination组件逻辑是当前页pageNum每页条数pageSize。翻页时请求后端接口等数据返回之后更新表格。这块代码几乎每个Vue项目都会写示例直接放出来el-table :dataarchiveList v-loadingloading border stripe el-table-column propfile_no label档号 width160/el-table-column el-table-column proptitle label题名 min-width200 show-overflow-tooltip/el-table-column el-table-column propcategoryName label分类 width120/el-table-column el-table-column label状态 width100 template slot-scopescope el-tag :typestatusMap[scope.row.status].type{{ statusMap[scope.row.status].text }}/el-tag /template /el-table-column el-table-column label操作 width200 fixedright template slot-scopescope el-button sizemini typetext clickhandleBorrow(scope.row)借阅/el-button el-button sizemini typetext v-ifcanEdit clickhandleEdit(scope.row)编辑/el-button el-button sizemini typetext classdanger-text clickhandleDelete(scope.row)删除/el-button /template /el-table-column /el-table题名列一定要加show-overflow-tooltip属性因为档案题名动不动就三四十个字不省略会把表格撑得没法看。这是使用Element UI表格时很细节但体验提升明显的点。新增和编辑用同一个弹窗组件通过传入的row数据区分是新增还是编辑。表单校验规则是根据业务要求定的档号必填且校验格式题名必填分类必选形成日期必填。校验通过后再调接口。还有一个交互细节编辑弹窗关闭之后表单校验状态要重置否则下次打开时上一个表单的红包提示还留着。我用this.$refs.form.resetFields()清掉校验状态和数据。3.3 借阅申请的交互流程借阅流程的交互我走了三步第一步用户在档案列表页点击“借阅”弹出对话框填写借阅事由。这里注意到前端只是收集信息具体能不能借还要看后续审批。第二步档案员在借阅管理页面看到待审批列表点“详情”查看申请的事由和相关档案信息然后点通过或驳回。通过时系统弹窗询问实际借出时间默认当前时间。第三步归还操作。用户在借阅记录列表找到“借出中”的记录点“归还”系统自动记录归还时间并修改档案状态。这个流程的技术点在于前端页面之间的状态联动。档案列表页显示的是档案主表的status字段借阅管理页则查借阅记录表的状态。前端操作归还时后端接口要同时修改借阅状态和档案状态我设计成纯后端事务操作前端叫一个接口就完事。这样做的好处是就算前端有多个入口触发同一个操作也不会出现业务逻辑分散问题。4. Node.js后端接口与关键实现4.1 后端工程结构与统一响应后端用的是Express框架项目结构按模块划分server/ app.js # 入口中间件注册 config/ db.js # MySQL连接池 jwt.js # 密钥与过期时间配置 middlewares/ auth.js # 登录校验 roleCheck.js # 角色校验 upload.js # 文件上传配置 routes/ login.js archive.js borrow.js category.js user.js stats.js controllers/ # 各模块业务逻辑 utils/ response.js # 统一响应封装这里我建议把接口返回格式统一成这种结构{ code: 0, message: success, data: { } }前端axios拦截器统一处理code不为0时弹出message对应的错误提示。这样写接口时不用每处都自己封装重复的成功失败逻辑调试和对接效率高很多。实现上我写了一个工具函数exports.ok (data) ({ code: 0, message: success, data }); exports.fail (message, code 1) ({ code, message, data: null });所有接口返回都包裹这两个方法。4.2 登录鉴权与JWT实现登录流程算是这个系统最核心的基础设施。用户输入用户名密码后端用bcryptjs比对密码哈希比对成功则签发一个JWT返回前端。之后前端请求在axios拦截器里带上Authorization头后端中间件统一解析验证。签发JWT的代码const jwt require(jsonwebtoken); const { secret, expiresIn } require(../config/jwt); exports.generateToken (user) { return jwt.sign( { id: user.id, username: user.username, role: user.role }, secret, { expiresIn } ); };校验中间件const auth (req, res, next) { const token req.headers.authorization?.replace(Bearer , ); if (!token) return res.status(401).json({ code: 401, message: 未登录 }); try { const payload jwt.verify(token, secret); req.user payload; next(); } catch (e) { return res.status(401).json({ code: 401, message: 登录已过期 }); } };角色校验中间件则是在auth之后再用const roleCheck (...roles) { return (req, res, next) { if (!roles.includes(req.user.role)) { return res.status(403).json({ code: 403, message: 无权限操作 }); } next(); }; }; // 使用示例管理员和档案员才能新增档案 router.post(/archive, auth, roleCheck(admin, archivist), archiveCtrl.add);关于token过期时间内部系统我设置了12小时对绝大多数上班场景足够也不会造成频繁重新登录。token过期后前端拦截器检测到401自动跳转登录页。4.3 档案分页查询与多条件筛选这是所有查询页面都会用到的模式值得认真写一下。前端传过来的查询参数包括pageNum、pageSize、keyword档号或题名的模糊匹配、categoryId、secretLevel、status。后端动态拼接SQLasync list(req, res) { const { pageNum 1, pageSize 10, keyword , categoryId, secretLevel, status } req.query; const offset (pageNum - 1) * pageSize; let where WHERE 11 ; const params []; if (keyword) { where AND (file_no LIKE ? OR title LIKE ?) ; params.push(%${keyword}%, %${keyword}%); } if (categoryId) { where AND category_id ? ; params.push(categoryId); } if (secretLevel) { where AND secret_level ? ; params.push(secretLevel); } if (status ! undefined status ! ) { where AND status ? ; params.push(status); } const [rows] await db.query( SELECT a.*, c.name AS category_name FROM t_archive a LEFT JOIN t_category c ON a.category_id c.id ${where} ORDER BY a.id DESC LIMIT ? OFFSET ?, [...params, Number(pageSize), offset] ); const [countRows] await db.query( SELECT COUNT(*) AS total FROM t_archive a ${where}, params ); res.json(gen.ok({ list: rows, total: countRows[0].total, pageNum: Number(pageNum), pageSize: Number(pageSize) })); }这里有几个小坑要提醒LIMIT和OFFSET在mysql2驱动里传参数时直接用参数占位符是没问题的但注意必须转成Number类型否则数据库驱动可能报语法错误。查询条件多时动态拼接where很容易忘加空格。我习惯用WHERE 11开头条件再逐条 append这样每个条件前面都有AND逻辑不会乱性能影响可以忽略。模糊匹配两边的%必须手动拼接直接传参数进去不会自动带所以我用了%\${keyword}%的写法。4.4 文件上传Excel批量导入与附件管理纸质档案数量比较多一间档案室动辄几千卷逐条手输效率太低。我在系统里加了Excel批量导入功能管理员下载模板填好内容后上传后端解析批量写入数据库。Excel解析用的库是node-xlsx结合muler实现文件上传。核心流程前端用el-upload组件选文件上传到后端的/api/archive/import接口。后端先把文件保存到uploads目录的临时位置。用xlsx库读取工作表把每一行映射成档案对象。校验必填字段和档号格式。分批插入每100条执行一次batch insert提高效率。返回导入成功多少条、失败多少条失败时把原因汇总成文本返回前端展示。这个环节有一次印象很深某同事导入了一个1200行的Excel第一次跑的时候超时了。排查发现是因为逐行插入1200条大概要十几秒。改成批量插入之后时间从十几秒降到了两秒以内。内部系统虽然并发要求不高但也别忽略这种明显可以优化的性能瓶颈。文件附件的应用场景相对少因为这款系统是档案目录管理不要求持久化存储扫描件。如果需要做扫描件上传我建议把文件存储交给独立文件服务比如FastDFS或者对象存储而不是直接丢到应用服务器本地磁盘否则做负载均衡的时候文件同步会是噩梦。4.5 借阅事务与并发控制借阅数据的一致性我在前面已经提到要靠事务保证。写出来代码逻辑是这样的const connection await db.getConnection(); try { await connection.beginTransaction(); // 1. 校验档案当前状态 const [archives] await connection.query( SELECT * FROM t_archive WHERE id ? FOR UPDATE, [archiveId] ); const archive archives[0]; if (!archive || archive.status ! 1) { throw new Error(当前档案不可借出); } // 2. 更新档案状态为借出 await connection.query( UPDATE t_archive SET status 2 WHERE id ?, [archiveId] ); // 3. 更新借阅记录状态为借出中 await connection.query( UPDATE t_borrow SET status 1, borrow_time NOW(), approver_id ? WHERE id ?, [req.user.id, borrowId] ); await connection.commit(); } catch (e) { await connection.rollback(); throw e; } finally { connection.release(); }这里SELECT FOR UPDATE是防止并发下两个审批人同时审批同一笔借阅都查到档案是“在库”状态然后都去更新。加上行级锁后第二个事务的查询会等第一个事务提交查询结果就会变成“借出中”从而触发异常提示。这是并发场景下应该想到的基本手段。5. 联调与部署过程中的经验坑5.1 跨域与前端代理开发阶段前端跑在8080端口后端跑在3000端口跨域问题不可避免。开发时我推荐用Vue CLI的proxy配置简单无感// vue.config.js module.exports { devServer: { proxy: { /api: { target: http://localhost:3000, changeOrigin: true, pathRewrite: { ^/api: /api } } } } };前端所有请求都以/api开头代理就把请求转发到后端。这样前端代码里不用写完整的后端地址之后部署到线上配合Nginx还能继续用同样的代理逻辑。生产环境我在Nginx里做反向代理配置server { listen 80; server_name archive.example.com; root /usr/share/nginx/html; index index.html; location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } }前端打包后用try_files保证刷新页面时路由不会404这是SPA部署的一个经典坑。5.2 后端进程守护Node.js后端直接裸跑的话进程挂了就完蛋。我当时用PM2做进程守护一条命令搞定pm2 start app.js --name archive-server --max-memory-restart 300M pm2 savePM2的好处是自带崩溃自动重启、资源限制和日志管理对中小型Node服务来说非常实用。后端代码更新部署时git pull后直接pm2 reload archive-server即可几乎零停机。5.3 时间字段时区问题联调过程中有一个特别容易忽略的问题前端显示的时间和数据库存的时间差8小时。排查下来发现是MySQL连接时区设置的问题。后端连接数据库时在连接字符串上加一个参数解决// db.js const db mysql.createPool({ host: localhost, user: root, password: ******, database: archive_db, charset: utf8mb4, dateStrings: true });dateStrings:true会让MySQL返回的DATE和DATETIME类型直接以字符串形式返回避免经过Node层的Date对象转换引入时区偏移。这个配置我强烈建议做MySQL Node项目的时候直接写上省掉很多时间格式化的麻烦。前端展示时再用dayjs把时间格式化成项目想要的格式比如“2024-11-30 14:30”。组件库里的时间日期选择器返回的值本身也是字符串传参时直接传给后端后端再落库全程无隐式转换干净利落。5.4 导出结果的几个踩坑细节系统里有导出Excel的需求我也用node-xlsx实现。第一个坑是导出大数据量比如导出全年所有借阅记录一次性查全量内存容易飙升。我的处理是导出时也带查询条件并且把导出接口的并发量做限制。对内部系统来说按条件分批导出的体验并不差。第二个坑是Excel中的档号被自动当成数字处理导致以0开头的档号前面0被吞掉。这是Excel内部机制不是后端代码问题。规避方案是导出Excel时把档号列的单元格格式设置为文本型。xlsx库设置单元格类型为z文本前端拿到数据后大部分现代浏览器打开的Excel能正确显示完整档号。5.5 操作日志的必要性档案系统涉及大量数据变更一旦有人误删或者修改了关键档案信息没有日志就无从追溯。我在系统里加了一个操作日志表记录操作人、操作类型、操作对象、操作时间、请求参数摘要。实现方式不复杂后端写个中间件对需要记录的操作路由包裹一下在controller执行前后把日志落库。一开始我甚至考虑过用AOP类似的切面日志后来觉得对这个体量来说太重了。内部系统最重要的是人能查到谁在什么时候动了什么数据满足审计线索要求即可。日志查询入口放在管理员的系统设置模块里权限只开放给管理员。6. 上线后的问题反思与可扩展方向6.1 上线初期暴露的问题系统上线之后前两周我一直在盯着使用反馈。问题主要集中在几类第一类是部分老同事不习惯系统操作。有些操作员之前在Excel里用筛选习惯了对系统里“下拉选择关键字搜索”的组合还不熟。我把搜索区做得更直白把常用查询条件放成默认展示并且增加搜索示例文案比如“输入档号或题名关键字”操作员理解成本降低了很多。第二类是档案员对状态流转偶尔理解错。比如申请已驳回用户再点借阅时如果没有提示会以为是系统bug。我特意在申请按钮下面加了一行提示文字说明被驳回后必须重新发起申请并附上驳回理由的展示。很多问题不是技术上的而是理解上的前端文案要多站在用户视角写。第三类是数据治理问题。系统上线后发现历史Excel导过来的数据里有不少档号重复、题名残缺、分类误填。因此我增加了一个数据校验报告功能在导入完成后输出异常数据清单提醒管理员修订。这个在上线初期帮上大忙。6.2 后续可以扩展的方向如果这个系统继续往下做我会优先考虑以下几个方向。第一扫码盘点。每卷档案贴一个二维码管理员用手机小程序扫码核对档案在库状态盘点的效率会比手工翻台账快非常多。二维码里编码档案ID小程序扫码后调后端接口返回该档案目录和状态管理员确认即可。第二OCR识别录入。如果要做档案扫描件的自动化著录可以用OCR从扫描图片中抽取题名、责任者、日期等元数据辅助管理员快速建档。这块自然语言处理和图像识别的工程量不小但不是做不了可以一期先做一个简单的扫描件上传和预览功能OCR抽取留到二期。第三提醒与日志增强。到期未归还的提醒从站内消息扩展到企业微信或邮件通知操作日志从简单文本升级成结构化审计报表。这些扩展不需要动核心数据表结构系统整体架构还有很大弹性。就我个人做了几个内部管理系统之后的体会来说vue nodejs Element UI这套组合做中后台业务系统开发效率、维护成本、上手难度综合来看性价比很高。项目难的不是框架本身而是对业务的抽象和信息模型的设计。档案室管理系统这个案例尤其典型——字段不算多但互相之间的状态流转、权限边界、数据一致性都需要想清楚。把业务细节和数据表结构梳理明白代码只是表达这些设计的过程而已。如果你正准备做类似系统建议从数据库表设计和档案编号规则开始动手表结构定了后面都是水到渠成的事。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑