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

来源:这里教程网 时间:2026-02-28 20:52:08 作者:

订单状态字段必须用
ENUM
TINYINT
,别用
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_at
carrier
tracking_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
表承载完整操作日志。导出物流数据、对接快递平台、做发货时效分析,都靠这张表撑着。

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

相关推荐