今天我们来拆解一个真实的订单处理流程。它分好几个步骤:数据校验、订单状态更新,还有日志记录。想象一下在餐厅,厨师、服务员和收银员得配合好。我们这个流程其实就是把这些环节的逻辑串起来。
流程需求描述
订单处理流程要做这些事:
- 检查仓库里有没有需要的商品。
- 如果库存够,就从仓库里扣掉数量。
- 把订单状态改成“已处理”。
- 把成功的信息写进日志表。
- 如果出错了,所有操作都要回滚。
流程实现
第1步:建表和准备数据结构
写流程前,先建好要用的表。
orders 表 — 订单
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_name TEXT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL CHECK (quantity > 0),
status TEXT DEFAULT 'Pending'
);
这个表存订单。每个订单有客户、产品ID、数量和状态(默认“待处理”)。
inventory 表 — 仓库
CREATE TABLE inventory (
product_id SERIAL PRIMARY KEY,
product_name TEXT NOT NULL UNIQUE,
stock INT NOT NULL CHECK (stock >= 0)
);
仓库商品表。每个产品有当前库存(stock)。
order_logs 表 — 操作日志
CREATE TABLE order_logs (
log_id SERIAL PRIMARY KEY,
order_id INT NOT NULL REFERENCES orders(order_id) ON DELETE CASCADE,
log_message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
日志表用来记录订单处理的状态信息。
第2步:流程结构
多步骤流程的结构如下:
- 查查仓库有没有要的商品,数量够不够。
- 如果库存够,就在
inventory表里减掉数量。 - 把订单状态改成“已处理”。
- 把成功结果写进
order_logs表。 - 遇到错误就回滚。
第3步:写流程
来写个process_order流程,按上面说的步骤来。
CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
DECLARE
v_product_id INT;
v_quantity INT;
v_stock INT;
BEGIN
-- 第1步:查订单信息
SELECT product_id, quantity
INTO v_product_id, v_quantity
FROM orders
WHERE order_id = $1;
-- 检查订单是否存在
IF NOT FOUND THEN
RAISE EXCEPTION '订单ID % 不存在。', $1;
END IF;
-- 第2步:查仓库库存
SELECT stock INTO v_stock
FROM inventory
WHERE product_id = v_product_id;
IF NOT FOUND THEN
RAISE EXCEPTION '产品ID % 在仓库里找不到。', v_product_id;
END IF;
IF v_stock < v_quantity THEN
RAISE EXCEPTION '产品ID % 库存不足。需要: %, 可用: %。',
v_product_id, v_quantity, v_stock;
END IF;
-- 第3步:扣减库存
UPDATE inventory
SET stock = stock - v_quantity
WHERE product_id = v_product_id;
-- 第4步:把订单状态改成“已处理”
UPDATE orders
SET status = 'Processed'
WHERE order_id = $1;
-- 第5步:写日志,记录成功
INSERT INTO order_logs (order_id, log_message)
VALUES ($1, '订单处理成功。');
EXCEPTION
WHEN OTHERS THEN
-- 任何失败都写日志
INSERT INTO order_logs (order_id, log_message)
VALUES ($1, '订单处理出错: ' || SQLERRM);
-- 回滚所有操作
RAISE;
END;
$$ LANGUAGE plpgsql;
我们来拆解下这个流程。
校验阶段:
先查
orders表里有没有这个订单。如果没有,就抛出详细异常。同理,查仓库有没有这个产品和库存够不够。仓库操作阶段:
库存够就直接
UPDATE扣减。订单状态变更阶段:
把状态改成“Processed”(已处理),表示订单搞定了。
日志记录阶段:
订单处理成功后,往
order_logs表里插入一条记录。异常处理:
如果哪里出错了,在
EXCEPTION块里捕获,写详细错误日志,然后回滚所有操作。
使用示例
我们来造点测试数据,看看流程效果。
-- 往仓库加点商品
INSERT INTO inventory (product_name, stock)
VALUES ('Laptop', 10), ('Monitor', 5);
-- 加点订单
INSERT INTO orders (customer_name, product_id, quantity)
VALUES
('Alice', 1, 2),
('Bob', 2, 1),
('Charlie', 1, 20); -- 这个订单应该会报错
现在来测试下流程:
-- 处理Alice的订单
SELECT process_order(1);
-- 处理Bob的订单
SELECT process_order(2);
-- 试试处理Charlie的订单(会报错)
SELECT process_order(3);
结果:
- Alice和Bob的订单会处理成功,写进日志,仓库库存也会减少。
- Charlie的订单因为库存不够会报错,日志里会有错误记录。
处理完后查下表:
SELECT * FROM inventory; -- 看库存变化
SELECT * FROM orders; -- 看订单状态
SELECT * FROM order_logs; -- 看日志记录
常见错误和建议
错误:
SELECT INTO后忘了检查NOT FOUND。一定要处理查不到数据的情况,不然会有奇怪的异常。
错误:没写
EXCEPTION块。流程里没写异常处理的话,出错时事务可能卡住或者流程乱套。
建议:防SQL注入。
用强类型参数,没必要别用动态SQL。
流程扩展
实际项目里可以加更多校验,比如:
- 考虑客户的折扣或促销。
- 处理订单前查下客户信用额度。
- 不仅记录成功,还要记录回滚操作。
GO TO FULL VERSION