fix(api): decrement SKU stock only when remaining quantity is sufficient

Prevent oversell by conditioning stock updates and locking SKUs in primary-key order, and record the same concurrent-counter rule in the API spec and agent guide.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Chengdong Zhang
2026-09-18 17:41:06 +08:00
co-authored by Cursor
parent 53548196f8
commit 835afbe5e0
4 changed files with 32 additions and 6 deletions
+11 -5
View File
@@ -184,11 +184,16 @@ pub async fn insert_item(
}
pub async fn decrement_stock(tx: &mut PgConnection, sku_id: Uuid, qty: i32) -> ApiResult<()> {
sqlx::query("UPDATE skus SET stock = stock - $2 WHERE id = $1")
.bind(sku_id)
.bind(qty)
.execute(&mut *tx)
.await?;
let result = sqlx::query(
"UPDATE skus SET stock = stock - $2 WHERE id = $1 AND stock >= $2",
)
.bind(sku_id)
.bind(qty)
.execute(&mut *tx)
.await?;
if result.rows_affected() == 0 {
return Err(ApiError::Conflict("insufficient stock".into()));
}
Ok(())
}
@@ -329,6 +334,7 @@ pub async fn lock_purchasable_skus(
JOIN shops sh ON sh.id = p.shop_id
WHERE s.id = ANY($1) AND s.active = TRUE
AND p.status = 'published' AND sh.status = 'active'
ORDER BY s.id
FOR UPDATE OF s",
)
.bind(sku_ids)