ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Flask与Django对比:企业档案管理系统开发实战

Flask与Django对比:企业档案管理系统开发实战 开头做企业内部档案管理系统这件事听起来简单真正上手之后才会发现坑有多深。纸质档案借阅长期存在三个老大难问题档案去向说不清、借出容易归还难、月底台账对不上。我在实际接手一个企业档案借阅信息系统的开发任务时第一反应就是“这不就是个增删改查吗”可等我把业务流程梳理完才发现真正的复杂度根本不在CRUD本身而是借阅状态流转、权限边界、逾期追踪这些业务细节。这个项目最终选择了Python Flask框架实现开发工具用PyCharm数据库沿用了轻量的SQLite起步后期可以平滑迁移到MySQL。标题里提到django也不奇怪很多人在Flask和django之间纠结过——我在文章里会单独对比一下这两个框架在这个场景下的取舍。系统本身覆盖了档案登记、检索、借阅申请、审批、出库、归还、逾期提醒这几个核心闭环适合正在做企业内部管理系统、毕业设计或者刚接触Flask想做一个完整项目的朋友参考。1. 系统整体设计与技术选型思路1.1 档案借阅的业务场景与核心需求拆解先把业务讲透。企业档案借阅不是一个简单的“借书系统”档案类型决定了它的业务规则比图书管理复杂得多。我在设计需求时接触到的档案大概分几类合同档案、人事档案、财务凭证、技术图纸、行政公文每一类的密级、借阅权限、归档要求都不一样。这就决定了系统不能只做一张“档案表一条借阅记录”这种简化模型。从核心业务场景来看我需要解决这几个问题谁能借什么档案普通员工只能申请自己部门相关的低密级档案部门主管有审批权档案管理员负责档案的最终出库和回收确认。借阅流程怎么走申请 - 部门审批 - 档案管理员确认出库 - 借阅中 - 归还登记 - 归档确认任何一个环节缺失都会导致档案去向不明。逾期怎么处理档案管理员需要看到所有超期未还的记录系统要能自动计算逾期天数并生成提醒列表。台账怎么生成月底、季度末要能快速导出一段时间内所有借阅流水方便审计和追溯。这些需求梳理清楚之后技术方案基本就定了。系统的核心不是界面多华丽而是业务状态机的严谨程度和流程的完整闭环。1.2 Flask vs django这个场景下我为什么选Flask标题里同时出现了flask和django这大概是很多人在技术选型时最纠结的地方。我先说结论这个项目用Flask是合适的但django也并不是错误选项关键看你的业务复杂度和团队情况。django的优势在于“全家桶”自带Admin后台、ORM、认证体系、表单处理、模板引擎适合业务量大、模块众多、团队协作开发的中大型项目。假设你的档案系统未来要扩展成整个企业的OA平台包含考勤、工资、审批流、公告等十几个模块django的模块化App机制和现成的后台管理界面会帮你省很多事。但我的实际体感是一个档案借阅系统撑死也就四五个业务模块用django有点杀鸡用牛刀。Flask的优势是轻、灵活、可控性强数据库用什么、认证怎么做、后台要不要、接口怎么组织都由你自己决定。而且Flask的入门曲线平缓代码量也少一个小团队或者个人开发者从零搭建一套内部工具Flask往往是效率最高的选择。另外还有一个很实际的原因Flask的蓝图Blueprint结构在应对这种中小规模系统时非常直观模型、视图、模板各归各的位置代码量控制在几千行以内的时候维护成本比django那一套要低得多。如果你后续想改成前后端分离Flask写个JSON API也特别顺手。django的DRF虽然也不差但引入的重量明显更大。1.3 技术架构与项目目录规划实际开发时我的项目结构是这样组织的archive_system/ ├── app.py # 应用入口注册蓝图 ├── config.py # 配置文件数据库、秘钥、上传路径 ├── requirements.txt # 依赖清单 ├── models/ │ ├── __init__.py │ ├── user.py # 用户模型 │ ├── archive.py # 档案模型 │ └── borrow.py # 借阅记录模型 ├── views/ │ ├── __init__.py │ ├── auth.py # 登录/登出 │ ├── archive_view.py # 档案管理接口 │ └── borrow_view.py # 借阅流程接口 ├── templates/ # Jinja2模板 ├── static/ # CSS/JS └── utils/ ├── decorators.py # 权限装饰器 └── helpers.py # 通用工具日期、分页等这种结构的好处是模型、视图、工具函数各不干扰后续做功能扩展时不用在一堆代码里翻来翻去。我这里选用了传统的服务端渲染模板方式好处是开发速度快、部署简单对于企业内部用户来说浏览器打开就能用不需要额外启动前端工程。如果你的需求是移动端适配要求高、界面交互复杂那再上FlaskVue的分离式架构也不迟。2. 开发环境准备与PyCharm配置2.1 Python环境安装与虚拟环境创建老生常谈但必须说Python版本和虚拟环境是新手最容易踩坑的地方。我在开发这个项目时用的是Python 3.10Flask 3.0.x。如果你是全新的环境先到Python官网下载安装包安装时务必勾选“Add Python to PATH”这个选项不勾的话后面在命令行里敲python会提示找不到命令很耽误事。装好Python之后我不建议直接全局装Flask。每个项目用独立的虚拟环境是Python开发的基本素养否则不同项目依赖的包版本打架会让你心态爆炸。PyCharm在这方面做得很好——新建项目时可以直接创建虚拟环境。在PyCharm里创建虚拟环境的路径是File - New Project - 选择项目目录 - 解释器类型选Virtualenv - Python版本选本机安装的版本。勾选“Make available to all projects”反而不推荐保持每个项目独立就行。如果是命令行方式手动创建也很快python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate激活之后命令行前面会出现(venv)前缀这时候安装的包都在虚拟环境里不会污染全局。2.2 Flask及相关依赖安装激活虚拟环境后安装项目需要的依赖包pip install flask pip install flask-sqlalchemy pip install flask-login pip install flask-wtf pip install python-dotenv逐个说下为什么需要这些Flask本身提供了路由、请求响应、模板渲染这些核心能力。Flask-SQLAlchemyFlask官方的ORM扩展好处是写代码时不用死磕原生SQL模型类定义好之后增删改查都有现成的API。Flask-Login负责用户会话和登录态管理seesion的创建销毁、当前用户获取都封装好了比自己写装饰器处理session可靠得多。Flask-WTF做表单时很有用特别是CSRF防护企业内部系统虽然不面临高风险攻击但防护意识要有。python-dotenv把数据库地址、密钥这些敏感配置放进.env文件避免硬编码在代码里。装完之后可以用pip freeze命令确认一下版本pip freezeWindows环境下如果遇到安装慢或者超时的问题换镜像源就是最快的解法pip install flask -i https://pypi.tuna.tsinghua.edu.cn/simple2.3 PyCharm配置解释器与数据库工具PyCharm有两个版本社区版免费专业版收费。这个项目用社区版就完全够了。项目打开后如果虚拟环境没有自动识别可以手动配置File - Settings - Project - Python Interpreter - 右上角齿轮 - Add - Existing environment选到venv目录下的python.exe即可。PyCharm自带的数据库工具Database面板在专业版里很强大可以看到表结构、执行SQL、甚至直接生成模型映射。社区版没有这个功能的完整形态不过对开发影响不大——SQLite的直观性足够实在想看数据可以用DB Browser for SQLite这个免费工具。配置好环境后先创建数据库文件并验证连接。我用的是SQLite起步后续需要切换MySQL时改一下config.py里的连接串即可import os BASE_DIR os.path.abspath(os.path.dirname(__file__)) class Config: SECRET_KEY os.environ.get(SECRET_KEY) or dev-key-please-change SQLALCHEMY_DATABASE_URI sqlite:/// os.path.join(BASE_DIR, archive.db) SQLALCHEMY_TRACK_MODIFICATIONS False这里SECRET_KEY很重要Flask用它来对session数据做签名如果不设置或者用默认值生产环境会有安全隐患。SQLALCHEMY_TRACK_MODIFICATIONSFalse是为了关闭Flask-SQLAlchemy的不必要事件通知减少内存开销。3. 核心数据模型与数据库设计3.1 用户模型与角色权限权限设计是档案管理系统里最容易出问题的地方。我的做法是采用最简单的RBAC基于角色的访问控制不引入复杂的权限表就三个角色枚举员工staff、部门主管manager、档案管理员admin。用户模型的核心字段包括工号、姓名、部门、角色、密码哈希。密码绝对不能明文存储我直接用werkzeug自带的加密函数from werkzeug.security import generate_password_hash, check_password_hash from flask_login import UserMixin from flask_sqlalchemy import SQLAlchemy from datetime import datetime db SQLAlchemy() class User(UserMixin, db.Model): __tablename__ users id db.Column(db.Integer, primary_keyTrue) employee_no db.Column(db.String(20), uniqueTrue, nullableFalse) name db.Column(db.String(50), nullableFalse) department db.Column(db.String(100), nullableFalse) role db.Column(db.String(20), nullableFalse, defaultstaff) password_hash db.Column(db.String(200), nullableFalse) created_at db.Column(db.DateTime, defaultdatetime.now) def set_password(self, password): self.password_hash generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password)UserMixin是Flask-Login提供的混入类自动实现了is_authenticated、is_active、get_id等方法省去了手动实现的麻烦。employee_no设为唯一索引这是员工登录时最自然的凭证。3.2 档案模型设计要点档案信息表的字段设计直接决定了后面的检索和借阅体验。我实践下来的核心字段包括class Archive(db.Model): __tablename__ archives id db.Column(db.Integer, primary_keyTrue) archive_no db.Column(db.String(50), uniqueTrue, nullableFalse) # 档案编号 title db.Column(db.String(200), nullableFalse) # 档案标题 category db.Column(db.String(50), nullableFalse) # 档案分类 security_level db.Column(db.String(20), nullableFalse) # 密级 storage_location db.Column(db.String(200)) # 存放位置 department db.Column(db.String(100)) # 所属部门 status db.Column(db.String(20), defaultavailable) # 在库/借出/待归还 created_at db.Column(db.DateTime, defaultdatetime.now) updated_at db.Column(db.DateTime, defaultdatetime.now, onupdatedatetime.now)档案编号我采用的是“分类前缀年份流水号”的规则比如HT-2024-001合同类、RS-2024-002人事类。生成逻辑在归档接口里自动完成不需要人工录入避免手误。分类前缀单独配置一个字典新增分类时加一行就行。storage_location字段在纸质档案场景下很有价值它记录的是实体档案架位编号。我遇到过这种情况借阅记录显示档案已归还但管理员找不到实物放在哪就是因为系统里没记录存放位置。有了这个字段归还登记时必须填写存放位置盘点时就能顺着物理位置去核对。3.3 借阅记录与流程状态设计借阅记录是整个系统的灵魂我把它设计成了一条完整的状态跟踪链class BorrowRecord(db.Model): __tablename__ borrow_records id db.Column(db.Integer, primary_keyTrue) archive_id db.Column(db.Integer, db.ForeignKey(archives.id), nullableFalse) user_id db.Column(db.Integer, db.ForeignKey(users.id), nullableFalse) approver_id db.Column(db.Integer, db.ForeignKey(users.id)) borrow_date db.Column(db.DateTime) due_date db.Column(db.DateTime) return_date db.Column(db.DateTime) status db.Column(db.String(20), nullableFalse, defaultpending) reason db.Column(db.Text, nullableFalse) # 借阅事由 reject_reason db.Column(db.String(200)) # 驳回原因 created_at db.Column(db.DateTime, defaultdatetime.now)status字段我定义了六个状态字符串形式虽然看起来朴素但在实际项目中比数字枚举可读性高得多pending待审批approved已批准等待出库reject已驳回borrowed借阅中returned已归还overdue已逾期这里有个设计细节值得说明我不建议把“逾期”设计成独立状态而是让系统通过对比due_date和当前日期动态判断。为什么要这样因为如果逾期是状态机里的一个节点那么归还时必须从overdue状态跳回returned状态判断就多了一层复杂度。我更倾向的方式是数据库里保存baseline状态borrowed列表查询时用条件表达式计算是否超期超期的记录在界面上用红色标注。这样既保留了状态机的简洁又能实现逾期清单的动态生成。4. 核心业务闭环与关键接口实现4.1 借阅申请与审批流借阅申请是整个流程的起点。用户在页面上选择要借的档案填写借阅事由和预计借阅时长系统生成一条pending状态记录。为什么借阅事由要必填因为档案借阅不是图书馆借小说每一份档案的去向都有审计需求。审批人需要根据事由判断是否合理档案管理员做台账时也需要注明用途。这个字段在设计时我把它设为必填vaildation在服务端做了前端也做了双保险。审批接口的核心逻辑是这样的from flask_login import login_required, current_user from flask import jsonify, request from . import bp from models import db, BorrowRecord, Archive bp.route(/borrow/int:record_id/approve, methods[POST]) login_required def approve_borrow(record_id): if current_user.role not in (manager, admin): return jsonify({code: 403, msg: 没有审批权限}), 403 record BorrowRecord.query.get_or_404(record_id) action request.json.get(action) # approve or reject if action approve: # 检查档案是否仍然可借 archive Archive.query.get(record.archive_id) if archive.status ! available: return jsonify({code: 400, msg: 该档案已被借出或锁定}), 400 record.status approved record.approver_id current_user.id elif action reject: record.status reject else: return jsonify({code: 400, msg: 非法操作}), 400 db.session.commit() return jsonify({code: 200, msg: 操作成功})这里有一个精细设计审批通过时我加了一个“档案是否仍然可借”的二次校验。为什么要二次校验因为从用户提交申请到主管审批中间可能存在时间差。如果同一份档案被A提前申请且已借出B的审批请求到了之后就不能再通过。这个看似不起眼的校验避免了“超借”问题——一份实体档案同时被两个人借走这在纸质管理里是灾难。4.2 出库与归还档案状态如何同步审批通过后档案管理员要执行出库操作。出库动作的本质是把档案状态从available改为borrowed同时记录借出时间和应还时间bp.route(/borrow/int:record_id/checkout, methods[POST]) login_required def checkout_archive(record_id): if current_user.role ! admin: return jsonify({code: 403, msg: 仅档案管理员可操作}), 403 record BorrowRecord.query.get_or_404(record_id) if record.status ! approved: return jsonify({code: 400, msg: 该记录未处于待出库状态}), 400 archive Archive.query.get(record.archive_id) archive.status borrowed record.status borrowed record.borrow_date datetime.now() # 借阅时长为申请时的days字段默认7天这里从申请上下文读取 record.due_date record.borrow_date timedelta(daysrecord.borrow_days) db.session.commit() return jsonify({code: 200, msg: 出库成功})很多新手在做这一步时会漏掉“同时更新两张表”这个关键点。出库不仅是借阅记录状态变化档案表的状态也必须同步。如果不改archive.status就会出现在库档案列表里还能搜到一份实际上已经被人借走的档案检索模块的准确性就失控了。归还登记是流程的终点也最容易遗漏细节。登记归还时除了把状态改成returned、写回归还时间之外还要把档案状态改为available并将其“锁定”在同一位置。如果租借过程中档案被翻阅过实物摆放位置变了管理员在归还登记页还能修正storage_location字段保证台账信息与实物位置实时一致。4.3 逾期判断与自动化提醒逾期管理是档案管理员每天都要看的核心页面。我采用的方案是借款期限到了之后系统每天在管理员仪表盘显示逾期列表同时给借阅人发站内通知。这个方案不依赖定时任务而是通过查询时实时计算好处是零维护、无延迟。计算逾期的核心代码逻辑from datetime import datetime records BorrowRecord.query.filter( BorrowRecord.status borrowed, ).all() now datetime.now() overdue_records [] for record in records: if record.due_date and record.due_date now: overdue_days (now - record.due_date).days overdue_records.append({ record: record, overdue_days: overdue_days })如果企业内部有正式的定时任务体系比如APScheduler、Celery或者服务器Cron也可以改成每天定时扫描然后推送消息给借阅人。我在实际项目中是两者结合页面实时计算用于管理员日常操作定时邮件提醒用于通知借阅人。前者的实现极其轻量后者的效果能规范化企业流程两者配合超期归还的现象明显改善。4.4 档案检索与分页实践档案检索功能看似简单但有几个优化点值得分享。搜索条件包含关键词、分类、密级、所属部门这几个维度。我的实现方式是动态构建查询条件from sqlalchemy import or_ def search_archives(keyword, category, security_level, dept, page, per_page10): query Archive.query if keyword: like_pattern f%{keyword}% query query.filter( or_( Archive.title.like(like_pattern), Archive.archive_no.like(like_pattern) ) ) if category: query query.filter(Archive.category category) if security_level: query query.filter(Archive.security_level security_level) if dept: query query.filter(Archive.department dept) pagination query.paginate(pagepage, per_pageper_page, error_outFalse) return paginationFlask-SQLAlchemy的paginate方法返回的分页对象会自动包含items、pages、total这些属性模板里直接用就够了不用自己写切片的逻辑。error_outFalse这个参数要记住如果用户访问的页码超出了范围不会报404错而是返回空列表这个在管理系统的使用体验上更重要——档案管理员往往记不住总共有几页。分页模板部分配合Jinja2写一个简单的翻页组件上一页、下一页、页码列表几十行代码就搞定了。这里不展开贴代码核心思路就是拿到pagination对象后渲染它的iter_pages方法返回的页码序列。4.5 登录与权限装饰器的进阶用法Flask-Login在全系统的用户管理上很好用。登录视图是这样的bp.route(/login, methods[GET, POST]) def login(): if request.method POST: employee_no request.form.get(employee_no) password request.form.get(password) user User.query.filter_by(employee_noemployee_no).first() if user and user.check_password(password): login_user(user) return redirect(request.args.get(next) or url_for(auth.dashboard)) flash(工号或密码错误, danger) return render_template(login.html)注意login_user之后我用了redirect(request.args.get(next) or url_for(auth.dashboard))。这个next参数是Flask-Login在拦截未登录请求时自动携带的比如用户访问了/borrow/create这个页面被踢到登录页登录成功后又自动跳回原页面。这个体验细节很多人不做但实际使用中非常加分——用户不用每次登录后再一次导航到自己原本想去的位置。自定义权限校验装饰器是另一个实用的技巧。因为系统有三种角色每个视图的访问权限不同写一个通用装饰器比在每个视图函数里重复判断current_user.role要整洁得多from functools import wraps from flask_login import current_user from flask import abort def role_required(*roles): def decorator(f): wraps(f) def wrapper(*args, **kwargs): if current_user.role not in roles: abort(403) return f(*args, **kwargs) return wrapper return decorator使用方式bp.route(/admin/archives) login_required role_required(admin) def admin_archives(): ...代码的语义非常清晰先确保已登录再确保角色是admin。如果新增了一个“部门主管兼档案员”的业务场景只要把角色加进元组就可以了。5. 常见问题与排查技巧实录5.1 数据库与ORM常见坑Q1改了模型字段后报错no such column这是Flask-SQLAlchemy新手踩得最多的坑。模型类加了一个字段但数据库表结构没有同步更新因为Flask-SQLAlchemy默认不会自动做migration。我建议直接安装Flask-Migrate这是基于Alembic的迁移工具后续模型变更只需要三步命令flask db migrate -m add column storage_location flask db upgrade如果项目已经上线且有数据一定要用Flask-Migrate千万不要手动改数据库表结构否则模型和库不一致排查起来很折磨。Q2查询结果总是有重复数据我遇到的情况是join查询时因为一对多关系没有加distinct结果集产生了笛卡尔积式的重复。用query.filter之后务必检查是否有多表join多表join后需要调用.distinct()来去重。另外用relationship定义关系时如果不需要懒加载推荐设置lazyselectin减少N1查询的几率。Q3SQLite数据库文件锁死或数据丢失SQLite在Windows环境下如果程序异常退出有时会留下-journal文件导致数据库锁定。遇到这种情况先关掉所有连接删除同目录下的-journal文件再重启应用。SQLite更适合开发和单用户场景如果系统正式上线、并发量超过几十人建议切换到MySQL或PostgreSQL。5.2 Flask开发环境的经典问题Q1改了代码但页面没变化Flask的debug模式要手动开启否则代码改完必须重启服务才能看到效果。开发阶段在入口文件里设置if __name__ __main__: app.run(debugTrue)debug模式开启后代码修改会自动reload而且页面报错时会显示详细的调试页面包括堆栈信息、当前变量值。但生产环境必须关闭debug同时把host设置为127.0.0.1或在反向代理层做好访问控制。Q2模板修改了但渲染的还是旧内容这个问题通常是浏览器缓存导致的。模板引用的CSS或JS文件路径没有变化浏览器会从缓存里加载旧文件。最简单的解决方法是给静态文件加上版本号参数link relstylesheet href{{ url_for(static, filenamecss/style.css?v2.0) }}每次发布时改一下?v后面的版本号就能强制刷新缓存。Q3PyCharm里中文乱码怎么办PyCharm的File Encoding设置需要统一为UTF-8。路径File - Settings - Editor - File Encodings把Global Encoding、Project Encoding、Default encoding for properties files都设为UTF-8。如果还乱码检查一下Python文件的头部是否声明了编码。Python 3默认就是UTF-8读取源文件这个问题一般只会在老项目或Windows默认编码环境下出现。5.3 业务逻辑上的边界条件实际开发中我发现自己最容易忽略的是这些边界情况用户重复提交借阅申请前端按钮没有disable用户双击导致提交了两条一样的申请。处理方式是在后端加校验同一个人同一份档案只能有一个active状态的申请。审批人审批自己提交的申请这在逻辑上是不允许的需要在审批接口加一个判断语句record.user_id不等于current_user.id。档案被删除后借阅记录的关联查询不能做物理删除要做软删除。就是在档案表加一个is_deleted字段默认为False删除时改为True即可。借阅记录里的外键能查到历史数据这一点对审计很重要。同一天有大量归还操作时的状态一致性事务处理。在出库、归还这些涉及两表更新的操作上用db.session直接提交这个本身就在事务中只要commit之前任何一步失败前面的操作都会回滚。5.4 遗留一个问题为什么不能用密码明文存库企业内部系统往往被低估安全威胁。哪怕只是内部系统用户的账号密码也绝对不能明文存。如果数据库被非法访问或误操作泄露备份文件被带出公司明文密码几乎是灾难。所以我用werkzeug的generate_password_hash对密码做了哈希处理这个函数会引入随机盐即使两个用户设置同样的密码生成的哈希值也不一样。登录校验用check_password_hash对比整个过程对用户无感但安全等级完全不一样。建议至少要求6位以上的密码如果系统有初始密码比如管理员手动导入用户时设置的默认密码123456那么首次登录时一定强制用户修改这是很基础但很必要的一项安全习惯。6. 从开发到上线的补充经验项目在本地跑通和真正给企业用户用起来中间还有一段路。这一段我把自己的实测心得和补充经验打包分享出来希望能给做同类项目的朋友一些参考。6.1 SQLite切换MySQL的过程如果你的企业内部已经统一使用了MySQL这是很多中大型公司的标配我建议你在项目起步时就计划好切换方案。Flask-SQLAlchemy的ORM让我在这个切换过程中几乎没改任何业务代码只调整了config.py里的数据库连接串SQLALCHEMY_DATABASE_URI mysqlpymysql://username:passwordlocalhost/archive_db?charsetutf8mb4需要额外安装的依赖是pymysqlpip install pymysql即可。注意charset一定要设置成utf8mb4否则中文保存到MySQL里会出现乱码这是因为MySQL的utf8只支持三个字节而emoji和一些生僻字需要四个字节。6.2 使用Flask-Migrate的迁移工作流随着功能迭代模型字段几乎一定会增加。例如后来我在借阅记录里增加了borrow_days字段用于记录用户申请的借阅天数。这个改动如果用Flask-Migrate来做就是三步的事情flask db migrate -m add borrow_days column flask db upgrade反之如果不用迁移工具你就要记得每次更新模型后手动对比库表结构这个在项目早期还能忍受等表多了以后就是纯折磨。所以我的建议就是从第一天就配置好Flask-Migrate不要等到模型改了再补。6.3 部署方式的选择宝塔面板还是Gunicorn反向代理项目上线部署时有几种主流方式我根据自己的实践对比一下。宝塔面板目前很多中小企业在用图形化管理上传项目代码、配置Python环境、安装Nginx、设置反向代理全程浏览器操作。如果你不太熟悉Linux命令宝塔是最快的路径。Gunicorn Nginx经典方案。Gunicorn作为Python WSGI服务器运行Flask应用Nginx做反向代理和静态文件服务。配置灵活性能也好。Flask自带的开发服务器app.run有个提示横幅它只是为了开发调试而设计的不用于生产——性能很差也没有并发处理能力。部署时务必要换成Gunicorn或uWSGI这样的生产级WSGI服务器。Gunicorn启动命令参考gunicorn -w 4 -b 0.0.0.0:8000 app:app-w 4代表4个worker进程-b指定监听地址app:app表示app.py文件里的app实例。如果是在Windows服务器上Gunicorn不支持那就用waitress替换用法类似。6.4 数据备份与导出台账企业内部系统上线后数据备份是每天必须关心的事情。SQLite直接备份数据库文件即可MySQL可以用mysqldump定时代理。我在项目中加了一个“台账导出”功能把借阅记录列表导出为Excel文件这个功能用openpyxl库实现并不复杂。这一步不要省因为月底盘点时档案管理员真的很需要它曾经一个台账就能节省我超过一个小时的核对时间。6.5 不断扩展的可能性系统跑顺之后我一直在想它的扩展方向。一个是给档案表加二维码标签每份档案生成唯一的二维码打印出来贴在实体档案袋上手机扫码就能看到档案信息和借阅状态这对仓库盘点非常高效。另一个是接入企业微信或钉钉的消息推送审批流到节点时自动推送给主管微信审批效率能再上一个台阶。每个扩展点都保留着与核心系统一致的业务复杂度后续如果接手的人要扩展代码结构也能轻松承接。说到底档案借阅系统看似就是个“图书管理系统”真正做进去之后才发现业务抽丝剥茧之后的复杂程度完全取决于你对流程细节的重视程度。我的个人体会是与其绞尽脑汁堆功能不如把“档案状态什么时候变、哪些操作可以触发状态变化、哪些角色不能干什么”想在前面。状态机清晰了这个系统就稳稳站住了后面再怎么扩展也不慌。最后分享一个小细节我在部署正式环境时把SECRET_KEY和环境变量分开了线上用的密钥写在服务器环境变量里代码库里放的是开发用的占位符避免密钥泄露在Git仓库的历史记录里。这种习惯越早养成越好等系统真正承载了企业数据就再没有机会大意了。
返回列表