资讯动态

用Python与SQLite构建商品库存系统:AI辅助开发实践指南

发布时间:2026/9/4 21:45:46 来源:尧图企业网站定制
用 WorkBuddy 这类 AI 工作台配合 Python 去搭一套商品库存管理系统值不值得花时间我的判断是值得而且很适合个人项目、小门店、学习练手或者作为正式系统的最初原型。它的价值不是让你真的一句话就拿到生产级系统而是把“建表、写增删改查、调通入库出库”这些重复步骤压到很低的成本。真正决定系统能不能用的不是生成速度而是库存建模、事务边界和异常数据兜底。这篇文章适合谁看想学 Python 但不想只写练习题的人、想给自己店里做个简单工具的人、以及已经在用 AI 辅助编码但总感觉生成的代码不够“可信”的人。先说结论一句话可以启动项目但不能替你完成系统设计。1. 先别急着写代码先梳理库存系统到底要管什么1.1 最小可用的商品库存系统其实只需要三件事商品库存管理系统的核心不是“界面做得多漂亮”而是让库存数据不要变成一团乱账。很多人第一次上手就想着做多仓库、做盘点、做报表、做权限结果代码没跑通需求已经膨胀了。一个最小可用的系统只需要覆盖三件事商品档案维护 SKU、商品名称、分类、单位、最低库存预警线。库存变动支持入库、出库并且每一次变动都留下记录。当前库存查询随时能回答“某个 SKU 现在还有多少”。这套口径定义了系统边界。后面的所有代码、表结构、测试用例都围绕这三件事展开。先把这三个主线跑通再考虑 Excel 导入、网页界面、多用户并发这些增强项。1.2 WorkBuddy 在一句话需求里替你做完了哪一部分很多刚接触 WorkBuddy 的人第一反应是到处找 WorkBuddy 怎么使用的教程但真正决定成果的往往不是工具本身的入口而是你给它的需求描述。如果你只丢过去一句“用 Python 做一个商品库存管理系统”WorkBuddy 或者同类 AI 工作台通常会先给出一版可运行的脚手架。里面可能会有 Python 文件、建表 SQL、简单的命令行菜单但它不会替你回答这些问题库存变化时商品表和流水表要不要一起更新出库数量大于当前库存时允许扣成负数吗入库和出库应该走同一套函数还是各自写死商品重复录入 SKU 时是报错还是跳过用户输入的是小数、负数、空字符串怎么处理这些就是业务规则。WorkBuddy 擅长把已经说清楚的需求翻译成代码但它不会在你没有交代规则的时候替你做出正确决定。它给你的代码更像是“草稿”需要你把约束条件补进去再让它迭代修改。1.3 一句话开发的正确理解方式所谓“一句话设计商品库存管理系统”更准确的理解是用一句话把项目目标定下来再靠上下文把规则补全。我会把这句目标变成这样的对话结构第一步告诉 AI 你要做什么用什么语言、什么数据库。第二步把功能清单和业务规则写清楚。第三步让 AI 先给数据表结构和核心函数不要一上来就生成完整项目。第四步把小样跑起来再让 AI 补菜单、导入导出和界面。这个流程走完后你才真正具备对生成代码的判断力。否则即使工具能生成几千行代码你也分不清哪些字段重要、哪些逻辑会造成库存脏数据。2. 搭建 Python 环境先保证本地能跑再谈高级功能2.1 Python 安装与编辑器配置开发这类小型系统不需要先从复杂的工程结构开始。常见做法是本机安装一个 Python 解释器再用 VS Code 作为编辑器。第一次配置时最容易被忽略的是“安装时没勾选将 Python 加入 PATH”。Windows 用户在安装界面记得勾选Add Python to PATH否则后面在命令行执行python很可能提示找不到命令。Linux 环境下系统通常自带 Python 3。不同发行版自带版本不一样先在终端执行python3 --version确认一下。如果提示没有安装再用系统的包管理器安装。macOS 一般建议用官方安装包也可以用 Homebrew本教程不需要特别新的版本常见稳定版都可以。写入命令时也要注意 Windows 和 Linux 的差异。Windows 下激活虚拟环境venv\Scripts\activatemacOS 和 Linux 下通常是source venv/bin/activateVS Code 里要安装 Python 扩展扩展会读取当前虚拟环境。如果切换终端后发现python对应的还是系统解释器先在 VS Code 底部状态栏或者命令面板里重新选择解释器路径指向你创建的venv目录。2.2 数据库选型为什么先用 SQLite很多初学者一听到库存系统就觉得必须上 MySQL、PostgreSQL。如果只是本机练习、单门店使用或者小团队内部工具SQLite 已经足够。它是 Python 标准库自带的模块不需要额外安装数据库服务不需要设置账号密码也不用处理端口占用。SQLite 仍然支持事务这对库存系统非常重要。入库和出库都不是简单的UPDATE product SET stock_qty stock_qty 1而是“更新当前库存”和“插入一条变动记录”两个动作。如果其中一步成功、另一步失败库存数据就会不一致。事务可以保证这两个动作要么同时成功要么同时回滚。单机环境下的 SQLite 面对普通小门店的入库、出库频率性能是够用的。等系统真正需要多人高并发访问再迁移到 PostgreSQL 也不迟。现在不要为了“看起来专业”把环境复杂度拉到最高。2.3 初始化项目目录和环境验证建议单独建一个项目目录不要把代码散落在桌面或下载目录。目录结构可以很轻inventory_system/ ├── demo_ims.py ├── shop.db ├── import_data/ └── requirements.txtshop.db是 SQLite 生成的数据库文件第一次运行程序后会自动出现。import_data目录用来放 CSV 文件。激活虚拟环境后执行一条简单的验证命令确认核心模块可用python -c import sqlite3, csv, sys; print(sys.version_info); print(env ok)如果能打印出 Python 版本和env ok说明解释器和标准库已经没问题。后续如果要用 Flask 提供网页接口再单独安装pip install flask如果是学习阶段不要一次性安装一堆依赖。每增加一个功能因为装不上而影响主流程的情况我见得太多了。注意检查环境时最容易把问题搞混的是“代码报错但环境没问题”。如果报No module named flask先看当前终端是否激活了虚拟环境再执行pip list确认包安装位置。3. 怎样把你的“一句话需求”变成 WorkBuddy 能懂的上下文3.1 一句给 AI 的需求至少包含四类信息直接说“帮我写库存系统”AI 大概率会按它见过的通用模板输出。问题在于通用模板不等于你的业务场景。给 WorkBuddy 这类工具写需求也要像开发需求评审一样把信息分成几层技术栈Python、SQLite、命令行还是 Web。功能范围先做商品管理、入库、出库、库存查询不做采购和订单。数据规则SKU 唯一出库不能把库存扣成负数每次变动写流水。输入约定操作通过菜单输入文件导入用 CSV。把它们拼成一段话效果明显好过一句话。下面是一个可以发给 WorkBuddy 的提示词模板你现在是 Python 开发工程师请帮我用 Python SQLite 实现一个命令行商品库存管理系统 Demo。 功能要求 1. 新建商品字段包括 SKU、名称、分类、单位、最低库存预警数量。 2. SKU 不能重复重复时提示错误。 3. 支持入库指定商品 SKU 和数量增加当前库存同时写入一条入库流水。 4. 支持出库指定商品 SKU 和数量如果当前库存不足则报错不扣减库存。 5. 支持查看商品列表、库存流水。 技术要求 - 使用 sqlite3 标准库。 - 入库和出库必须使用事务避免中间失败造成库存不一致。 - 代码保持简单先不要写 Web 框架。这个提示词和“一句话帮我写库存系统”相比信息量完全不是一个等级。AI 生成的代码会更贴合你的规则你后续检查和改动的成本也更低。3.2 WorkBuddy 生成代码之后先查四点AI 生成的初稿第一步不是放进编辑器而是先做静态检查。我一般重点看四个地方是否真的用with conn:包住了更新和插入流水还是只用了一句UPDATE。出库前有没有查询当前库存并且判断库存是否足够。是否使用了参数化查询而不是把用户输入直接拼进 SQL。路径处理是否写成相对路径数据库文件会不会落在难以找到的位置。如果这四个地方有问题我会让 WorkBuddy 先改再进入运行。这样能省下不少调试时间。用 WorkBuddy 不等于盲信输出生成后的检查是必须的一步。3.3 用多轮对话代替一次生成更合理的用法是增量开发。第一次先让 WorkBuddy 生成本文上面的最小 Demo跑通后再分轮加功能第二轮让它加 CSV 商品导入。第三轮让它加“库存低于阈值时提醒”的查询。第四轮让它加 Web 接口但要求保留原来的业务函数不重写。每轮增加功能后都跑一次回归验证确认老功能没有被破坏。这样做有个额外好处即使 AI 后面改崩了你也能快速定位是哪个函数出了问题。4. 数据表设计库存系统能不能用关键在流水怎么建4.1 商品档案表和库存流水表库存系统最忌讳的是只有一张商品表只在商品表里增减库存没有任何历史记录。那样一旦输错一笔数后面根本无法追溯。为了不出现这种问题至少需要两张表product商品表和stock_record库存变动记录表。下面是一个适合 Demo 的建表参考CREATE TABLE IF NOT EXISTS product ( id INTEGER PRIMARY KEY AUTOINCREMENT, sku TEXT NOT NULL UNIQUE, name TEXT NOT NULL, category TEXT DEFAULT 默认分类, unit TEXT DEFAULT 件, low_stock_threshold INTEGER NOT NULL DEFAULT 0, stock_qty INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS stock_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_id INTEGER NOT NULL, change_type TEXT NOT NULL CHECK(change_type IN (in, out)), change_qty INTEGER NOT NULL DEFAULT 0, stock_after INTEGER NOT NULL, note TEXT DEFAULT , created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(product_id) REFERENCES product(id) );这张设计里product.stock_qty是冗余字段代表当前最新库存。stock_record是操作流水每一条都记录了变动类型、变动数量、变动后库存和备注。很多人问为什么要存stock_after直接用流水求和不行吗从理论上说当前库存可以由流水汇总算出来但实际业务中我们经常需要把某一次操作和当时库存对照。如果直接在流水表里记录变动后的库存查账时会直观很多。4.2 库存变动记录为什么不能覆盖和删除流水表的核心原则是只追加不删除不修改历史记录。即使入库数量录错了也应该是再做一笔出库或调整单把它冲销而不是直接删掉原来的入库记录。举个例子某商品早上入库 50 件下午发现实际上只到了 40 件。如果直接把入库记录的change_qty改成 40那么流水账和实际操作就对不上了。更好的是保留原“入库 50”记录再做一笔“出库 10”或“调整 -10”同时备注原因。这样后续人对账时能完整看到数据变化过程。每次库存发生变化时必须同时处理两件事更新product.stock_qty。在stock_record中新增一条记录。4.3 “允许商品库存为负数”是一个很危险的设计商品库存管理系统中出库操作必须检查数量。如果库存只有 20 件用户要出库 30 件程序要么阻止要么弹出二次确认。直接允许stock_qty变成负数表面上是灵活实际上会让库存彻底失真。检查库存不足的逻辑并不复杂先按 SKU 或商品 ID 查出当前库存。如果当前库存小于出库数量提示错误并中止。只有库存充足时才更新库存并写入流水。更重要的是这个判断必须和库存更新、流水插入放在同一个事务里。如果不放在一个事务里两个终端同时出库时可能都读到库存 20最后都更新成功库存就可能变成负数。SQLite 对并发写有锁定机制但代码层面仍然要主动用事务保证一致性。5. 写一个能跑的商品-入库-出库-查询闭环5.1 先搭最基础的数据库连接函数这部分代码是所有功能的地基。数据库连接函数里的row_factory sqlite3.Row能让查询结果像字典一样通过列名取值代码更可读。每次操作都打开新连接可以避免长连接导致数据库文件被持续占用。import sqlite3 from pathlib import Path DB_PATH Path(shop.db) SCHEMA CREATE TABLE IF NOT EXISTS product ( id INTEGER PRIMARY KEY AUTOINCREMENT, sku TEXT NOT NULL UNIQUE, name TEXT NOT NULL, category TEXT DEFAULT 默认分类, unit TEXT DEFAULT 件, low_stock_threshold INTEGER NOT NULL DEFAULT 0, stock_qty INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS stock_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_id INTEGER NOT NULL, change_type TEXT NOT NULL CHECK(change_type IN (in, out)), change_qty INTEGER NOT NULL DEFAULT 0, stock_after INTEGER NOT NULL, note TEXT DEFAULT , created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(product_id) REFERENCES product(id) ); def get_conn(): conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row conn.execute(PRAGMA foreign_keys ON) return conn def init_db(): with get_conn() as conn: conn.executescript(SCHEMA)注意如果数据库文件已经在旧版本里存在CREATE TABLE IF NOT EXISTS不会覆盖旧表。你想调整字段时不要直接删掉数据文件除非你确认不要里面的数据。5.2 商品新增和入库出库函数新增商品时我先让库存从 0 开始。初始库存不要直接写进商品创建函数而是通过一次入库操作加进去。这样能保证“库存变动一定有流水”这条规则不被绕过。def create_product(sku, name, category默认分类, unit件, low_stock_threshold0): if not sku or not sku.strip() or not name or not name.strip(): raise ValueError(SKU 和商品名称不能为空) with get_conn() as conn: try: conn.execute( INSERT INTO product (sku, name, category, unit, low_stock_threshold) VALUES (?, ?, ?, ?, ?) , (sku.strip(), name.strip(), category, unit, int(low_stock_threshold)), ) except sqlite3.IntegrityError: raise ValueError(fSKU {sku} 已存在) def _change_stock(conn, product_id, change_type, change_qty, note): row conn.execute( SELECT stock_qty FROM product WHERE id ?, (product_id,) ).fetchone() if row is None: raise ValueError(f商品 id{product_id} 不存在) current row[stock_qty] if change_type in: new_qty current change_qty elif change_type out: if current change_qty: raise ValueError(f库存不足当前库存 {current}需要出库 {change_qty}) new_qty current - change_qty else: raise ValueError(f不支持的变动类型: {change_type}) conn.execute( UPDATE product SET stock_qty ?, updated_at CURRENT_TIMESTAMP WHERE id ? , (new_qty, product_id), ) conn.execute( INSERT INTO stock_record (product_id, change_type, change_qty, stock_after, note) VALUES (?, ?, ?, ?, ?) , (product_id, change_type, change_qty, new_qty, note), ) return new_qty def inbound(product_id, qty, note): if qty 0: raise ValueError(入库数量必须大于 0) with get_conn() as conn: return _change_stock(conn, product_id, in, qty, note) def outbound(product_id, qty, note): if qty 0: raise ValueError(出库数量必须大于 0) with get_conn() as conn: return _change_stock(conn, product_id, out, qty, note)这里最关键的是_change_stock里UPDATE和INSERT都在同一个with get_conn() as conn:中。如果中间抛出异常连接上下文管理器会自动回滚不会出现“库存已经扣了但没有流水记录”的问题。5.3 查询函数和命令行菜单查询当前商品时不必每次都用复杂 SQL。先写一个简单版本def list_products(): with get_conn() as conn: rows conn.execute( SELECT id, sku, name, category, unit, stock_qty, low_stock_threshold FROM product ORDER BY id ).fetchall() return [dict(row) for row in rows] def list_records(product_idNone): with get_conn() as conn: if product_id is None: rows conn.execute( SELECT * FROM stock_record ORDER BY id DESC LIMIT 200 ).fetchall() else: rows conn.execute( SELECT * FROM stock_record WHERE product_id ? ORDER BY id DESC LIMIT 200, (product_id,), ).fetchall() return [dict(row) for row in rows]命令行菜单可以用最朴素的input()循环实现。先初始化数据库再打印操作编号根据输入调用上面的函数。菜单里要处理一个容易被忽略的情况用户输入的不是数字而是空字符串或字母。用try/except包住转换避免整个程序崩溃。跑通标准是新增商品 - 入库 100 - 查询库存看到 100 - 出库 30 - 查询库存看到 70 - 再出库 200 - 程序报“库存不足”库存维持 70。能连续通过这一串操作系统的主链路就算通了。6. 用 CSV 批量导入商品解决“一个个录入太慢”的真实痛点6.1 单条录入只是开始录入效率决定系统能不能真正被用起来库存系统如果每次新增商品都要在命令行里先输 SKU、再输名称、再输分类录入 200 个商品会让人崩溃。真实场景下必须支持 CSV 批量导入。CSV 格式比 Excel 更方便程序处理业务人员也可以先用 Excel 维护一个临时清单再另存为 CSV。保存时要注意编码Windows 的 Excel 另存 CSV 经常带 UTF-8 BOM。Python 读取时使用encodingutf-8-sig可以自动去掉 BOM避免第一列字段名变成\ufeffsku。6.2 示例 CSV 导入函数CSV 文件第一行是字段名后面每一行是一条商品。格式参考sku,name,category,unit,low_stock_threshold SKU001,无线鼠标,电脑外设,个,5 SKU002,机械键盘,电脑外设,把,5导入函数设置的条件是SKU 为空就跳过SKU 重复就提示并跳过不让整个程序中断。import csv def import_products_from_csv(csv_path): csv_path Path(csv_path) if not csv_path.exists(): print(f文件不存在{csv_path}) return 0 success_count 0 with csv_path.open(r, encodingutf-8-sig, newline) as f: reader csv.DictReader(f) for row in reader: sku (row.get(sku) or ).strip() name (row.get(name) or ).strip() if not sku or not name: print(跳过空 SKU 或空名称的行) continue try: create_product( skusku, namename, categoryrow.get(category, 默认分类).strip() or 默认分类, unitrow.get(unit, 件).strip() or 件, low_stock_thresholdint(row.get(low_stock_threshold) or 0), ) success_count 1 except ValueError as exc: print(f跳过 {sku}: {exc}) print(f导入完成成功 {success_count} 条) return success_count这段代码里用了or来兜底空字符串防止某个单元格是空值导致.strip()报错。6.3 导入商品后的期初库存怎么处理商品导入成功后库存默认是 0。如果这家店已经有存量商品比如仓库里已经有 100 个无线鼠标不能直接修改数据库把stock_qty改成 100而是做成一批入库单。先导入商品再批量做一次入库备注写明“期初盘点入库”。这样做的原因是期初库存也是一笔数据变动同样会影响总库存。如果直接在数据库里改stock_qty那么stock_record流水里找不到这笔数据的来源。一个月后想对账你会不知道当初的 100 是从哪里来的。7. 验证与排错怎么判断这个系统真的能长期用7.1 手工验收用例不能省代码能启动不代表逻辑正确。至少跑一遍下面的验收用例顺序操作预期结果1新增商品 SKU001创建成功2重复新增同一个 SKU报错原商品不覆盖3SKU001 入库 100当前库存 100流水出现 in 记录4SKU001 出库 30当前库存 70流水出现 out 记录5再次出库 200提示库存不足库存仍为 706

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价