Browse Source

add transaction processing

master
王菲 1 week ago
parent
commit
9ff8d02aab
4 changed files with 232 additions and 95 deletions
  1. BIN
      project1/bookstore/.coverage
  2. +11
    -25
      project1/bookstore/.idea/workspace.xml
  3. +191
    -64
      project1/bookstore/be/model/buyer.py
  4. +30
    -6
      project1/bookstore/be/model/seller.py

BIN
project1/bookstore/.coverage View File


+ 11
- 25
project1/bookstore/.idea/workspace.xml View File

@ -5,26 +5,10 @@
</component>
<component name="ChangeListManager">
<list default="true" id="1a55d07b-cd3d-49e8-ab78-b30bd356da76" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/be/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/be/app.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/be/serve.py" beforeDir="false" afterPath="$PROJECT_DIR$/be/serve.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/doc/auth.md" beforeDir="false" afterPath="$PROJECT_DIR$/doc/auth.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/doc/buyer.md" beforeDir="false" afterPath="$PROJECT_DIR$/doc/buyer.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/doc/seller.md" beforeDir="false" afterPath="$PROJECT_DIR$/doc/seller.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/conf.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/conf.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/conftest.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/conftest.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/gen_book_data.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/gen_book_data.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test.md" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_add_book.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_add_book.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_add_funds.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_add_funds.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_add_stock_level.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_add_stock_level.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_bench.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_bench.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_create_store.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_create_store.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_login.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_login.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_new_order.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_new_order.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_password.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_password.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_payment.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_payment.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/fe/test/test_register.py" beforeDir="false" afterPath="$PROJECT_DIR$/fe/test/test_register.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.coverage" beforeDir="false" afterPath="$PROJECT_DIR$/.coverage" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/be/model/buyer.py" beforeDir="false" afterPath="$PROJECT_DIR$/be/model/buyer.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/be/model/seller.py" beforeDir="false" afterPath="$PROJECT_DIR$/be/model/seller.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -37,9 +21,9 @@
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 7
}]]></component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 7
}</component>
<component name="ProjectId" id="2pX8ZovNUDmOAv85mA3joBKdQ9Z" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
@ -50,7 +34,7 @@
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"WebServerToolWindowFactoryState": "false",
"last_opened_file_path": "D:/bookstore/bookstore1",
"last_opened_file_path": "D:/ACourses/CDMS/P2/bookstore2/CDMS.Xuan_ZHOU.2024Fall.DaSE/project1/bookstore",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
@ -66,7 +50,9 @@
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1732901118349</updated>
<workItem from="1732901119489" duration="1761000" />
<workItem from="1732901119489" duration="3029000" />
<workItem from="1733360082661" duration="44000" />
<workItem from="1734359241039" duration="4744000" />
</task>
<servers />
</component>

+ 191
- 64
project1/bookstore/be/model/buyer.py View File

@ -12,28 +12,41 @@ class Buyer(db_conn.DBConn):
def new_order(self, user_id: str, store_id: str, id_and_count: [(str, int)]) -> (int, str, str):
order_id = ""
session = None # 初始化会话
try:
if not self.user_id_exist(user_id):
return error.error_non_exist_user_id(user_id) + (order_id,)
if not self.store_id_exist(store_id):
return error.error_non_exist_store_id(store_id) + (order_id,)
# 开始事务
session = self.conn.client.start_session()
session.start_transaction()
uid = "{}_{}_{}".format(user_id, store_id, str(uuid.uuid1()))
total_price = 0
for book_id, count in id_and_count:
result = self.conn.store_col.find_one({"store_id": store_id, "books.book_id": book_id}, {"books.$": 1})
if not result:
session.abort_transaction() # 事务回滚
return error.error_non_exist_book_id(book_id) + (order_id,)
result1 = self.conn.book_col.find_one({"id": book_id})
stock_level = result["books"][0]["stock_level"]
price = result1["price"]
if stock_level < count:
session.abort_transaction() # 事务回滚
return error.error_stock_level_low(book_id) + (order_id,)
result = self.conn.store_col.update_one({"store_id": store_id, "books.book_id": book_id, "books.stock_level": {"$gte": count}},
{"$inc": {"books.$.stock_level": -count}})
result = self.conn.store_col.update_one(
{"store_id": store_id, "books.book_id": book_id, "books.stock_level": {"$gte": count}},
{"$inc": {"books.$.stock_level": -count}},
session=session # 加入事务
)
if result.modified_count == 0:
session.abort_transaction() # 事务回滚
return error.error_stock_level_low(book_id) + (order_id,)
self.conn.order_detail_col.insert_one({
@ -41,9 +54,10 @@ class Buyer(db_conn.DBConn):
"book_id": book_id,
"count": count,
"price": price
})
}, session=session) # 加入事务
total_price += price * count
now_time = datetime.utcnow()
self.conn.order_col.insert_one({
"order_id": uid,
@ -52,26 +66,36 @@ class Buyer(db_conn.DBConn):
"create_time": now_time,
"price": total_price,
"status": 0
})
}, session=session) # 加入事务
order_id = uid
except BaseException as e:
logging.info("528, {}".format(str(e)))
# 提交事务
session.commit_transaction()
except Exception as e:
if session:
session.abort_transaction() # 异常发生时回滚事务
logging.error(f"Error during new_order: {str(e)}")
return 528, "{}".format(str(e)), ""
finally:
if session:
session.end_session() # 结束会话
return 200, "ok", order_id
def payment(self, user_id: str, password: str, order_id: str) -> (int, str):
session = None # 初始化会话
try:
result = self.conn.order_col.find_one({"order_id": order_id, "status": 0})
if result is None:
return error.error_invalid_order_id(order_id)
buyer_id = result["user_id"]
store_id = result["store_id"]
total_price = result["price"]
if buyer_id != user_id:
return error.error_authorization_fail()
result = self.conn.user_col.find_one({"user_id": buyer_id})
if result is None:
return error.error_non_exist_user_id(buyer_id)
@ -79,42 +103,67 @@ class Buyer(db_conn.DBConn):
if password != result.get("password", ""):
return error.error_authorization_fail()
result = self.conn.store_col.find_one({"store_id": store_id})
if result is None:
return error.error_non_exist_store_id(store_id)
seller_id = result.get("user_id")
if not self.user_id_exist(seller_id):
return error.error_non_exist_user_id(seller_id)
if balance < total_price:
return error.error_not_sufficient_funds(order_id)
result = self.conn.user_col.update_one({"user_id": buyer_id, "balance": {"$gte": total_price}}, {"$inc": {"balance": -total_price}})
# 开始事务
session = self.conn.client.start_session()
session.start_transaction()
# 扣除买家余额
result = self.conn.user_col.update_one(
{"user_id": buyer_id, "balance": {"$gte": total_price}},
{"$inc": {"balance": -total_price}},
session=session
)
if result.matched_count == 0:
session.abort_transaction() # 回滚事务
return error.error_not_sufficient_funds(order_id)
result = self.conn.user_col.update_one({"user_id": seller_id}, {"$inc": {"balance": total_price}})
# 增加卖家余额
result = self.conn.user_col.update_one(
{"user_id": seller_id},
{"$inc": {"balance": total_price}},
session=session
)
if result.matched_count == 0:
session.abort_transaction() # 回滚事务
return error.error_non_exist_user_id(buyer_id)
self.conn.order_col.insert_one({
"order_id": order_id,
"store_id": store_id,
"user_id": buyer_id,
"status": 1,
"price": total_price
})
result = self.conn.order_col.delete_one({"order_id": order_id, "status": 0})
if result.deleted_count == 0:
return error.error_invalid_order_id(order_id)
except BaseException as e:
# 更新订单状态为已付款
self.conn.order_col.update_one(
{"order_id": order_id},
{"$set": {"status": 1}},
session=session
)
# 删除未付款订单
self.conn.order_col.delete_one(
{"order_id": order_id, "status": 0},
session=session
)
# 提交事务
session.commit_transaction()
except Exception as e:
if session:
session.abort_transaction() # 回滚事务
logging.error(f"Error during payment: {str(e)}")
return 528, "{}".format(str(e))
return 200, "ok"
finally:
if session:
session.end_session() # 结束会话
return 200, "ok"
def add_funds(self, user_id, password, add_value) -> (int, str):
try:
@ -133,51 +182,83 @@ class Buyer(db_conn.DBConn):
return 200, ""
def receive_books(self, user_id: str, order_id: str) -> (int, str):
try :
session = None # 初始化会话
try:
# 开始事务
session = self.conn.client.start_session()
session.start_transaction()
result = self.conn.order_col.find_one({
"$or": [
{"order_id": order_id, "status": 1},
{"order_id": order_id, "status": 2},
{"order_id": order_id, "status": 3},
]
})
if result == None:
}, session=session)
if result is None:
session.abort_transaction() # 回滚事务
return error.error_invalid_order_id(order_id)
buyer_id = result.get("user_id")
paid_status = result.get("status")
if buyer_id != user_id:
session.abort_transaction() # 回滚事务
return error.error_authorization_fail()
if paid_status == 1:
session.abort_transaction() # 回滚事务
return error.error_books_not_sent()
if paid_status == 3:
session.abort_transaction() # 回滚事务
return error.error_books_repeat_receive()
self.conn.order_col.update_one({"order_id": order_id}, {"$set": {"status": 3}})
except BaseException as e:
# 更新订单状态为已收货
self.conn.order_col.update_one({"order_id": order_id}, {"$set": {"status": 3}}, session=session)
# 提交事务
session.commit_transaction()
except Exception as e:
if session:
session.abort_transaction() # 回滚事务
logging.error(f"Error during receive_books: {str(e)}")
return 528, "{}".format(str(e))
finally:
if session:
session.end_session() # 结束会话
return 200, "ok"
def cancel_order(self, user_id: str, order_id: str) -> (int, str):
session = None # 初始化会话
try:
# 未付款
result = self.conn.order_col.find_one({"order_id": order_id, "status": 0})
# 开始事务
session = self.conn.client.start_session()
session.start_transaction()
result = self.conn.order_col.find_one({"order_id": order_id, "status": 0}, session=session)
if result:
# 未付款订单
buyer_id = result.get("user_id")
if buyer_id != user_id:
return error.error_authorization_fail()
store_id = result.get("store_id")
price = result.get("price")
self.conn.order_col.delete_one({"order_id": order_id, "status": 0})
# 已付款
self.conn.order_col.delete_one({"order_id": order_id, "status": 0}, session=session)
else:
# 已付款订单
result = self.conn.order_col.find_one({
"$or": [
{"order_id": order_id, "status": 1},
{"order_id": order_id, "status": 2},
{"order_id": order_id, "status": 3},
]
})
}, session=session)
if result:
buyer_id = result.get("user_id")
if buyer_id != user_id:
@ -185,46 +266,63 @@ class Buyer(db_conn.DBConn):
store_id = result.get("store_id")
price = result.get("price")
result1 = self.conn.store_col.find_one({"store_id": store_id})
result1 = self.conn.store_col.find_one({"store_id": store_id}, session=session)
if result1 is None:
return error.error_non_exist_store_id(store_id)
seller_id = result1.get("user_id")
result2 = self.conn.user_col.update_one({"user_id": seller_id}, {"$inc": {"balance": -price}})
result2 = self.conn.user_col.update_one({"user_id": seller_id}, {"$inc": {"balance": -price}},
session=session)
if result2 is None:
return error.error_non_exist_user_id(seller_id)
result3 = self.conn.user_col.update_one({"user_id": buyer_id}, {"$inc": {"balance": price}})
result3 = self.conn.user_col.update_one({"user_id": buyer_id}, {"$inc": {"balance": price}},
session=session)
if result3 is None:
return error.error_non_exist_user_id(user_id)
result4 = self.conn.order_col.delete_one({
"$or": [
{"order_id": order_id, "status": 1},
{"order_id": order_id, "status": 2},
{"order_id": order_id, "status": 3},
]
})
"$or": [
{"order_id": order_id, "status": 1},
{"order_id": order_id, "status": 2},
{"order_id": order_id, "status": 3},
]
}, session=session)
if result4 is None:
return error.error_invalid_order_id(order_id)
else:
return error.error_invalid_order_id(order_id)
# recovery the stock
result = self.conn.order_detail_col.find({"order_id": order_id})
# 恢复库存
result = self.conn.order_detail_col.find({"order_id": order_id}, session=session)
for book in result:
book_id = book["book_id"]
count = book["count"]
result1 = self.conn.store_col.update_one({"store_id": store_id, "books.book_id": book_id}, {"$inc": {"books.$.stock_level": count}})
result1 = self.conn.store_col.update_one(
{"store_id": store_id, "books.book_id": book_id},
{"$inc": {"books.$.stock_level": count}},
session=session
)
if result1.modified_count == 0:
return error.error_stock_level_low(book_id) + (order_id,)
self.conn.order_col.insert_one({"order_id": order_id, "user_id": user_id, "store_id": store_id, "price": price, "status": 4})
except BaseException as e:
# 插入取消的订单记录
self.conn.order_col.insert_one(
{"order_id": order_id, "user_id": user_id, "store_id": store_id, "price": price, "status": 4},
session=session
)
# 提交事务
session.commit_transaction()
except Exception as e:
if session:
session.abort_transaction() # 回滚事务
logging.error(f"Error during cancel_order: {str(e)}")
return 528, "{}".format(str(e))
finally:
if session:
session.end_session() # 结束会话
return 200, "ok"
def check_hist_order(self, user_id: str):
@ -322,31 +420,60 @@ class Buyer(db_conn.DBConn):
else:
return 200, "ok", ans
def auto_cancel_order(self) -> (int, str):
session = None # 初始化会话
try:
wait_time = 20 # 等待时间20s
interval = datetime.utcnow() - timedelta(seconds=wait_time) # UTC时间
orders_to_cancel = self.conn.order_col.find({"create_time": {"$lte": interval}, "status": 0})
# 开始事务
session = self.conn.client.start_session()
session.start_transaction()
wait_time = 20 # 等待时间20秒
interval = datetime.utcnow() - timedelta(seconds=wait_time) # UTC时间
orders_to_cancel = self.conn.order_col.find({"create_time": {"$lte": interval}, "status": 0},
session=session)
if orders_to_cancel:
for order in orders_to_cancel:
order_id = order["order_id"]
user_id = order["user_id"]
store_id = order["store_id"]
price = order["price"]
self.conn.order_col.delete_one({"order_id": order_id, "status": 0})
result = self.conn.order_detail_col.find({"order_id": order_id})
# 删除未付款订单
self.conn.order_col.delete_one({"order_id": order_id, "status": 0}, session=session)
result = self.conn.order_detail_col.find({"order_id": order_id}, session=session)
for book in result:
book_id = book["book_id"]
count = book["count"]
result1 = self.conn.store_col.update_one({"store_id": store_id, "books.book_id": book_id}, {"$inc": {"books.$.stock_level": count}})
# 恢复库存
result1 = self.conn.store_col.update_one(
{"store_id": store_id, "books.book_id": book_id},
{"$inc": {"books.$.stock_level": count}},
session=session
)
if result1.modified_count == 0:
session.abort_transaction() # 回滚事务
return error.error_stock_level_low(book_id) + (order_id,)
self.conn.order_col.insert_one({"order_id": order_id, "user_id": user_id,"store_id": store_id, "price": price, "status": 4})
except BaseException as e:
# 插入取消的订单记录
self.conn.order_col.insert_one(
{"order_id": order_id, "user_id": user_id, "store_id": store_id, "price": price, "status": 4},
session=session)
# 提交事务
session.commit_transaction()
except Exception as e:
if session:
session.abort_transaction() # 回滚事务
logging.error(f"Error during auto_cancel_order: {str(e)}")
return 528, "{}".format(str(e))
finally:
if session:
session.end_session() # 结束会话
return 200, "ok"
def is_order_cancelled(self, order_id: str) -> (int, str):

+ 30
- 6
project1/bookstore/be/model/seller.py View File

@ -1,4 +1,5 @@
import json
import logging
from be.model import error
from be.model import db_conn
@ -68,28 +69,51 @@ class Seller(db_conn.DBConn):
return 200, "ok"
def send_books(self, user_id: str, order_id: str) -> (int, str):
session = None # 初始化会话
try:
# 开始事务
session = self.conn.client.start_session()
session.start_transaction()
result = self.conn.order_col.find_one({
"$or": [
{"order_id": order_id, "status": 1},
{"order_id": order_id, "status": 2},
{"order_id": order_id, "status": 3},
]
})
}, session=session)
if result == None:
if result is None:
session.abort_transaction() # 回滚事务
return error.error_invalid_order_id(order_id)
store_id = result.get("store_id")
paid_status = result.get("status")
result = self.conn.store_col.find_one({"store_id": store_id})
result = self.conn.store_col.find_one({"store_id": store_id}, session=session)
seller_id = result.get("user_id")
if seller_id != user_id:
session.abort_transaction() # 回滚事务
return error.error_authorization_fail()
if paid_status == 2 or paid_status == 3:
session.abort_transaction() # 回滚事务
return error.error_books_repeat_sent()
self.conn.order_col.update_one({"order_id": order_id}, {"$set": {"status": 2}})
except BaseException as e:
# 更新订单状态为已发货
self.conn.order_col.update_one({"order_id": order_id}, {"$set": {"status": 2}}, session=session)
# 提交事务
session.commit_transaction()
except Exception as e:
if session:
session.abort_transaction() # 回滚事务
logging.error(f"Error during send_books: {str(e)}")
return 528, "{}".format(str(e))
return 200, "ok"
finally:
if session:
session.end_session() # 结束会话
return 200, "ok"

Loading…
Cancel
Save