推荐用 orders——简洁通用、ORM友好;order_master冗余且与order_items不对称;status用TINYINT或ENUM;created_at/updated_at必存;customer_id可NULL;order_items需拆分规格;menus与menu_categories应分表管理;库存扣减须SELECT...FOR UPDATE行锁。

订单主表用 orders 还是 order_master?别用 order
MySQL 里 order 是保留字(ORDER BY),直接建表会报错 ERROR 1064 (42000)。实际项目中见过三次因这命名导致初始化失败。推荐用 orders——简洁、通用、ORM 友好;order_master 虽语义清晰,但冗余,且和关联表 order_items 不对称。
字段设计要预留扩展:
- status 用 TINYINT 或 ENUM('pending','confirmed','canceled','completed'),别用字符串,避免拼写不一致
- created_at 和 updated_at 必须有,餐饮场景常需查“10 分钟内未确认的订单”
- 外键 customer_id 建议允许 NULL(堂食匿名下单常见)
order_items 表必须拆分菜品与规格,不能只存 menu_id
用户点的是“宫保鸡丁(微辣,加花生)”,不是抽象的“宫保鸡丁”。如果 order_items 只记 menu_id,结账后无法还原当时选择——辣度、配菜、是否去葱等都丢了。
正确结构包含:
- menu_id(指向菜品主表)
- specification 字段存 JSON,如 {"spicy":"mild","add":"peanuts"};或更规范地建独立 item_specifications 表
- notes 字段留作自由备注(“不要香菜”“打包”)
- 单价 unit_price 必须快照保存,否则菜单调价后历史订单金额对不上
菜品数据用 menus + menu_categories 两级,别硬塞进一个表
餐厅菜单常按“凉菜/热菜/酒水/套餐”分类,且分类可能动态增减(比如夏天加“冰饮”类)。若把分类名直接存在 menus.category_name 字段,会导致:
- 修改分类名要全表 UPDATE
- 无法设置分类排序(“酒水”总得排最后)
- 不能给分类设启用/禁用状态(下架某类但保留菜品)
推荐做法:
- menu_categories 表含 id、name、sort_order、is_active
- menus 表用 category_id 关联,加 is_available 控制单个菜品上下架
- 查询某分类下可用菜品时,两表 JOIN + WHERE category.is_active = 1 AND menu.is_available = 1
高并发下单时,SELECT ... FOR UPDATE 锁哪一行?别锁整张表
库存扣减场景(如“麻婆豆腐只剩 2 份”),多个服务员同时点单,必须防超卖。错误做法:SELECT stock FROM menus WHERE id = 123 后再 UPDATE——中间有竞态窗口。
Shop7z网上购物系统支持电脑版+手机版+支付宝及微信支付,支持QQ和微信一键登陆,系统集众家之所长,大气超美观页面+手机版+商品组合套餐+限时抢购秒杀+图片批量上传+淘宝数据包导入+弹出式分类菜单+不同规格不同价格+新订单邮件通知+销售报表打印与Excel输出+物流跟踪打印查询+会员积分及优惠券+邮件群发+图片在线管理+销售统计报表+五种价格体系+礼品礼券+微信公众号支付+扫码支付等等等。
正确姿势:
- 在事务中执行 SELECT stock FROM menus WHERE id = 123 FOR UPDATE,锁住该行
- 立即判断 stock >= required_qty,不满足则 ROLLBACK
- 满足则 UPDATE menus SET stock = stock - ? WHERE id = 123
- 注意:FOR UPDATE 只在事务内有效,且索引必须覆盖 WHERE 条件(id 主键天然支持)
- 切勿在 orders 表上加锁——那会阻塞所有新订单,而库存锁只影响同一菜品
START TRANSACTION; SELECT stock FROM menus WHERE id = 123 FOR UPDATE; -- 应用层判断库存是否充足 UPDATE menus SET stock = stock - 1 WHERE id = 123; INSERT INTO order_items (order_id, menu_id, quantity, unit_price) VALUES (456, 123, 1, 28.00); COMMIT;
最易被忽略的一点:菜品库存字段 stock 必须是 INT 且带符号(支持临时负数调试),但业务逻辑里绝不允许负库存出库——扣减前校验比锁更前置、更轻量。









