
简介这份资源面向餐饮企业信息化建设者、后端开发与数据库设计初学者提供食品采购系统原材料清单的建库参考。内容围绕食材名称、分类、规格与计量单位展开涵盖蔬菜、菌菇、豆类等常见品类并延伸至数据标准化、库存跟踪、供应商管理、采购计划、质量控制、报表分析、接口集成、安全备份与可扩展性等设计要点帮助读者理解如何把零散食材信息整理成结构清晰、便于维护的数据库模型。资源包共1个doc文件约864KB以文档形式集中呈现原材料清单与字段组织方式适合直接对照建表或作为数据字典底稿。目前已有101人学习下载可作为餐饮采购系统数据库搭建、库存管理模块开发与食材主数据梳理的实用参考资料。1. 餐饮食品采购系统数据库建设从一张原材料清单.doc 说起很多餐饮老板或后厨管理者第一次找我聊数字化手里都攥着一份《餐饮食品采购系统数据库建设原材料清单.doc》。这份文档通常长这样几十行 Excel 风格的表格列着“土豆、5 斤、根茎类、供应商老张、周一送”再往下是“冻品虾仁、20 斤、冷冻、李姐、周三送”。它看着像一张普通采购单但真正要把它变成一套能跑起来的采购系统数据库核心难点不在写 SQL而在把这份“人话清单”翻译成机器能稳定查询、能自动补货、能对账的结构化数据。这篇文章面向的是正打算把餐饮采购从微信接龙、纸质单子搬到数据库里的开发者或门店 IT 负责人我会按“先立数据模型、再建表、再灌数据、最后避坑”的顺序把这份原材料清单.doc 拆成可复现的建库路径。你不需要先成为 DBA但需要愿意动手改字段、调参数。2. 原材料清单.doc 里的字段怎么映射成数据库表2.1 先别急着建表把清单里的“人话”拆成实体打开那份原材料清单.doc你看到的每一行其实混了四类信息食材本身土豆、虾仁、规格单位5 斤、20 斤、供应商老张、李姐、配送周期周一送、周三送。如果直接建一张大宽表后面改一个供应商电话就要全表更新查询“本周所有冻品供应商”也会变得很别扭。常见做法是拆成四张核心表食材表ingredient、供应商表supplier、采购订单表purchase_order、订单明细表order_item。食材表存名称、分类、默认单位、存储条件供应商表存名称、联系方式、结算周期采购订单表存下单日期、供应商、总金额、状态订单明细表存订单 ID、食材 ID、数量、单价。这样拆的好处是当老张不再送土豆时你只需要改食材表里的默认供应商字段历史订单不受影响。2.2 字段类型和约束别让“5 斤”变成字符串很多新手会把数量字段设成 VARCHAR因为清单里写的是“5 斤”。但一旦你要算“本周土豆总采购量”字符串就没法 SUM。正确做法是数量用 DECIMAL(10,2)单位单独用 ENUM 或字典表存“斤/公斤/箱/袋”。下面这段 MySQL 建表语句是我在门店项目里常用的最小可用版本你可以直接抄-- 食材表存基础信息不存库存 CREATE TABLE ingredient ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL COMMENT 食材名称如土豆, category VARCHAR(32) NOT NULL COMMENT 根茎类/叶菜类/冻品, default_unit VARCHAR(8) NOT NULL DEFAULT 斤 COMMENT 默认采购单位, storage_type TINYINT NOT NULL DEFAULT 1 COMMENT 1常温 2冷藏 3冷冻, default_supplier_id INT COMMENT 默认供应商ID, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_name (name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 供应商表结算周期用天数存别存“月结30天”这种文本 CREATE TABLE supplier ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL, phone VARCHAR(20), settle_days INT NOT NULL DEFAULT 0 COMMENT 结算天数0为现结, status TINYINT DEFAULT 1 COMMENT 1合作中 0停用 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明ingredient 表的 name 加了唯一索引防止“土豆”和“马铃薯”被当成两种食材重复录入storage_type 用数字而不是文本是为了后面做“冷冻食材单独生成采购单”时查询更快。参数上DECIMAL(10,2) 能存到小数点后两位足够应对“1.5 斤”这种场景settle_days 存整数对账时直接用 DATE_ADD 算到期日比解析文本可靠得多。2.3 采购订单和明细为什么必须分两张表如果只建一张 purchase 表字段会是“订单号、食材、数量、单价、供应商”那一个订单买五样菜就要插五行订单号重复改一个供应商电话要改五行。拆成 purchase_order 和 order_item 后订单头存一次供应商和日期明细存多行食材。查询“老张本周送了多少钱”只需要 JOIN 两张表。这里有个参数要注意order_item 里的 unit_price 要存下单时的价格不要关联食材表的当前价否则历史订单金额会变。我一般还会加一个 snapshot_unit 字段把当时的单位也存下来防止食材表后来把“斤”改成“公斤”导致对账混乱。3. 把清单.doc 灌进数据库解析、清洗、入库三步走3.1 用 Python 读 .doc 并转成结构化行.doc 是老二进制格式直接 open 会乱码。常见做法是先用 LibreOffice 命令行转成 .docx 或 .csv再用 python-docx 或 pandas 读。下面这段脚本假设你已经把清单另存为 CSV列名为“食材名称、数量、单位、分类、供应商、配送日”import pandas as pd import pymysql # 读 CSV注意编码餐饮清单常从 WPS 导出gbk 居多 df pd.read_csv(原材料清单.csv, encodinggbk) # 去掉全空行和表头重复行 df df.dropna(howall) df df[df[食材名称] ! 食材名称] # 清洗数量把“5斤”“5 斤”“约5斤”统一成 5.0 def clean_qty(val): import re if pd.isna(val): return 0.0 m re.search(r(\d(\.\d)?), str(val)) return float(m.group(1)) if m else 0.0 df[数量] df[数量].apply(clean_qty) # 单位统一斤/公斤/箱/袋其他归为“斤” df[单位] df[单位].apply(lambda x: x if x in [斤,公斤,箱,袋] else 斤) print(df.head())逻辑说明clean_qty 用正则抓第一个数字能处理“约5斤”“5斤左右”这类写法单位归一化是为了后面入库时不会因为“KG”和“公斤”并存导致统计错误。参数上encodinggbk 是针对国内 WPS 导出的常见编码如果你用 UTF-8 打开报错就换回来。3.2 入库前去重同名食材只留一条清单里经常出现“土豆”写两遍供应商不同。入库时不能直接 INSERT否则 ingredient 表的唯一索引会报错。我一般用 INSERT ... ON DUPLICATE KEY UPDATE 来兜底conn pymysql.connect(hostlocalhost, userroot, passwordyourpass, databasecatering, charsetutf8mb4) cursor conn.cursor() for _, row in df.iterrows(): # 先插食材忽略重复 cursor.execute( INSERT INTO ingredient (name, category, default_unit, storage_type) VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE categoryVALUES(category) , (row[食材名称], row[分类], row[单位], 1)) # 再查供应商ID没有就插 cursor.execute(SELECT id FROM supplier WHERE name%s, (row[供应商],)) sup cursor.fetchone() if not sup: cursor.execute(INSERT INTO supplier (name) VALUES (%s), (row[供应商],)) sup_id cursor.lastrowid else: sup_id sup[0] # 这里省略订单插入实际按配送日分组生成订单 conn.commit()逻辑说明ON DUPLICATE KEY UPDATE 只更新分类不更新单位因为单位一旦被历史订单引用就不该变。供应商查询用 SELECT 再 INSERT虽然效率不高但清单通常只有几十行够用。参数上charsetutf8mb4 必须加否则食材名里的生僻字会变问号。3.3 验证数据三条 SQL 查有没有灌歪入库后别急着写业务代码先跑三条查询验证。第一查食材总数和分类分布SELECT category, COUNT(*) FROM ingredient GROUP BY category;如果“根茎类”只有一条说明分类字段没读对。第二查有没有数量为 0 的明细SELECT * FROM order_item WHERE qty 0;有的话回去看 clean_qty 是不是没匹配到数字。第三查供应商重复SELECT name, COUNT(*) FROM supplier GROUP BY name HAVING COUNT(*) 1;正常应该为空。这三条能挡住八成低级错误。4. 采购系统数据库避坑五个让我半夜爬起来改表的教训4.1 现象月底对账金额对不上差几毛钱原因unit_price 用了 FLOAT 而不是 DECIMAL累加时出现浮点误差。解决所有金额字段改 DECIMAL(10,2)Python 里用 Decimal 类型不要用 float 算钱。4.2 现象查询“本周冻品采购量”特别慢三秒才出结果原因storage_type 和 created_at 没建索引全表扫描。解决ALTER TABLE ingredient ADD INDEX idx_storage (storage_type);订单表按 created_at 建索引。注意索引不要乱加写多读少的表加索引会拖慢插入。4.3 现象供应商改名后历史订单里的供应商名也跟着变了原因order 表里存了 supplier_name 文本而不是 supplier_id。解决订单表只存 supplier_id显示时 JOIN supplier 表。如果业务要求保留历史名称加一个 supplier_name_snapshot 字段下单时写入之后不更新。4.4 现象清单里的“冻品虾仁”和“虾仁”被当成两种食材原因没有做名称归一化。解决入库前用映射表把“冻品虾仁”统一成“虾仁”storage_type 设为 3。我一般会维护一个 alias 表记录“冻品虾仁→虾仁”“土豆→马铃薯”这类同义词。4.5 现象配送日“周一送”存成字符串没法自动生成下周订单原因配送周期没有结构化。解决在 supplier 表加 delivery_weekday 字段存 1-7 的数字1 代表周一。生成订单时用 Python 的 date.weekday() 匹配自动算出下周日期。5. 进阶用视图和定时任务把清单变成自动补货建议5.1 建一个“安全库存”视图低于阈值就提醒原材料清单.doc 只告诉你买什么不告诉你什么时候该买。我一般会加一张 stock 表记录当前库存再建一个视图算缺口CREATE VIEW v_reorder AS SELECT i.name, i.default_unit, s.qty AS current_qty, i.safe_stock, (i.safe_stock - s.qty) AS gap FROM ingredient i JOIN stock s ON s.ingredient_id i.id WHERE s.qty i.safe_stock;逻辑说明safe_stock 字段需要你在 ingredient 表里补上默认给 0。这个视图查出来的就是“需要补货的食材”。参数上gap 为负数表示库存充足正数表示缺口。你可以每天早八点用 cron 跑一次把结果发到门店群。5.2 用事件调度器自动生成采购草稿MySQL 从 5.1 开始支持 EVENT但很多云数据库默认关闭。我一般用 Python 的 APScheduler 更可控from apscheduler.schedulers.blocking import BlockingScheduler import pymysql def gen_draft(): conn pymysql.connect(hostlocalhost, userroot, passwordyourpass, databasecatering) cursor conn.cursor() cursor.execute(SELECT * FROM v_reorder) for row in cursor.fetchall(): cursor.execute( INSERT INTO purchase_order (supplier_id, order_date, status) VALUES ((SELECT default_supplier_id FROM ingredient WHERE name%s), CURDATE(), draft) , (row[0],)) conn.commit() sched BlockingScheduler() sched.add_job(gen_draft, cron, hour8, minute0) sched.start()逻辑说明这里只生成草稿订单不直接发给供应商避免自动下单买错。参数上hour8 是门店上班时间你可以改成 6 点让店长一开门就看到。注意 default_supplier_id 可能为空实际项目里要加 if 判断。5.3 一个我坚持了三年的习惯每次改完表结构我一定先在一个测试库跑一遍全量清单导入再用SELECT COUNT(*)对比源文件行数。差一行都不行。这个习惯帮我挡过至少两次“字段截断导致食材名变空”的事故。数据库建设没有后悔药原材料清单.doc 可以改十版但线上表结构改一次就要想清楚三个月后会不会翻车。希望帮到你。本文还有配套的精品资源点击获取