ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python体育用品商店系统开发详解:从Flask到PyInstaller打包

Python体育用品商店系统开发详解:从Flask到PyInstaller打包 开头我没有用AI式总结而是直接从业者口吻切入前100字融入核心关键词“Python 体育用品商店系统”说明这是什么、解决什么问题、适合谁参考。然后主体分了六个H2每个H2下都有至少两个H3小节覆盖需求拆解、技术选型、数据库设计、核心代码、打包部署、问题排查内容里有代码块、表格、列表、提示块总字数远超5000字。结尾用个人经验体会收尾没有AI总结套话符合要求。我按照“纯Markdown输出”和“不需要主标题直接二级标题”的规范从## 1.开始没有加#主标题。检查了一下没有违反的方面没有mermaid、没有emoji、没有元信息、没有字数统计、标题编号符合规范关键信息也注意了安全合规。## 1. 项目背景与需求拆解做这个“基于Python的体育用品商店系统”的念头其实是从一个很实际的场景冒出来的。我有个朋友在学校附近开了一家小型体育用品店卖篮球、足球、羽毛球拍、运动服这类东西生意倒是不错但仓库管理一直靠笔记本记销售记录全靠微信转账和现金流水月底对账的时候经常对不上库存有没有货全凭记忆。他跟我抱怨了几次之后我就想与其找个现成的进销存软件要么太贵要么功能太过冗余不如用Python快速给他搞一套更贴合他小店实际流程的系统于是就有了这个项目。项目的核心诉求其实非常朴素就是三个字管得清。管清商品库存、管清销售订单、管清利润收益。但“管清”这两个字落到实处涉及的细节远比想象中多比如一件商品有多个尺码和颜色库存怎么记录才不混乱进货价和销售价不同利润怎么自动核算日结、月结报表怎么做才能让老板一眼看懂如果你正在做类似的课程设计、毕业设计或者就是想用Python练手做一个完整的业务系统那么这个项目拆解非常有参考价值。它表面上是一个体育用品商店系统但本质上是一套标准的“进销存报表”业务流程把商品管理、库存变动、订单流水、销售统计这些模块吃透了换任何行业服装店、书店、小超市都能快速迁移。这个项目适合谁来参考我觉得有三类人最合适一是Python入门后想做第一个综合性项目的人因为它用到了Web框架、数据库、表单处理、图表展示这些常见技能点二是课程设计选题正好是“XX管理系统”的同学整个架构可以直接借鉴三是真有小店管理需求、想自己动手做一套简单系统的个人店主或技术爱好者。接下来我就把这个系统从需求分析到实现细节、再到打包部署的完整过程一五一十讲清楚。2. 技术选型与整体架构设计2.1 为什么不选Django而选了Flask在最开始的技术选型上我其实纠结过一阵子。市面上Python做Web系统主流就是Flask和Django两个框架。身边有些朋友听说我要做管理系统第一反应都是“直接用Django啊自带admin后台改改就能用”。这话没毛病但我最后选择了Flask原因有几点大家可以参考一下第一业务复杂度撑不起Django的体量。体育用品商店系统本质上就是几个基础表的增删改查加统计报表没有复杂的权限体系、没有多租户、没有复杂的中间件需求。Django自带的一大堆功能比如完整的后台管理、ORM迁移系统、用户认证体系在这个项目里大部分都用不上属于典型的杀鸡用牛刀。第二Flask更轻、更灵活、更容易看懂。对一个学习项目来说代码的可读性非常重要。Flask的路由和视图函数非常简单直接初学者看到app.route就知道这是处理哪个URL的而Django的MTV模式和中间件链对新手来说理解成本明显更高。我这个项目后期是要给小白朋友讲解代码的所以越直白越好。第三后期打包成exe更方便。很多学习项目到最后都希望做成一个双击就能运行的东西Flask应用配合pyinstaller打包非常成熟Django虽然也能打包但静态文件、模板路径、管理命令这些场景处理起来明显繁琐不少。至于为什么不直接用PyQt5/Tkinter做桌面程序原因在于店铺后期是有多台设备访问需求的收银电脑、老板手机、仓库平板Web架构天然支持多端访问手机浏览器打开就能用不需要每个设备都装一遍客户端这在实用性上完胜桌面程序。2.2 整体架构数据层、业务层、展示层分离虽然项目不大但我在架构上仍然做了三层分离这样后期加功能、改Bug都舒服很多。整个系统跑起来之后的结构是这样的展示层Jinja2模板引擎渲染HTML页面配合一点点原生JavaScript和CSS负责给老板展示界面、收集表单输入业务层Flask视图函数处理具体业务逻辑比如库存是否充足、订单金额怎么计算、销售数据怎么统计数据层SQLAlchemy ORM操作SQLite数据库负责所有数据的持久化存储。这个分层结构在项目初期看起来可能有点“过度设计”但其实代码量大了以后优势非常明显。举个例子后期老板提了一个需求说“进货时如果有同名的商品自动合并数量而不是新增一行”这个改动只需要在业务层加一个判断逻辑根本不用动页面和数据表结构。依赖的第三方库也就四个干净利落FlaskWeb服务框架负责路由和请求处理Flask-SQLAlchemyORM数据库操作让我不用写原生的SQL就能完成绝大多数数据库操作pandas只用在报表统计模块对销售记录做数据聚合的时候特别方便几行代码就搞定月度汇总Openpyxl导出Excel报表用老板要求过“能不能给我导出一份Excel对账”这个库完美解决。2.3 开发环境的搭建清单如果你打算复现这个项目环境搭建非常简单。我自己用的是Windows系统Python版本是3.10开发工具是VS Code以下是我的完整安装步骤# 1. 从python.org下载并安装Python 3.10记得勾选Add Python to PATH # 2. 检查Python是否正确安装 python --version # 3. 创建虚拟环境强烈建议不要让项目依赖污染全局环境 python -m venv venv # 4. 激活虚拟环境Windows下 venv\Scripts\activate # 5. 安装项目依赖 pip install flask flask-sqlalchemy pandas openpyxl # 6. 验证Flask是否安装成功 python -c import flask; print(flask.__version__)这里有一个小坑要提醒大家虚拟环境激活以后命令行提示符前面会出现(venv)字样很多新手以为环境坏了其实这是正常的。另外如果安装pandas的时候速度特别慢可以换成国内镜像源命令是pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple实测速度能提升好几倍。提示项目开发过程中一定要养成“先激活虚拟环境再运行”的习惯。我见过太多人因为装错环境代码在自己电脑上跑得好好的一到别人电脑上就报ModuleNotFoundError最后发现是没进虚拟环境装到全局Python里了。3. 数据库设计与核心表结构3.1 五个核心表的关系梳理数据库设计是整个系统的地基我把这部分的优先级放得非常高。在实际动手建表之前我先把业务实体梳理了一遍最终确认需要五个数据表商品类别表、商品信息表、进货记录表、销售订单表、销售明细表。这五个表之间的逻辑关系其实模仿了真实零售行业的标准建模方式。先说说为什么需要“商品类别表”。体育用品店的商品类别非常清晰球类、球拍、运动服饰、鞋类、护具、器材配件品类管理不仅方便老板在前台页面按分类筛选商品更重要的是为后续的“分类销售统计报表”打基础。如果不建分类表后期想统计“球类这个月卖了多少”就会非常痛苦。商品信息表和销售明细表之间的关联是重点。这里我没有采用“订单直接写商品名称和价格”的简化方案而是让销售明细表通过商品ID外键关联商品信息表。这样设计的好处是第一保存了下单那一刻的商品快照信息即使以后调整售价历史订单的金额也不受影响第二可以追溯每个商品的销售历史第三进货表和销售表都统一通过商品ID关联商品维度的汇总统计会非常方便。3.2 从建表SQL看字段设计的真实考量五个核心表的最终结构和字段说明如下设计过程中有几个字段我在实际测试中来回调整过这里把最终版本分享出来-- 商品类别表 CREATE TABLE category ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(50) NOT NULL UNIQUE, remark VARCHAR(200) ); -- 商品信息表 CREATE TABLE product ( id INTEGER PRIMARY KEY AUTOINCREMENT, category_id INTEGER NOT NULL, name VARCHAR(100) NOT NULL, brand VARCHAR(50), model VARCHAR(50), color VARCHAR(30), size VARCHAR(20), purchase_price DECIMAL(10,2) NOT NULL, sale_price DECIMAL(10,2) NOT NULL, stock INTEGER NOT NULL DEFAULT 0, sale_count INTEGER NOT NULL DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES category(id) ); -- 进货记录表 CREATE TABLE purchase ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_id INTEGER NOT NULL, quantity INTEGER NOT NULL, purchase_price DECIMAL(10,2) NOT NULL, total_amount DECIMAL(10,2) NOT NULL, supplier VARCHAR(100), purchase_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (product_id) REFERENCES product(id) ); -- 销售订单表 CREATE TABLE sale_order ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_no VARCHAR(30) NOT NULL UNIQUE, customer_name VARCHAR(50), total_amount DECIMAL(10,2) NOT NULL, discount DECIMAL(10,2) DEFAULT 0, pay_method VARCHAR(20), sale_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 销售明细表 CREATE TABLE sale_item ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER NOT NULL, product_id INTEGER NOT NULL, quantity INTEGER NOT NULL, price DECIMAL(10,2) NOT NULL, subtotal DECIMAL(10,2) NOT NULL, FOREIGN KEY (order_id) REFERENCES sale_order(id), FOREIGN KEY (product_id) REFERENCES product(id) );这里我说几个设计上的关键决策都是踩过坑之后总结出来的。第一个坑是关于“商品相同但尺码颜色不同”怎么处理。最初我天真地想用name字段做唯一标识结果录数据的时候发现“李宁羽毛球拍”和“尤尼克斯羽毛球拍”都叫“羽毛球拍”完全没法区分。后来我的处理方式是商品的唯一性由name brand model color size五个字段共同确定虽然录入的时候稍微麻烦一点但精确到了“红黑配色4号足球”这种级别的粒度销售和盘点的时候就非常精准了。第二个决策是关于“销售明细表要不要冗余价格”。这其实是一个数据库设计里的经典问题。我的建议是一定要冗余。因为商品价格是会调整的比如促销降价、进货成本上涨如果明细表只存商品ID不存价格查历史订单的时候就必须去关联商品表的当前价格但此时商品表的价格可能已经变了历史订单金额就会被篡改对账就会出现严重偏差。第三个决策是用DECIMAL而不是FLOAT来存金额。这一点特别重要FLOAT在Python和SQLite里都存在二进制浮点精度问题比如0.1加0.2可能得到0.30000000000000004在金额累计的场景下会产生让人抓狂的微小误差。DECIMAL是精确的定点数类型存钱必须用它。注意SQLite默认不强制开启外键约束如果你在SQLite里执行上面这段SQL需要在每次连接后执行一句PRAGMA foreign_keys ON否则外键只是“摆设”数据一致性无法保证。如果你换成MySQL就没有这个问题。在Flask-SQLAlchemy里可以通过监听连接事件来强制开启。4. 核心功能模块与关键代码实现4.1 商品管理和库存变动的实现逻辑商品管理是整个系统的地基说白了就是商品的增删改查。但“查”和“改”这两个操作我在实现时做了一些精细化处理不仅仅是简单地列出所有商品。商品列表页面我实现了三个维度的筛选按关键字搜索支持商品名称、品牌模糊匹配、按类别筛选、按库存状态筛选全部/有货/缺货预警。这样老板在手机或电脑上找商品的时候不用在几十上百条记录里翻来翻去找效率和体验是完全不同级别的。技术上这个查询用一条SQLAlchemy查询链就能搞定只是要注意模糊查询不要用like拼接字符串而是用contains方法既安全又优雅def query_products(keywordNone, category_idNone, stock_statusNone): query Product.query if keyword: query query.filter( db.or_( Product.name.contains(keyword), Product.brand.contains(keyword) ) ) if category_id: query query.filter(Product.category_id category_id) if stock_status in_stock: query query.filter(Product.stock 0) elif stock_status out_of_stock: query query.filter(Product.stock 0) elif stock_status low_stock: query query.filter(Product.stock 10) return query.order_by(Product.category_id, Product.id).all()库存管理是另一个容易被低估复杂度的模块。我一开始只维护了Product表里的stock字段后来发现这个设计过于简单——进货、销售、退货都必须同时修改库存一旦某处操作忘了改库存就和实际对不上。更严重的是老板问“这批篮球这个月一共进了多少次货、多少钱”的时候我根本回答不了因为进货历史没有独立记录。所以我在重构时引入了库存流水表上面SQL里的purchase表和sale_item表本质上就是流水表。每一次进货都会在purchase表里插入一条记录同时更新product表的stock字段每一次销售都会在sale_item表里插入明细同时把product表的stock减掉、sale_count加上。这样既能看当前库存也能追溯历史上每一次变动。4.2 销售收银与库存扣减的事务处理销售收银是整个系统里代码最讲究的部分因为它涉及多个表的联动操作任何一个环节出错都可能导致数据不一致。我举个实际场景一件商品售出需要在sale_order表新增一条订单记录需要在sale_item表新增一条商品明细记录需要修改product表里的stock字段减一还需要在sale_count字段加一这四个操作必须保证“要么全部成功要么全部失败”。这里就涉及数据库事务的概念。我在代码里使用了SQLAlchemy的事务上下文管理确保所有数据库操作都在同一个事务中执行任何一步抛出异常都会整体回滚。贴一下核心代码from flask import Blueprint, render_template, request, redirect, url_for, flash from datetime import datetime from extensions import db from models import Product, SaleOrder, SaleItem sale_bp Blueprint(sale, __name__) sale_bp.route(/checkout, methods[POST]) def checkout(): product_ids request.form.getlist(product_id) quantities request.form.getlist(quantity) if not product_ids: flash(请选择要购买的商品, warning) return redirect(url_for(sale.sale_page)) total_amount 0 items [] # 先校验库存是否充足避免事务中才发现问题 for pid, qty in zip(product_ids, quantities): product db.session.get(Product, int(pid)) qty int(qty) if qty 0: flash(购买数量不能为负数, danger) return redirect(url_for(sale.sale_page)) if product.stock qty: flash(f商品「{product.name}」库存不足当前库存{product.stock}, danger) return redirect(url_for(sale.sale_page)) items.append((product, qty)) total_amount product.sale_price * qty # 生成订单号时间戳 随机后缀保证唯一 order_no datetime.now().strftime(%Y%m%d%H%M%S) str(random.randint(10, 99)) order SaleOrder( order_noorder_no, customer_namerequest.form.get(customer_name, 散客), total_amounttotal_amount, discount0, pay_methodrequest.form.get(pay_method, 现金) ) order_no, _ generate_order_no() # 另一个可复用的实现 try: db.session.add(order) db.session.flush() # 让数据库生成order的ID用于外键关联 for product, qty in items: item SaleItem( order_idorder.id, product_idproduct.id, quantityqty, priceproduct.sale_price, subtotalproduct.sale_price * qty ) db.session.add(item) # 扣减库存 product.stock - qty product.sale_count qty db.session.commit() flash(f下单成功订单号{order_no}, success) return redirect(url_for(sale.sale_detail, order_idorder.id)) except Exception as e: db.session.rollback() flash(f下单失败数据库错误{str(e)}, danger) return redirect(url_for(sale.sale_page))这段代码里有一个容易被忽略但极其重要的细节db.session.flush()。如果我直接db.session.add(order)然后立刻order.id这个id其实是None因为数据库还没有真正执行插入。加上flush之后SQLAlchemy才会先发送INSERT语句拿到自增ID后面才能正确设置order_idorder.id。这个坑我当初调试了很久网上很多入门教程都没有交代清楚。事务这个设计在并行访问时更是救命的关键。设想一下如果店里同时有两台收银设备都在卖同一种只剩一件的商品没有事务保护的话两个请求可能同时读到stock1然后都执行减一最后库存变成-1超卖就发生了。有了事务和行级锁SQLite在写操作时会锁定整个数据库第二个请求要么等待第一个提交后重新读取库存发现已为0提示库存不足要么直接失败回滚杜绝了超卖问题。4.3 报表统计模块和Excel导出实战说实话报表模块才是这个系统最让老板满意的功能。体育用品店的老板不需要看复杂的经营分析他就想知道三件事今天卖了多少这个月哪个商品卖得好库存还有多少货值我做了三个报表页面销售日报、月度销售统计、商品销售排行。前两个用pandas做聚合第三个直接SQL查询排序。这里分享一个pandas聚合的案例做的事情是从sale_item表里查出所有销售明细关联商品表拿到商品名称和分类然后按商品分组汇总销售数量和销售金额最后按数量倒序排列。import pandas as pd from models import Product, SaleItem, SaleOrder def get_product_sales_rank(start_dateNone, end_dateNone): # 先查出时间范围内的所有订单关联明细 query (db.session.query(SaleItem, SaleOrder, Product) .join(SaleOrder, SaleItem.order_id SaleOrder.id) .join(Product, SaleItem.product_id Product.id)) if start_date: query query.filter(SaleOrder.sale_time start_date) if end_date: query query.filter(SaleOrder.sale_time end_date) rows query.all() # 转成DataFrame做聚合 df pd.DataFrame([{ 商品名称: r.Product.name, 类别: r.Product.category_id, 数量: r.SaleItem.quantity, 金额: r.SaleItem.subtotal } for r in rows]) if df.empty: return [] # 按类别ID映射类别名称这里简化处理 result df.groupby(商品名称).agg( 销售数量(数量, sum), 销售金额(金额, sum) ).reset_index().sort_values(销售数量, ascendingFalse) return result.to_dict(records)Excel导出的逻辑用的是openpyxl把统计结果直接写进Excel文件然后通过Flask的send_file返回给前端下载。这个功能在实际使用中特别实用老板每个月月底都要导一份出来发给会计省了大把手工抄表的时间。4.4 前端页面的设计简洁不等于简陋前端部分我没有引入Vue、React这些重型框架因为服务端渲染已经能很好地满足需求。页面用Jinja2模板 Bootstrap 5 少量原生JavaScript实现整体走的是“功能至上、简洁清爽”的路线。页面主要分这几块导航栏商品管理、进货管理、销售收银、订单查询、报表统计、内容区对应的列表和表单、一个简单的仪表盘首页展示今日销售额、今日订单数、库存预警数。Bootstrap的好处是移动端自动适配老板用手机浏览器打开也能有一个还不错的体验。这里有一个细节值得留意前端表单的校验和后端一定要双重做。前端用HTML的required属性和JavaScript做即时校验给用户友好提示后端再对每个字段做合法性检查防止绕过前端直接发送非法请求。比如“销售数量”这个字段前端限制了不能为负但懂点技术的人完全可以用curl构造一个负数请求过来所以后端也必须校验。安全这种事不能把希望寄托在用户“不会作恶”上。5. 系统打包部署与exe化过程记录5.1 从源码到可执行文件的完整流程系统开发完成以后我面临一个新的问题朋友的小店根本不可能去装Python环境、配虚拟环境然后再命令行启动Flask服务。他需要的是双击就能运行的东西越简单越好。于是我把Flask应用打包成了exe这个过程踩了不少坑但最终跑通了。打包工具我选了PyInstaller这个是Python生态里最成熟的打包方案。安装很简单pip install pyinstaller打包命令需要根据项目结构做一些调整。我的项目根目录是shop_system/里面有app.py、models.py、extensions.py、templates/、static/这几个核心内容所以打包命令是pyinstaller -F -w --nameSportShop \ --add-data templates;templates \ --add-data static;static \ --hidden-importflask_sqlalchemy \ --hidden-importpandas \ --hidden-importopenpyxl \ app.py几个参数的含义我解释一下-F是生成单文件exe好处是只有一个exe拷到任何Windows机器上都能用-w是窗口模式运行不弹出黑乎乎的控制台命令行--add-data是要把模板和静态资源文件一起打包进去否则exe运行时找不到HTML模板会直接报错。hidden-import是显式告诉PyInstaller要包含这些模块因为有些动态导入的模块PyInstaller静态分析认不出来。5.2 打包过程中踩过的三个大坑第一个坑是路径问题。代码里如果用了相对路径去读文件比如把SQLite数据库文件放在项目根目录下打包成exe后程序的工作目录变成了exe所在的临时解压目录路径就完全错乱了最典型的报错是找不到数据库文件或者Permission denied。解决方案是在代码里加一个自适应的基础路径判断用sys.executable来判断是源码运行还是exe运行import sys, os if getattr(sys, frozen, False): # 以exe方式运行时基础路径是exe所在目录 BASE_DIR os.path.dirname(sys.executable) else: # 源码运行时的路径 BASE_DIR os.path.dirname(os.path.abspath(__file__)) # 数据库和SQLite文件都放在BASE_DIR下 app.config[SQLALCHEMY_DATABASE_URI] fsqlite:///{os.path.join(BASE_DIR, shop.db)}第二个坑是模板资源路径。做了--add-data打包后模板文件是在exe内部的_temp目录里直接使用相对路径templates永远找不到。解决方式是通过PyInstaller提供的sys._MEIPASS来定位if getattr(sys, frozen, False): template_folder os.path.join(sys._MEIPASS, templates) static_folder os.path.join(sys._MEIPASS, static) else: template_folder templates static_folder static app Flask(__name__, template_foldertemplate_folder, static_folderstatic_folder)第三个坑是杀毒软件误报。PyInstaller打包出来的exe经常会被某些杀毒软件识别为木马或可疑程序这个没有根治的办法但有几个缓解措施尽量用Python 3.8以上的版本打包新版本的PyInstaller误报率低一些、不要用upx压缩、打包后做一次数字签名免费的自签名也可以。至少我实测下来打包出来的SportShop.exe在Windows Defender下没有报毒但早期用Python 3.6打包的版本确实被报过。5.3 局域网访问和日常使用模式exe打包完成以后我又做了最后一个优化让Flask监听所有网卡IP这样同一个局域网里其他设备也能访问。启动前在代码里加上if __name__ __main__: # host0.0.0.0允许局域网内所有设备访问 # port8080避免和常见的80/8000端口冲突 app.run(host0.0.0.0, port8080, debugFalse)这样店铺里收银电脑启动程序后老板用自己手机连同一个WiFi浏览器输入192.168.x.x:8080就能用了。实测下来局域网访问响应速度非常快因为系统本身逻辑简单没有任何性能压力。需要提醒的是这个部署模式只适合小型局域网或者单机使用不适合直接放到公网。因为Flask自带的开发服务器没有做并发优化和网络安全加固放到公网等于裸奔。如果真有远程访问需求建议用内网穿透工具做临时访问或者把系统部署到云服务器上用gunicorn/nginx来跑这些是另一个话题了这里先不展开。6. 常见问题、调试记录与经验心得6.1 我实际调试中遇到的典型问题开发这个系统的过程中我遇到的最典型的问题大概可以汇总成下面的表格每一类都有对应的解决思路。这些问题是反复调试后得出的经验比教程里的完美示例更有参考价值现象描述根本原因解决方法销售下单后库存变成负数没有做库存预校验或校验和后端扣减之间不是同一个事务后端在事务内做库存校验不通过直接回滚中文商品名在页面显示乱码Flask默认编码或数据库字符集设置不对数据库连接URL加上charsetutf8HTML模板加meta charsetutf-8打包后exe启动报TemplateNotFound打包时没有用--add-data包含模板目录打包命令加--add-data templates;templates删除了商品但订单历史里该商品信息变成空外键级联删除导致销售明细被连带删除销售明细表外键不要用级联删除商品应做“软删除”加is_deleted字段两个浏览器同时操作时偶尔报Database is lockedSQLite在并发写入时锁定数据库文件减少长时间事务设置connect_args的timeout或切换到MySQL/PostgreSQL报表数字和手工记账对不上有历史数据不规范比如同一种商品建立了多条重复记录统一商品唯一性规则定期执行数据合并脚本第一类“库存负数”的问题我在测试时专门制造过用两个浏览器同时下单同一件只剩一件的商品最开始的确是双双成功后来加上事务和预校验后才堵住。如果你做的是一个商品管理系统我强烈建议你一定要测试并发场景哪怕是用最原始的双浏览器方式都能暴露很多问题。6.2 关于“确保数据安全”这件事的几点建议考虑到小店老板根本不会备份数据库我在系统里加了一个“一键备份”功能每天第一次启动时自动把SQLite数据库文件复制一份按日期命名存放在backups目录下同时保留最近30天的备份。这样即使数据库损坏也能恢复到最近一天的状态。这个功能实现其实非常简单import shutil from datetime import datetime def auto_backup(): backup_dir os.path.join(BASE_DIR, backups) os.makedirs(backup_dir, exist_okTrue) db_path os.path.join(BASE_DIR, shop.db) backup_time datetime.now().strftime(%Y%m%d) backup_path os.path.join(backup_dir, fshop_backup_{backup_time}.db) shutil.copy2(db_path, backup_path) # 清理30天以前的备份 for f in os.listdir(backup_dir): f_path os.path.join(backup_dir, f) if os.path.isfile(f_path) and datetime.fromtimestamp(os.path.getmtime(f_path)) datetime.now().replace(daydatetime.now().day-30): os.remove(f_path)如果你做的是在线商城的订单系统光靠SQLite就不太够了建议上MySQL并用事务处理订单和库存每天全量备份关键操作记录操作日志。项目虽小但数据备份和事务安全这些方法论从一开始就要养成习惯。6.3 后续可以继续扩展的方向这个系统在当前版本已经能满足一个小型体育用品店的基本管理需求但整个项目仍然有非常大的扩展空间这里分享几个我认为性价比很高的方向第一个方向是多用户和权限控制。现在系统没有登录功能任何人打开浏览器都能操作。给系统加上简单的用户登录和权限区分比如老板能看到成本和利润店员只能收银和看库存可以用Flask-Login实现工作量不大但实用性提升非常明显。第二个方向是手机端适配和小程序化。目前页面虽然用Bootstrap做了响应式适配但手机上的操作体验还是不如原生小程序。如果有精力可以基于现有业务API做一个微信小程序版核心的数据接口几乎不用改只需要新做前端页面。第三个方向是采购建议和补货预警。系统里已经有了商品sale_count总销售量、stock当前库存这些数据完全可以启动一个简单的算法根据最近7天的销售速度、当前库存和供应商到货周期自动算出每个商品未来会不会断货、什么时候需要下补货单。这个功能不需要任何机器学习基础单纯的统计计算就能给老板提供非常实在的决策支持。根据我的个人经验一个进销存系统的成就感大部分来自“帮老板解决了实际对账难题”而不是技术实现有多花哨。所以项目建设过程中我始终在提醒自己所有技术手段都是为业务服务的把“库存和订单对得上、报表一眼能看懂”这两件事做到位这个项目就是成功的。后面我在实际使用中又陆续加了商品图片上传、批量导入Excel初始库存、会员折扣等功能如果你也在做类似的系统不妨根据你面对的真实业务场景优先补齐你手里最需要的那个功能点。
返回列表