1. 项目概述
这个超小型商品进销存系统的开发源于我在一家社区便利店兼职时的实际需求。老板需要一套简单易用的系统来管理日常的商品进货、销售和库存,但市面上的商业软件要么功能过于复杂,要么价格超出预算。于是我用Python+MySQL开发了这个轻量级解决方案,整个系统核心功能代码不到1000行,却能满足小型零售店铺90%的日常管理需求。
系统采用经典的三层架构:前端使用Tkinter构建简易GUI,业务逻辑层处理核心进销存算法,数据层采用MySQL存储。特别针对没有IT背景的店铺使用者做了大量交互优化,比如扫码枪直接输入商品条码、自动计算库存警戒值等实用功能。开发过程中使用了DataGrip作为数据库管理工具,其直观的ER图功能对设计表结构帮助很大。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心需求解析
2.1 基础功能矩阵
这个微型系统需要实现以下核心功能模块:
- 商品管理:基础信息维护(名称、条码、分类、进价、售价)
- 进货管理:采购单录入、供应商管理、进货历史查询
- 销售管理:收银台功能、销售流水记录、退货处理
- 库存管理:实时库存查询、库存预警、盘点功能
- 统计报表:日/月销售报表、毛利分析、热销商品排行
2.2 技术选型考量
选择Python+MySQL组合主要基于以下实际因素:
- 开发效率:Python丰富的库能快速实现业务逻辑,比如用Pandas处理报表数据
- 部署成本:MySQL社区版完全免费,且对小型系统性能足够
- 学习曲线:店铺员工后续维护时,Python比Java/C++更易上手
- 扩展性:未来可方便地接入微信小程序等扩展前端
提示:小型系统要特别注意避免过度设计,我的经验法则是——初期只实现必需功能,其他需求等实际使用中确实需要时再迭代。
3. 数据库设计实战
3.1 表结构设计
使用DataGrip设计的核心表结构如下(简化版):
sql复制CREATE TABLE `product` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`barcode` VARCHAR(20) UNIQUE,
`name` VARCHAR(50) NOT NULL,
`category` VARCHAR(20),
`purchase_price` DECIMAL(10,2),
`retail_price` DECIMAL(10,2),
`stock` INT DEFAULT 0,
`alert_stock` INT DEFAULT 5
);
CREATE TABLE `supplier` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`contact` VARCHAR(20)
);
CREATE TABLE `purchase` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`supplier_id` INT,
`total_amount` DECIMAL(10,2),
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`supplier_id`) REFERENCES `supplier`(`id`)
);
CREATE TABLE `purchase_detail` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`purchase_id` INT,
`product_id` INT,
`quantity` INT,
`unit_price` DECIMAL(10,2),
FOREIGN KEY (`purchase_id`) REFERENCES `purchase`(`id`),
FOREIGN KEY (`product_id`) REFERENCES `product`(`id`)
);
3.2 关键设计技巧
- 条码索引优化:为barcode字段添加唯一索引,加速销售时的商品查询
- 价格精度:使用DECIMAL(10,2)确保金额计算精确到分
- 库存更新:采用触发器自动更新product表的stock字段
- 软删除:重要表添加is_deleted标记而非物理删除
sql复制-- 库存更新触发器示例
DELIMITER //
CREATE TRIGGER update_stock_after_purchase
AFTER INSERT ON purchase_detail
FOR EACH ROW
BEGIN
UPDATE product SET stock = stock + NEW.quantity
WHERE id = NEW.product_id;
END//
DELIMITER ;
4. Python核心实现
4.1 数据库连接池
使用PyMySQL实现连接管理:
python复制import pymysql
from dbutils.pooled_db import PooledDB
class DBUtil:
_pool = None
@classmethod
def get_connection(cls):
if not cls._pool:
cls._pool = PooledDB(
creator=pymysql,
host='localhost',
user='store',
password='Store@123',
database='inventory',
maxconnections=5
)
return cls._pool.connection()
4.2 销售业务逻辑
典型销售流程的实现:
python复制def make_sale(items):
conn = DBUtil.get_connection()
try:
with conn.cursor() as cursor:
# 1. 创建销售主单
cursor.execute("INSERT INTO sale(total_amount) VALUES(0)")
sale_id = cursor.lastrowid
total = 0
# 2. 处理每个商品
for item in items:
# 检查库存
cursor.execute("SELECT stock,retail_price FROM product WHERE id=%s", (item['pid']))
product = cursor.fetchone()
if product['stock'] < item['qty']:
raise Exception(f"商品{item['pid']}库存不足")
# 计算金额
amount = product['retail_price'] * item['qty']
# 添加明细
cursor.execute("""
INSERT INTO sale_detail(sale_id,product_id,quantity,unit_price,amount)
VALUES(%s,%s,%s,%s,%s)
""", (sale_id, item['pid'], item['qty'], product['retail_price'], amount))
# 扣减库存
cursor.execute("UPDATE product SET stock=stock-%s WHERE id=%s",
(item['qty'], item['pid']))
total += amount
# 3. 更新总金额
cursor.execute("UPDATE sale SET total_amount=%s WHERE id=%s", (total, sale_id))
conn.commit()
return sale_id
except Exception as e:
conn.rollback()
raise e
finally:
conn.close()
4.3 Tkinter界面优化技巧
- 扫码枪支持:绑定键盘事件自动聚焦到输入框
python复制entry_barcode.bind("<Key>", lambda e: process_barcode() if e.keysym=='Return' else None)
- 快捷键设计:F1收银、F2查询、F3退货等
- 数据验证:限制价格输入必须为数字
python复制vcmd = (root.register(validate_price), '%P')
entry_price.config(validate="key", validatecommand=vcmd)
5. 部署与运维实战
5.1 最小化部署方案
对于没有IT支持的店铺,我采用以下简易部署方式:
- MySQL部署:
- 使用MySQL 5.7社区版(更稳定)
- 配置my.ini优化内存使用:
code复制[mysqld] innodb_buffer_pool_size = 32M key_buffer_size = 8M - Python环境:
- PyInstaller打包成单文件exe
- 自动检测并安装缺失依赖
5.2 数据备份策略
编写自动备份脚本backup.py:
python复制import subprocess
import datetime
def backup():
today = datetime.datetime.now().strftime("%Y%m%d")
cmd = f"mysqldump -u store -pStore@123 inventory > backup_{today}.sql"
subprocess.run(cmd, shell=True)
# 保留最近7天备份
# ...
通过Windows任务计划每天凌晨2点执行。
6. 典型问题排查
6.1 并发修改问题
当多人同时操作时可能出现库存不一致,解决方案:
- 乐观锁:在更新库存时检查版本号
sql复制UPDATE product SET stock=stock-1, version=version+1
WHERE id=100 AND version=5
- 事务隔离:设置事务隔离级别为REPEATABLE READ
6.2 性能优化记录
当销售记录超过1万条时遇到的性能问题及解决:
- 添加索引:为sale表的create_time字段添加索引
- 分页查询:改造报表查询使用LIMIT分页
- 定期归档:将3个月前的销售数据移到历史表
7. 扩展与演进
经过半年实际使用后,根据用户反馈增加了以下实用功能:
- 移动端查询:用Flask开发简易API供手机查询库存
- Excel导入导出:使用openpyxl实现批量商品录入
- 会员积分:扩展会员表和积分规则
这个项目给我的最大启示是:小型系统应该保持"够用就好"的设计哲学。最初版本只用了2周开发时间,但解决了店铺80%的痛点。后续再根据实际需求逐步迭代,比一开始就追求大而全要高效得多。
