news 2026/9/8 7:58:42

用Flask和SQLite开发轻量公文签收系统,告别纸质台账

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Flask和SQLite开发轻量公文签收系统,告别纸质台账

简介:一套基于经典ASP技术的简单公文签收系统,面向ASP初学者及需要快速搭建轻量公文流转应用的小型组织。系统覆盖文件上传、流程定义、自动提醒、签收审批、状态追踪、版本控制与权限管理等核心环节,结构简单却功能完整,适合作为课程设计或毕业设计参考。压缩包共57个文件,主体为27个ASP页面,另含11个JPG和11个GIF图片素材、2个INC公共文件、1个PSD界面设计稿,以及CSS样式、TXT说明、HTM帮助页、MDB数据库和BAK备份文件,可支撑前后台完整运行。整个资源包仅196KB,小巧便于部署。已有219人学习下载。代码注释清晰,目录划分明确。通过源码可快速掌握文件上传、用户管理、部门管理、信息发布等功能的实现思路,并借助说明文档、数据库备份和界面设计稿开展二次开发与界面定制。 办公室老张又抱着一摞盖着红章的公文,挨个科室跑着催签收,这是我决定动手做这套“简单的公文签收系统”的直接导火索。说是“简单”,其实功能闭环一点都不少:收文登记、线上签收、未签名单、台账查询,全部跑在一个轻量级Web服务上。这套系统就是为综合办公室、单位内勤这类人员准备的,不需要单独配服务器,一台能跑Python的电脑就够,代码量也不大,却能彻底告别纸质签收单满天飞、Excel台账漏登记的日子。

先说明一下我现在已经把这套系统实际用在了单位内网环境里,从需求梳理、数据库设计,到页面搭建、内网部署,全程没有引入任何重量级框架。下面我把整个做项目的思路、核心代码和踩过的坑原原本本写出来,想给你的部门或办公室也做一套类似工具的,可以直接照着往下走。

1. 项目背景与需求拆解

1.1 公文签收为什么会成为问题

大多数基层单位的公文签收,表面上看着是“签个名就完事”,但实际运行起来有几个很典型的痛点。第一是流转环节多,一份文件从收发室登记,经过办公室主任批办,再到分管领导圈阅,最后落到经办科室,中间任何一个人出差或开会,文件就被压在桌子上。第二是签收状态不透明,领导问“某某文件发了没有”,负责登记的人只能靠回忆,或者把整个文件夹翻一遍。第三是台账和纸质签收单对不上,每到月底统计办文量,就得人工逐张翻签收单,费时费力还容易漏。

这些问题单靠Excel表其实能解决一部分,但我试过之后发现不够用。原因是多人同时维护同一个Excel文件时,版本冲突、内容被覆盖、表格锁死这些事反复发生,而且系统不会主动提醒谁还没有签收。所以这个项目从一开始就不是“做一个花哨的系统”,而是“把签收这个动作管理起来”。

1.2 需求清单:先列清楚再动手

动手写代码之前,我把需求归纳成了四个核心功能点。第一是收文登记,办公室人员录入公文的标题、来文单位、收文日期、密级和办理时限;第二是签收确认,经办人在自己的待办列表里看到文件,点击签收并填写签收意见;第三是签收台账,所有历史文件的签收状态、签收时间、签收意见可以随时查询并导出;第四是催办提醒,系统能列出三天以上未签收的文件,方便办公室人员定向催办。

围绕这四个功能,我主动砍掉了一些暂时用不上的东西,比如工作流审批、电子签章、移动端App。原因是这套系统的定位很明确:做一个轻量、易维护、能解决实际流转问题的工具,而不是建设一套完整OA。等业务量真的上来了,再迁移到成熟的协同办公平台也不迟。需求收敛后,整个系统的边界就非常清晰了,后面开发的时候每一个页面、每一段代码都有明确的目的。

2. 技术方案选型与整体设计

2.1 为什么不选“大而全”的OA

在动工之前,我也认真考虑过直接用市面上的OA系统,或者是钉钉、企业微信里的审批应用。但结合单位的实际情况,这些方案有两个绕不开的问题。一是部署成本高,OA系统通常需要单独的服务器和维护人员,哪怕SaaS版本也要按人头收费,为几十个人的签收需求开通一年的费用并不划算。二是流程太死板,OA里的公文模块往往预设了复杂的审批链,而我们的实际流程就是“登记-签收-查询”,硬套通用流程反而增加了大家的学习成本。

自己开发一套轻量系统,最大的优势就是可以按需定制。界面上的每一个按钮都是我们业务里真实存在的动作,没有冗余操作,内网部署也完全不依赖外部网络。这种“定制感”带来的用户体验,是通用软件给不了的。

2.2 技术栈选定:Flask + SQLite + Jinja2

技术选型我特别保守,最终选用的是Python Flask加SQLite,配合Jinja2模板渲染页面。选择Flask的原因很直接,它是Python生态里最简单易上手的Web框架,单个文件就能写一个完整的应用,路由和请求处理逻辑非常直观,不需要像Django那样配置一整套项目骨架。SQLite则是为了零成本部署,它就是一个文件数据库,不需要额外安装数据库服务,数据全部存放在一个文件里,备份时把这个文件复制走就行。

前端部分我没有采用前后端分离的方案,也没有引入Vue或React,而是直接用Jinja2模板在服务端渲染页面。这样整个项目只有十来个模板文件,后端把签收状态、文件列表这些数据塞进模板,用户打开页面就能看到完整内容。对于内网这种低并发场景,这个组合的稳定性和维护成本都要远优于前后端分离架构。

2.3 系统整体流程与模块划分

系统的完整流程可以描述为:登记员在“收文登记”页面填写公文基本信息并提交,系统将公文存入数据库并生成一条唯一的公文编号;相关人员登录进入系统后,在主页面“待签收列表”中看到分配给本部门的文件,点击“签收”按钮,填写签收意见,系统记录签收人、签收时间;管理员进入“签收台账”页面,可以按日期、部门、签收状态筛选所有文件的流转情况,同时看到一个“未签收清单”,用来做催办。

整个应用从代码层面划分成了三个模块:一是models.py负责数据库表结构和初始化的逻辑;二是app.py作为主程序,包含所有页面路由和业务处理逻辑;三是templates目录存放页面模板文件。三个模块各司其职,哪怕是我这种习惯边写边改的人,也能随时定位问题出在哪个环节。

3. 数据库设计与核心功能实现

3.1 表结构设计:够用又不啰嗦

数据库设计是整个系统的基础,我在这一块没有过度设计,只用到了三张表。第一张users用户表很简单,字段是用户ID、用户名、密码哈希、所属部门和角色。其中角色只区分“admin”和“user”两种,admin可以登记文件和查看全部台账,普通用户只能签收和查看自己相关的文件。密码没有存明文,用的是werkzeug自带的密码哈希函数,别人即使拿到数据库文件也看不到原始密码。

第二张documents公文表是核心表,字段包括公文编号、文件标题、来文单位、收文日期、办理时限、密级、签收状态、创建人和创建时间。密级字段我没有做复杂的控制,在这里只是作为一个信息项展示,毕竟真正涉密的文件根本不应该走这套系统。签收状态我用了整数来存,0代表未签收,1代表已签收,这样后续做统计时非常方便。

第三张sign_logs签收记录表负责记录每一次签收操作,字段有签收记录ID、公文ID、签收人、签收时间、签收意见。之所以单独建一张表而不是直接往公文表里写签收人,是考虑到同一份文件可能被多个部门签收,一对多关系用独立的日志表更合理。三张表建好之后,配合外键关联就能覆盖所有核心业务场景了。

建表语句我放在了init_db.py脚本里,关键代码如下:

import sqlite3 conn = sqlite3.connect("sign_system.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, department TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'user' ) """) cursor.execute(""" CREATE TABLE IF NOT EXISTS documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, doc_no TEXT UNIQUE NOT NULL, title TEXT NOT NULL, sender TEXT NOT NULL, receive_date TEXT NOT NULL, deadline TEXT, secret_level TEXT DEFAULT '普通', sign_status INTEGER DEFAULT 0, creator TEXT NOT NULL, create_time TEXT NOT NULL ) """) cursor.execute(""" CREATE TABLE IF NOT EXISTS sign_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, doc_id INTEGER NOT NULL, signer TEXT NOT NULL, sign_time TEXT NOT NULL, comment TEXT, FOREIGN KEY (doc_id) REFERENCES documents(id) ) """) conn.commit() conn.close()

3.2 签收流程的核心逻辑

签收操作是整个系统的核心交互动作,我把它实现成一个POST请求。用户在待签收列表中看到公文后点击“签收”,前端弹出一个对话框,要求填写签收意见,后端收到请求后做两步事务性操作:第一步把documents表里对应记录的sign_status更新为1;第二步往sign_logs表里插入一条签收日志,记录谁在什么时间签收的。

这里有一个值得分享的细节,我并没有限制一份文件只能被一个人签收,而是允许多个部门分别签收。比如一份需要办公室和财务科共同落实的文件,两个部门的人在各自登录后都能看到并签收,台账上会显示两条签收记录。这样设计更符合实际业务,一份文件确实可能对应多个执行主体。但如果你们的业务流程是固定的单人签收,只需要在写SQL时加上“当前公文已是已签收状态,普通用户不再显示该条记录”的过滤条件即可。

签收路由示例代码如下:

@app.route("/sign/<int:doc_id>", methods=["POST"]) def sign_document(doc_id): if not session.get("user"): return redirect(url_for("login")) signer = session["user"] comment = request.form.get("comment", "").strip() now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") conn = get_db() conn.execute( "UPDATE documents SET sign_status = 1 WHERE id = ?", (doc_id,) ) conn.execute( "INSERT INTO sign_logs (doc_id, signer, sign_time, comment) VALUES (?, ?, ?, ?)", (doc_id, signer, now, comment), ) conn.commit() flash("签收成功", "success") return redirect(url_for("todo_list"))

这一步做完,数据模型上已经形成了完整闭环:登记产生公文,签收产生日志,日志支撑台账查询。

3.3 催办提醒与台账统计

催办功能是这个系统真正给办公室人员减负的地方。以前催办靠人工翻本子,现在系统自动筛数据。我先在documents表里取了sign_status = 0的记录,然后根据创建时间计算已经过去的天数,把超过三天的文件单独列成清单。页面上用不同颜色标注,黄色代表超过三天,红色代表超过五天,办公室人员一眼就能看出哪些文件急需跟催。

台账页则提供多维度的查询,支持按收文日期范围、来文单位、标题关键词、签收状态组合筛选。所有查询结果都是基于SQL拼接实现的,虽然技术含量不高,但胜在简单可靠。查询结果支持导出CSV,用Excel打开就是一张现成的报表,月底统计办文量时免去了重新汇总的麻烦。

筛选逻辑的一个核心SQL片段如下:

query = "SELECT * FROM documents WHERE 1=1" params = [] if keyword: query += " AND title LIKE ?" params.append(f"%{keyword}%") if status: query += " AND sign_status = ?" params.append(int(status)) if start_date: query += " AND receive_date >= ?" params.append(start_date) if end_date: query += " AND receive_date <= ?" params.append(end_date) rows = conn.execute(query + " ORDER BY receive_date DESC", params).fetchall()

4. 实操搭建过程实录

4.1 环境准备与项目初始化

如果你准备在自己的电脑上复现这套系统,需要的只有Python 3.9以上版本和pip工具。创建虚拟环境这一步别偷懒,它能避免你的系统依赖被项目搞乱。初始化完成后,安装Flask、Werkzeug两个核心包就行,不需要额外的数据库驱动,因为SQLite是Python标准库自带的能力。

初始化项目时我的目录结构是这样的:

sign_system/ ├── app.py ├── init_db.py ├── models.py ├── requirements.txt ├── templates/ │ ├── base.html │ ├── login.html │ ├── index.html │ ├── todo_list.html │ ├── register_doc.html │ └── ledger.html └── instance/ └── sign_system.db

requirements.txt只有三行,FlaskWerkzeugitsdangerous(Flask的依赖会自动安装)。项目的生命周期里大概率不会遇到依赖冲突,这点是选择轻量框架带来的安心感。在数据库初始化时,我预先插入了一个管理员账号,用户名是admin,初始密码是admin123,登录后第一件事就是把密码改掉。这个初始账号的插入逻辑写在init_db.py里,和建表语句放在一起,方便新环境重新初始化。

4.2 后端路由与前端页面的联动

用户通过浏览器看到的每一个页面,背后都对应一个路由函数。我把主要路由归纳成了五类:登录注册、首页统计、待办列表、收文登记、签收台账。每一类路由内部,负责GET请求的通常是渲染模板,负责POST请求的通常是数据处理。这种“GET显示页面,POST处理操作”的约定让代码结构非常清晰,哪怕隔了一个月再来看,也能立刻想起来某段逻辑是干什么的。

登录状态我用Flask自带的session来维持,用户密码哈希验证通过后,把用户名和角色写入session,之后的每一个请求在进入具体处理逻辑前都会做一个登录校验,未登录的用户一律重定向到登录页面。为了不让每个视图函数里都重复写登录校验代码,我写了一个login_required装饰器,这也是Flask里最常见的做法。

页面模板之间通过base.html继承保持了统一的视觉风格和导航栏,左侧菜单是待签收列表、收文登记、签收台账、系统管理四项,顶部显示当前登录用户和退出按钮。因为目标用户是办公室的同事,所以我没有在样式上投入太多精力,只引入了一个简易的CSS框架,界面干净、按钮够大、字够清楚就行。后端在响应请求时把动态数据注入模板,前端通过Jinja2的语法进行循环渲染,例如待签收列表页的核心模板片段如下:

{% for doc in pending_docs %} <tr> <td>{{ doc.doc_no }}</td> <td>{{ doc.title }}</td> <td>{{ doc.sender }}</td> <td>{{ doc.receive_date }}</td> <td> <button onclick="openSignModal({{ doc.id }})">签收</button> </td> </tr> {% endfor %}

4.3 内网部署与日常维护

部署阶段我采用的是最朴素的方式,在一台Windows内网服务器上安装Python环境,把整个项目文件夹复制过去,运行waitress-serve --port=8080 app:app命令启动服务。Waitress是Windows平台上比较靠谱的纯Python WSGI服务器,比Flask自带的开发服务器稳定得多,能扛住几十个人同时在线访问,如果只是单位内网使用,完全足够了。

数据库文件的日常维护主要是备份,我写了一个备份脚本,每天凌晨通过Windows计划任务执行,把sign_system.db文件复制到另一块磁盘上,文件名追加日期。这个动作成本几乎为零,却是整个系统最值得保留的一道保险。万一某人误操作删了数据,至少能把昨天的数据找回来,损失控制在一天以内。

5. 常见问题与排查技巧实录

5.1 中文乱码问题

这套系统在开发过程中遇到最多的其实是编码问题。Windows内网环境的电脑默认编码可能是GBK,而Flask和SQLite默认按UTF-8处理,这会导致页面上出现乱码。排查思路很直接,先在浏览器页面右键查看字符编码,再检查数据库连接参数是否指定了UTF-8。我的解决办法是在app.py里统一加上响应头的字符集设置,并保证HTML模板里有<meta charset="utf-8">,之后乱码问题就再没出现过。

还遇到过一种更隐蔽的情况,同事在Excel里复制公文标题粘贴到表单里,带进来一些特殊字符或不可见空格,入库后在页面上显示正常,但导出CSV时总多出奇怪符号。后来我在表单提交的入口把字符串统一做了清洗,把空白字符和非法字符过滤掉,这个问题才算彻底解决。处理用户输入时,宁可严格一点也不要太宽松。

5.2 SQLite并发写入报错

SQLite虽然是文件数据库,但在多人同时签收这种“写多读少”的场景下,偶尔会报“database is locked”。项目初期出现过几次,排查后确认是多个并发连接同时写入导致的锁冲突。解决办法有两步,第一步是在每次数据库连接建立时打开WAL模式,这个模式能显著提升读写并发性能,命令是PRAGMA journal_mode=WAL;。第二步是所有写操作尽量在短时间内完成并立即关闭连接,避免长时间占用数据库锁。

WAL模式开启之后,数据库目录下会出现两个额外的文件-wal-shm,这是正常现象。每次备份时要注意把这三个文件一起复制,或者先执行一次checkpoint操作把WAL内容合并进主数据库,否则备份文件可能不完整。这个小细节我一开始没注意,测试恢复时才发现备份文件里缺了最后几笔数据。

5.3 误操作与权限管理的取舍

实际运行几个月后,我收到过最多的反馈不是功能不够,而是“我不小心点错了”。比如说登记员新建公文时,标题多打了一个字;或者同事签收时填写的意见写错了。这类误操作在纸质流程里很容易处理,划掉重签就行,但在系统里就涉及到已有数据的修改权限。

我给普通用户分配了“已签收公文撤销重签”的权限,前提是签收时间在当天,超过当天就无法撤回。管理员则可以对所有记录进行修改和删除操作。这样既避免了误操作需要找管理员解锁的麻烦,又保证了签收日志的严肃性。权限控制的核心原则是“让数据可追溯,不是让所有人随便改”,系统的任何操作都会在签收记录表里留下操作痕迹,所以权限放开一点问题也不大。

5.4 浏览器兼容与使用习惯

最后想分享一个容易被忽略的问题:单位内网环境里同事们用的浏览器五花八门,有老IE内核的、各种国产浏览器兼容模式的,也有新版Chrome和Edge。早期我在前端用一个比较现代的日期选择组件,结果有同事反馈页面打不开或按钮点不动,排查后发现是浏览器版本太老不兼容。

解决方法有两个,一个是把前端代码尽量写得“保守”,不用太新的CSS和JavaScript语法;另一个是在系统首页加了个浏览器检测的小提示,检测到IE内核时就显示“建议使用Edge浏览器访问,系统功能才能完整展示”。两个办法配合下来,兼容性问题少了很多。做这类内部工具时,永远不要高估同事的浏览器版本,这是很现实的经验。

写在最后的一点心得

这套公文签收系统的开发过程并不复杂,满打满算实际编码时间也就两三天,但它解决了许多真实运转中的麻烦。我最大的体会是,做内部管理工具,越简单越好维护越好,不要一开始就想着架构多先进、功能多齐全,先把“登记、签收、台账、催办”这四个核心动作做顺,系统就已经成功了一大半。

如果你也想给单位或部门做一套类似的系统,我个人建议从Excel台账和纸质签收的痛苦场景出发,梳理出最痛的那几个点,再动手写代码。另外,系统上线之后一定要组织一次简单的使用培训,哪怕只是当面演示十分钟,都能避免后续大量的答疑和误操作返工。最后再分享一个小技巧:给系统里的每一个关键操作都加上操作日志,虽然前期看起来多写了几个字段,但真正出现问题回溯的时候,你就会庆幸当时多留了这一手。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 7:57:48

国产多模态模型追平Opus:从本地部署到评测差距量化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 7:53:39

Qt集成阿里云OSS C++ SDK实战:上传下载与进度显示全解析

简介&#xff1a;面向需要在Qt项目中集成阿里云OSS C SDK的开发者&#xff0c;这份源码包提供了一套可直接参考的完整示例工程。包内涵盖SDK编译产物与调用实现&#xff0c;包含201个头文件、6个CPP源文件以及若干DLL和LIB库文件&#xff0c;并附有Qt工程配置、界面资源与进度展…

作者头像 李华
网站建设 2026/9/8 7:53:19

KV cache泄漏被忽视:nvidia-smi为何对vLLM显存问题视而不见?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 7:52:44

FPGA学习路线:从数字逻辑到项目实战的完整进阶指南

1. FPGA学习&#xff1a;为什么多数人卡在门口&#xff0c;而不是死在代码里FPGA这行当&#xff0c;每年入坑的人不少&#xff0c;真正能留下来干活的人却没想象中那么多。原因倒不复杂——FPGA的学习曲线不是一条缓坡&#xff0c;而是几段台阶。很多人一开始抱着Verilog语法啃…

作者头像 李华
网站建设 2026/9/8 7:52:20

黑苹果安装工具链全解析:从EFI配置到驱动调试的完整指南

简介&#xff1a;面向想在非苹果硬件上运行 macOS 的黑苹果玩家和初次尝试者&#xff0c;这份工具包把安装过程中最常遇到的引导配置、驱动修补、分区读写、EFI 定制等问题集中到了一起&#xff0c;从制作安装介质到安装后驱动注入都有对应方案。包内共 20 个文件&#xff0c;压…

作者头像 李华
网站建设 2026/9/8 7:51:41

Qt调用阿里云OSS C++ SDK:文件上传下载与进度显示完整教程

简介&#xff1a;这是一份完整的Qt调用阿里云OSS C SDK的示例项目源码&#xff0c;面向需要在Qt/C应用中实现云存储功能的开发者。资源演示了如何下载编译OSS SDK、在Qt工程中正确引用头文件与库&#xff0c;并通过信号槽机制实时展示文件上传/下载进度&#xff0c;同时针对静态…

作者头像 李华