ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python+SQLite实现体育用品商店管理系统:从需求到打包部署

Python+SQLite实现体育用品商店管理系统:从需求到打包部署 做了这么多年开发我一直觉得一个系统最理想的落地场景就是能在身边真实跑起来、真刀真枪解决具体问题的。前阵子一个开体育用品店的朋友跟我吐槽说店里用的收银软件又贵又难用Excel管库存早就乱成一锅粥会员积分还得每天手动算。我听完直接说要不我用Python给你写一套管理系统吧反正Python做这种业务系统开发速度快、库又全一个人完全能搞定。于是就有了这个“基于Python的体育用品商店”项目内部代号hx2767一路从需求梳理、数据库设计、功能开发到打包给店员日常使用整个过程踩了不少坑也沉淀了不少心得。这篇博客我不会只贴代码而是把完整的思路、数据库设计、核心模块实现、常见坑位和排查技巧全部拆开讲清楚。不管你是刚学Python想找个练手项目的新手还是想在业余时间给朋友店里做个轻量管理工具这篇内容都能提供一套可以照着抄的完整方案。整套代码量不大但麻雀虽小五脏俱全商品管理、库存联动、购物车下单、销售统计、会员积分这些核心业务点全都有学完它你对Python操作SQLite、函数模块化设计、控制台交互、简单数据可视化这套组合拳会有一个质的理解。1. 项目概述与思路拆解1.1 为什么自己写一套商店管理系统朋友店里的核心痛点其实很典型商品种类多球拍、球鞋、运动服、护具、器材进货批次多尺码颜色各不相同再加上促销活动、会员折扣、退货换货传统的Excel表格管理早就变成了一场灾难。店里之前也试过几款市面上的收银系统要么是按年收费的小型SaaS要么是绑定特定硬件的本地软件功能堆得又多又杂真正用得上的就那两三个模块操作流程反而被拖得更慢。这就引出我决定自己动手写管理系统的核心原因业务逻辑并不复杂但需求非常个性化。比如朋友希望商品的库存能按“尺码”独立记录下单时如果库存不够就自动拦截希望每笔订单都能打印出带店铺Logo的小票希望月底能直接导出一份本月的热销商品排行。这些需求在通用收银软件里要么没有要么要额外付费定制但用Python写就是几个函数的事。所以如果你也面临类似的业务场景我的建议很明确先别急着买软件花几天时间梳理清楚自己的刚需如果核心流程不超过十个功能点Python这套方案绝对值得一试。1.2 功能边界与业务流转好明确了目标我做的第一件事不是写代码而是把商店的日常业务流程完整画了一遍。这里画流程图不是用复杂工具就是纸上把每个环节和流转关系写出来。体育用品店的日常业务大致是进货采购员录入新到商品包括名称、分类、进价、售价、库存尺码数量上架商品进入“在售”状态在门店货架和线上同步展示销售收银员选择商品支持多件商品加入购物车系统实时检查库存计算总价、会员折扣生成订单库存变动订单创建后自动扣减对应尺码库存若库存低于预警阈值则提醒补货会员管理用户可注册会员消费累积积分积分可抵扣现金数据报表老板需要看每日销售额、商品销量排行、库存汇总、利润估算。这套流程理完之后项目的功能边界就非常清晰了。我明确砍掉了两块暂时不做的内容一是复杂的采购供应商管理对单店来说暂时没必要二是线上商城对接涉及接口权限后续再扩展。做管理系统最忌讳的就是一开始想做大而全正确的做法是把支撑核心业务闭环的功能做扎实后续再按需迭代。1.3 技术选型为什么是Python这套系统用到的核心语言就是Python。我并不是说Python是所有业务系统的最佳选择而是它在这个场景下有非常明显的匹配优势第一开发效率极高。Python语法简洁没有Java那种繁琐的样板代码业务逻辑直接用自然语言式的代码表达写起来很快调试也方便。对于一个两周内就要上线的小型管理工具来说开发效率就是最大的竞争力。第二内置SQLite零成本持久化。单店管理系统完全没有必要上MySQL或者PostgreSQL这种独立数据库服务。Python自带的sqlite3模块可以直接操作SQLite数据库文件数据存在一个本地文件里备份就是复制文件迁移就是拷贝文件对商店老板来说几乎零学习成本。第三生态完善后续扩展空间大。比如后面想把销售数据做成可视化报表可以直接用matplotlib或pyecharts画图想做个简单的Web管理界面可以用Flask或FastAPI快速封装API想把脚本打包成exe给店员用有PyInstaller。Python这种“先用起来再逐步升级”的模式特别适合小项目快速落地。技术栈定下来之后就是Python SQLite 标准库sqlite3, datetime, os 第三方库prettytable表格美化 pyinstaller打包。没有用重型框架目的就是让整个系统轻量、可解释、容易改。2. 环境准备与项目初始化2.1 Python环境与虚拟环境配置如果你还没装Python这里我多说一句。Windows用户直接去Python官网下载安装包安装时务必勾选“Add Python to PATH”这个选项不然装完之后命令行里敲python会提示找不到命令。很多新手在这步就卡住了其实不是Python没装好而是环境变量没配好。Mac用户则建议直接用Homebrew安装比官网pkg安装包更好管理。项目开发时我强烈建议创建虚拟环境避免不同项目之间的依赖包版本互相冲突。虚拟环境相当于给当前项目建一个独立的Python运行空间你在里面pip安装的所有库都不会污染全局环境。操作方式很简单# 创建虚拟环境在项目目录下执行 python -m venv venv # Windows激活虚拟环境 venv\Scripts\activate # Mac / Linux激活虚拟环境 source venv/bin/activate # 激活成功后命令行前面会多一个(venv)前缀在虚拟环境里安装依赖库安装完可以用 pip freeze requirements.txt 把依赖清单导出来换机器部署的时候直接 pip install -r requirements.txt 就能一键恢复环境。这个习惯真的能让你少掉很多头发尤其当你同时开发三四个Python项目时虚拟环境绝对是最基本也最有效的隔离方案。2.2 依赖库安装与项目结构这个项目依赖的第三方库不算多核心就两个prettytable用于在控制台打印美观的表格pyinstaller用于最后把项目打包成exe文件。安装命令如下pip install prettytable pyinstaller安装依赖后我建议按功能模块把代码拆成多个文件而不是把所有代码堆在一个main.py里。这既是好习惯也方便后期维护和单独测试。我的项目结构大致是这样的sport_shop/ │ ├── main.py # 程序入口控制主菜单循环 ├── database.py # 数据库初始化与连接管理 ├── models.py # 数据模型与CRUD操作商品、订单、会员 ├── services/ # 业务逻辑层 │ ├── product_service.py # 商品管理 │ ├── order_service.py # 订单与购物车 │ ├── member_service.py # 会员与积分 │ └── report_service.py # 统计报表 ├── utils/ # 工具函数 │ ├── input_utils.py # 输入校验 │ └── display_utils.py # 表格展示 └── data/ └── hx2767.db # SQLite数据库文件自动生成这里我特意不引入Django、Flask这类重量级框架就是想让整个项目保持“逻辑清晰、层级分明、可单文件运行”的特点。每个模块各司其职数据库操作集中在database.py里业务逻辑在services层main.py只负责把菜单和用户操作串起来。有人可能会问控制台程序有必要分这么细吗我的经验是有必要尤其是当业务逻辑变得复杂时模块化是避免代码腐烂最有效的防线。2.3 数据结构设计建表数据库设计是整个系统的地基我花了大半天时间反复推敲最终确定了五张核心表。这里直接贴建表SQL-- 商品表 CREATE TABLE IF NOT EXISTS products ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, -- 商品名称 category TEXT NOT NULL, -- 分类球拍/球鞋/服饰/器材 brand TEXT, -- 品牌 purchase_price REAL NOT NULL, -- 进价 sale_price REAL NOT NULL, -- 售价 stock_quantity INTEGER NOT NULL, -- 总库存冗余字段便于查询 warning_threshold INTEGER DEFAULT 5, -- 库存预警阈值 created_at TEXT DEFAULT (datetime(now, localtime)) ); -- 商品尺码库存表支持同一商品不同尺码独立库存 CREATE TABLE IF NOT EXISTS product_skus ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_id INTEGER NOT NULL, sku_size TEXT NOT NULL, -- 尺码如 42/XL/标准 quantity INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (product_id) REFERENCES products(id) ); -- 会员表 CREATE TABLE IF NOT EXISTS members ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, phone TEXT UNIQUE NOT NULL, -- 手机号作为唯一标识 points INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime(now, localtime)) ); -- 订单表 CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_no TEXT UNIQUE NOT NULL, -- 订单编号 member_id INTEGER, -- 关联会员可空 total_amount REAL NOT NULL, -- 订单总额 discount_amount REAL DEFAULT 0, -- 优惠金额 final_amount REAL NOT NULL, -- 实付金额 points_earned INTEGER DEFAULT 0, -- 本次获得积分 created_at TEXT DEFAULT (datetime(now, localtime)) ); -- 订单明细表 CREATE TABLE IF NOT EXISTS order_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER NOT NULL, product_id INTEGER NOT NULL, sku_size TEXT, quantity INTEGER NOT NULL, price REAL NOT NULL, -- 成交单价快照防止商品改价后历史订单出错 FOREIGN KEY (order_id) REFERENCES orders(id) );这里有两个设计细节我要特别说明一下。第一订单明细表单独建表没有直接把商品名字和价格塞进订单表。这样做的好处是一张订单可以包含多个商品查询订单详情时通过order_id关联即可同时price字段存的是成交那一刻的单价快照后续商品改价不会影响历史订单的数据准确性。第二product_skus表实现一对多尺码库存这是根据体育用品店的实际场景设计的同一款球鞋可能有40到45的六个尺码库存总库存是SKU库存的冗余汇总下单时校验指定尺码的库存扣减时也只扣那一个尺码这样才能真正避免“有总库存但没对应尺码”的尴尬。3. 核心功能实现与实操要点3.1 商品管理模块录入、查询、修改、库存预警商品管理是整套系统最先写的模块也是整个项目的基础。先看代码# services/product_service.py import sqlite3 from prettytable import PrettyTable from database import get_connection def add_product(name, category, brand, purchase_price, sale_price, skus, threshold5): 新增商品skus是一个列表元素为(尺码, 数量) conn get_connection() cursor conn.cursor() try: # 事务开始 cursor.execute( INSERT INTO products (name, category, brand, purchase_price, sale_price, stock_quantity, warning_threshold) VALUES (?, ?, ?, ?, ?, ?, ?) , (name, category, brand, purchase_price, sale_price, sum(qty for _, qty in skus), threshold)) product_id cursor.lastrowid # 插入尺码库存 for sku_size, qty in skus: cursor.execute( INSERT INTO product_skus (product_id, sku_size, quantity) VALUES (?, ?, ?) , (product_id, sku_size, qty)) conn.commit() return product_id except Exception as e: conn.rollback() raise e finally: conn.close() def list_products(): 展示所有商品信息 conn get_connection() cursor conn.cursor() cursor.execute( SELECT p.id, p.name, p.category, p.brand, p.purchase_price, p.sale_price, p.stock_quantity, p.warning_threshold FROM products p ORDER BY p.id ) rows cursor.fetchall() conn.close() table PrettyTable([ID, 名称, 分类, 品牌, 进价, 售价, 库存, 预警阈值]) for row in rows: warning ⚠ 低库存 if row[6] row[7] else table.add_row([row[0], row[1], row[2], row[3], row[4], row[5], row[6], f{row[7]} {warning}]) print(table)写这段代码的时候有几个点值得展开说。事务处理是我特别强调的。新增商品时不仅要往products表插一条记录还要往product_skus表插入多条尺码库存记录这两步操作必须保证“要么全部成功要么全部失败”。如果商品信息插入成功了尺码库存却插入失败数据库里就会出现一个“没有库存信息”的幽灵商品。所以我用conn.commit()和conn.rollback()把这组操作包在一个事务里任何一步出错整体回滚。SQL参数化也是一个基本但极其重要的安全点。很多人喜欢用f-string拼接SQL一旦商品名称里带个单引号SQL直接报错更严重的是如果做完Web化改造拼接SQL会成为SQL注入的重灾区。用?占位符传参数既安全又省去转义字符的麻烦。库存预警的显示逻辑看似简单实际是店里老板最常用到的功能。我不仅在商品列表里标注了低库存商品还单独写了一个check_low_stock()函数启动系统时自动扫描一遍所有低于预警阈值的商品直接提醒“该进货了”。这个小功能后来被朋友评价为“全系统最实用的功能”。3.2 购物车与订单中心从加购到完成支付订单模块是整套系统的核心链路也是业务逻辑最复杂的地方。我的实现思路是用户在控制台输入商品ID和尺码系统把商品加入购物车一个Python字典确认结算时再一次性创建订单。这样可以避免频繁操作数据库也让“购物车”这个用户心智保持一致。# services/order_service.py from collections import defaultdict cart defaultdict(lambda: {name: , price: 0, quantity: 0, sku_size: }) def add_to_cart(product_id, sku_size, quantity): 将商品加入购物车实时校验库存 conn get_connection() cursor conn.cursor() cursor.execute( SELECT sk.id, p.name, p.sale_price, sk.quantity FROM product_skus sk JOIN products p ON p.id sk.product_id WHERE sk.product_id ? AND sk.sku_size ? , (product_id, sku_size)) row cursor.fetchone() conn.close() if not row: return {success: False, message: f商品ID {product_id} 没有尺码 {sku_size} 的库存} sku_id, name, price, stock row # 检查购物车已有数量 本次购买数量 是否超过库存 cart_qty cart[product_id][quantity] if product_id in cart else 0 if cart_qty quantity stock: return {success: False, message: f库存不足当前尺码可用 {stock} 件购物车已有 {cart_qty} 件} cart[product_id] {name: name, price: price, quantity: cart_qty quantity, sku_size: sku_size} return {success: True, message: f已加入购物车{name} x {quantity}} def checkout(member_idNone): 结算购物车生成订单并扣减库存 if not cart: return {success: False, message: 购物车为空} conn get_connection() cursor conn.cursor() try: total sum(item[price] * item[quantity] for item in cart.values()) discount 0 points_used 0 member None final_amount total # 会员折扣与积分处理 if member_id: cursor.execute(SELECT id, name, points FROM members WHERE id ?, (member_id,)) member cursor.fetchone() if member: # 1. 先使用积分抵扣每100积分抵10元 points_to_use min(member[2], int(total / 10) * 100) points_used points_to_use discount points_to_use / 10 final_amount total - discount # 生成订单号格式年月日时分秒 随机两位 order_no datetime.now().strftime(%Y%m%d%H%M%S) str(random.randint(10, 99)) cursor.execute( INSERT INTO orders (order_no, member_id, total_amount, discount_amount, final_amount, points_earned) VALUES (?, ?, ?, ?, ?, ?) , (order_no, member_id, total, discount, final_amount, int(final_amount // 10))) order_id cursor.lastrowid # 插入订单明细 扣减库存 for product_id, item in cart.items(): cursor.execute( INSERT INTO order_items (order_id, product_id, sku_size, quantity, price) VALUES (?, ?, ?, ?, ?) , (order_id, product_id, item[sku_size], item[quantity], item[price])) # 扣减总库存和对应尺码库存 cursor.execute( UPDATE products SET stock_quantity stock_quantity - ? WHERE id ? , (item[quantity], product_id)) cursor.execute( UPDATE product_skus SET quantity quantity - ? WHERE product_id ? AND sku_size ? , (item[quantity], product_id, item[sku_size])) # 更新会员积分 if member: earned_points int(final_amount // 10) cursor.execute( UPDATE members SET points points - ? ? WHERE id ? , (points_used, earned_points, member_id)) conn.commit() cart.clear() return {success: True, message: f订单 {order_no} 创建成功实付 {final_amount:.2f} 元} except Exception as e: conn.rollback() raise e finally: conn.close()这段代码是项目里最核心的部分我拆开讲几个关键决策。库存校验的时机加入购物车时校验一次库存结算时并不重复校验。这里我想清楚了一个问题——单店收银场景通常没有并发抢购的压力同一时间一个店员在操作后台购物车阶段校验一次完全够用。但如果你未来要接线上商城结算时就必须重新校验一遍防止两个用户同时下单导致超卖。这个决策我写在代码注释里了后续扩展的时候一目了然。积分抵扣的算法我设计的是每100积分抵10元且本轮积分抵扣的金额不能超过订单总额的10%。这个规则是跟朋友商量后根据店铺毛利率定的每个店可以根据实际情况调整。积分来源是消费额的十分之一消费10元积1分这样形成“消费→积分→抵扣”的闭环既能激励会员复购又不会让折扣力度大到伤及利润。订单号生成用时间戳加随机数格式类似“2025031514302517”。这种生成方式在单店场景下足够唯一而且订单号里带着时间信息按订单号排序就能看出下单先后顺序后面做日结算时非常方便。3.3 库存联动与入库管理库存是整个系统里最不能出错的数据所以我特意把库存变动相关的逻辑集中到一起。上一节的结算扣库存是“出”这一节对应的是“入”——进货入库。def restock(product_id, sku_size, quantity, purchase_priceNone): 进货入库新增库存可选更新进价 conn get_connection() cursor conn.cursor() try: if purchase_price: cursor.execute(UPDATE products SET purchase_price ? WHERE id ?, (purchase_price, product_id)) cursor.execute( UPDATE product_skus SET quantity quantity ? WHERE product_id ? AND sku_size ? , (quantity, product_id, sku_size)) cursor.execute( UPDATE products SET stock_quantity stock_quantity ? WHERE id ? , (quantity, product_id)) conn.commit() return {success: True} except Exception as e: conn.rollback() raise e finally: conn.close()这里要和上一节强调的事务处理保持一致更新SKU库存和商品总库存必须放在同一个事务里。我见过不少类似的Demo代码只更新了SKU表结果总库存和明细库存对不上后期排查数据问题的时候那叫一个痛苦。实际操作中的经验进货入库时一定要顺手核对进价。体育用品的进价波动其实不小品牌方隔段时间就会调一次供货价如果进货时不更新进价月底利润统计就会失真。所以我的restock函数里做了一个可选参数purchase_price进货时如果发现进价变了顺手就把新的进价更新进去之后统计利润时才能算得准。3.4 数据报表模块销售统计与热销排行系统上线跑了一周后朋友说最想要的功能不是收银而是月底能一键知道“到底哪个羽毛球拍卖得最好”。于是报表模块就成了项目后期重点迭代的部分。# services/report_service.py def daily_sales_report(date): 查询某一天的销售汇总 conn get_connection() cursor conn.cursor() cursor.execute( SELECT COUNT(*), COALESCE(SUM(total_amount), 0), COALESCE(SUM(final_amount), 0) FROM orders WHERE date(created_at) ? , (date,)) row cursor.fetchone() conn.close() print(f日期: {date}) print(f订单数: {row[0]}) print(f订单原价总额: {row[1]:.2f} 元) print(f实收总额: {row[2]:.2f} 元) def top_selling_products(limit10): 查询热销商品TOP N conn get_connection() cursor conn.cursor() cursor.execute( SELECT p.name, p.brand, oi.sku_size, SUM(oi.quantity) as total_qty, SUM(oi.price * oi.quantity) as total_sales FROM order_items oi JOIN products p ON p.id oi.product_id JOIN orders o ON o.id oi.order_id WHERE date(o.created_at) date(now, -30 days) GROUP BY p.id, oi.sku_size ORDER BY total_qty DESC LIMIT ? , (limit,)) rows cursor.fetchall() conn.close() table PrettyTable([商品, 品牌, 尺码, 销量, 销售额]) for row in rows: table.add_row([row[0], row[1], row[2], row[3], f{row[4]:.2f}]) print(table)这个模块最有价值的地方在于通过订单明细表 order_items 里的sku_size字段可以精确到某个商品某个尺码的销量。比如一款篮球鞋整体销量不错但42码卖得特别好、44码库存积压这时候就是补货和调拨的决策依据。朋友以前靠感觉判断该进多少货现在看报表一目了然。如果你想把报表做得更直观可以再引入matplotlib把热销排行画成柱状图或者用pyecharts生成交互式HTML图表。我后期加了一个简单的销售趋势图功能用matplotlib画出最近30天的销售额折线图老板每天早上打开系统看一眼趋势心里就有数了。代码大概是这样import matplotlib.pyplot as plt def sales_trend_chart(days30): conn get_connection() cursor conn.cursor() cursor.execute( SELECT date(created_at) as d, SUM(final_amount) FROM orders WHERE date(created_at) date(now, ?) GROUP BY date(created_at) ORDER BY d , (f-{days} days,)) rows cursor.fetchall() conn.close() dates [row[0] for row in rows] amounts [row[1] for row in rows] plt.figure(figsize(10, 5)) plt.plot(dates, amounts, markero) plt.title(f最近 {days} 天销售趋势) plt.xlabel(日期) plt.ylabel(销售额(元)) plt.xticks(rotation45) plt.tight_layout() plt.show()用matplotlib之前要记得pip install matplotlib并且中文字体显示需要额外设置一下不然图表上的中文全是方块。Windows系统可以加一行plt.rcParams[font.sans-serif] [SimHei]Mac系统换成[PingFang SC]。4. 常见问题与排查技巧实录4.1 控制台中文乱码问题这个项目在Windows控制台运行时第一次打印中文商品名就出现了乱码。根本原因是Python默认的输出编码和Windows命令行编码不一致。解决办法有两种一是在main.py的开头加上import sys import io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)二是在运行前敲chcp 65001把命令行编码切到UTF-8。我后来直接把第一种方案写到了代码里一劳永逸。这个坑几乎每个做中文控制台程序的Python开发者都会踩提前设置好能省很多事。4.2 SQLite 数据库被锁的问题系统用了一段时间后朋友反馈偶尔会报错 “database is locked”。排查后发现原因有两个一是代码里某个分支没有正确关闭数据库连接连接一直被占用二是SQLite在Windows上默认锁粒度比较大多个连接同时写入时会冲突。解决方法是我把数据库读写操作统一封装每个函数都严格遵循“获取连接→操作→关闭连接”的模式并且写操作统一放到事务里尽量减少持有连接的时间。另外SQLite连接对象可以设置timeout参数conn sqlite3.connect(data/hx2767.db, timeout10)这样连接在等待锁的时候最多等10秒超时才报错而不是直接失败。4.3 PyInstaller 打包成 exe 的坑项目最后要打包给店员使用这就涉及到Python转exe文件。PyInstaller打包本身非常省事但有三个坑必须提前处理第一资源文件的路径问题。数据库文件路径如果写成相对路径data/hx2767.db打包后的exe运行时工作目录可能不在exe所在位置会导致找不到数据库。我最终的解决办法是写一个函数动态获取exe所在目录然后基于这个目录拼接所有路径import sys import os def get_base_path(): if getattr(sys, frozen, False): # 打包成exe后使用exe所在目录 return os.path.dirname(sys.executable) else: # 源码运行时使用项目根目录 return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DB_PATH os.path.join(get_base_path(), data, hx2767.db)第二打包时隐藏控制台窗口。如果后续想做成双击打开图形界面的程序PyInstaller打包时可以加--noconsole参数。但注意加了之后print输出就看不到了所以我建议先用带控制台窗口的版本确认稳定后再切到无控制台模式。第三PyInstaller的版本问题。请务必在虚拟环境里打包并且用pyinstaller --clean main.py清除缓存。我遇到过因为全局环境库太乱导致打包后exe体积巨大、启动超慢的问题虚拟环境下打包出来的exe干净很多。打包命令参考pyinstaller -F -w main.py --name sport_shop4.4 通用排查思路速查表我在调试这套系统的过程中整理了一份速查表希望能帮你节省排查时间现象可能原因排查方法控制台中文乱码编码不一致统一UTF-8输出chcp 65001database is locked连接未关闭/并发写检查连接释放设置timeout库存变成负数扣减逻辑缺少校验在update前先查当前库存乐观锁控制exe找不到数据库相对路径问题用sys.executable动态获取路径报表日期不对时区问题SQLite用datetime(now,localtime)订单总金额不对浮点运算精度金额用round保留2位或用Decimal我个人最深的体会是排查问题的思路比问题本身更重要。遇到bug别急着改代码先顺着“输入→处理→输出”这条链路把每一步的数据打印出来看一眼绝大多数问题都能在五分钟内定位。这套系统开发过程中最难的不是某个算法而是对业务规则的理解和对数据一致性的把控。商店管理看似简单但“库存够不够”“金额对不对”“报表准不准”这三件事每一件都是马虎不得的。最后再分享一个小技巧。这套系统上线后我并没有停止迭代而是给main.py加了一个“功能热重载”的入口——每次启动时自动扫描services目录下的所有.py文件如果有更新就直接加载最新的代码。这样后续改动业务逻辑店员那边只需要重启一次程序就能生效省掉了反复打包exe的麻烦。如果你想把这个项目继续扩展方向还有不少用Flask封装成内网可访问的Web版、用Excel批量导入商品数据、接入小票打印机、甚至用opencv做个简单的商品条码扫描。Python能做的事比你想的要多得多。
返回列表