mysql如何用mysql实现订单发货流程_mysql订单处理系统

7次阅读

订单状态须用tinyintenum类型存储,发货操作需事务+行级锁,库存扣减须原子化,发货记录应独立建表shipsments以支持多次发货与物流追踪。

mysql如何用mysql实现订单发货流程_mysql订单处理系统

订单状态字段必须用 ENUMTINYINT,别用 VARCHAR

字符串存状态(比如 'pending''shipped')看着直观,但查起来慢、改起来难、还容易拼错。mysql 里最稳妥的是 TINYINT:0=待支付,1=已支付,2=已发货,3=已完成,-1=已取消。或者用 ENUM('pending','paid','shipped','done','canceled')——它底层也转成整数,还能防非法值。

如果已有表用了 VARCHAR,别直接 ALTER table ... MODIFY status VARCHAR(20) 然后手动 UPDATE,得先加约束再批量更新:

ALTER TABLE orders ADD COLUMN status_new TINYINT DEFAULT 0; UPDATE orders SET status_new = CASE status WHEN 'pending' THEN 0 WHEN 'paid' THEN 1 WHEN 'shipped' THEN 2 ELSE 0 END; ALTER TABLE orders DROP COLUMN status, CHANGE status_new status TINYINT NOT NULL;

发货操作必须用事务 + 行级锁,不能只靠应用层判断

常见错误是先 select status FROM orders WHERE id = 123,发现是 paid 就执行 UPDATE ... SET status = 2。这在并发下会超发:两个发货员同时查到同一单是 paid,都去更新,结果发了两次货。

正确做法是在一条语句里完成校验和更新,并加 for UPDATE 锁住这行:

BEGIN; SELECT * FROM orders WHERE id = 123 AND status = 1 FOR UPDATE; -- 如果查不到,说明状态不对,直接 ROLLBACK UPDATE orders SET status = 2, shipped_at = NOW(), tracking_no = 'SF123456789' WHERE id = 123 AND status = 1; -- 检查 ROW_COUNT() 是否为 1,不是就说明被别人抢先改了 COMMIT;

发货时要同步更新库存,且库存扣减必须原子化

订单发货 ≠ 单纯改订单状态。真实场景中,发货前得确认对应商品的库存是否足够(尤其是预售或多仓场景),发货后要扣减可售库存。别在应用里先查库存再扣减——中间可能被其他订单抢走。

推荐在发货事务里一起处理,用子查询或 JOIN 确保一致性:

UPDATE products p JOIN order_items oi ON p.id = oi.product_id SET p.stock = p.stock - oi.quantity WHERE oi.order_id = 123 AND p.stock >= oi.quantity;

如果这条语句影响行为 0,说明某商品库存不足,整个发货事务该回滚。

  • 库存字段建议用 INT(无符号),避免负数
  • 高并发时考虑把库存拆到缓存(如 redis)做预占,MySQL 只做最终落库
  • 不要用 UPDATE ... SET stock = stock - 1 而不校验,这是典型的“超卖”温床

发货记录要独立建表,别orders 里加一堆 shipped_atcarriertracking_no

一张订单可能多次发货(比如分批发货、补发、换货),如果所有字段都塞进 orders 表,历史轨迹就没了,查询也变重。应该拆出 shipments 表:

CREATE TABLE shipments (   id BIGINT PRIMARY KEY AUTO_INCREMENT,   order_id BIGINT NOT NULL,   status TINYINT DEFAULT 1, -- 1=created, 2=packed, 3=shipped, 4=delivered   shipped_at DATETIME NULL,   carrier VARCHAR(32),   tracking_no VARCHAR(64),   created_at DATETIME DEFAULT CURRENT_TIMESTAMP,   INDEX idx_order_id (order_id),   FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE );

这样每次发货写一行,订单状态字段只反映「当前整体进展」,而 shipments 表承载完整操作日志。导出物流数据、对接快递平台、做发货时效分析,都靠这张表撑着。

真正麻烦的是状态流转逻辑——比如「已发货」不等于「物流已揽收」,系统得能区分业务状态和物流节点。这个边界一旦模糊,售后查件就会卡在「到底发没发」的扯皮里。

text=ZqhQzanResources