订单系统
下订单的逻辑2
加入购物车—>点击购物车—>选中商品—>结算—>确认订单页面
前端页面的准备:将ConfirmOrder.vue & AddMyAddr.vue&icon资源分别放入Vue项目指定的目录
-
加载收货地址API
-
点击添加新地址,实现添加新地址API(子组件给父组件传数据)
全局事件总线
-
删除地址&选择地址
点击地址模块右上角叉号,删除地址
点击地址,实现框选,选择地址
表结构设计
订单信息表:
#订单信息表
#order_id user addr total_count total_price freight pay_method pay_status created_time
#varchar(100) 外键 外键 int decimal decimal smallint smallint datetime
订单商品表
#订单商品表
#id good count price comment score order
#int 外键 int decimal longtext smallint 外键
class OrderGoods(models.Model):
SCORES = (
(0, "0分"),
(1, "20分"),
(2, "40分"),
(3, "60分"),
(4, "80分"),
(5, "100分")
)
good = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name="商品")
count = models.IntegerField("购买数量", default=1)
price = models.DecimalField("价格", max_digits=7, decimal_places=2)
comment = models.TextField("评论",default="")
score = models.SmallIntegerField("评分", default=5, choices=SCORES)
#若表中已有数据,迁移容易出现问题, 可以尝试修改db中的表名
order = models.ForeignKey(Orders, on_delete=models.CASCADE, verbose_name="订单号", default=1)
def __str__(self):
return self.good.sku_name + " in order:" + self.order.order_id
class Meta:
db_table = "order_goods_table"
verbose_name = "订单信息表"
verbose_name_plural = verbose_name
订单模型类
from django.db import models
from users.models import User
from users.models import UserAddr
from goods.models import Goods
# Create your models here.
#订单信息表
#id user addr total_count total_price post_price pay_method pay_status created_time
class Orders(models.Model):
PAY_STATUS = ((1,"待支付"),(2,"待发货"),(3,"待收货"),(4,"待评论"),(5,"已完成"),(6,"已取消"))
#订单编号--->时间戳字符串
order_id = models.CharField("订单编号", max_length=100,primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户")
addr = models.ForeignKey(UserAddr, on_delete=models.CASCADE, verbose_name="收货地址")
#买的总商品数
total_count = models.IntegerField("总商品数", default=1)
total_price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="总价格")
#邮费
post_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="运费")
#支付方式 1为支付宝 2为银联卡
pay_method = models.SmallIntegerField("支付方式", default=1, choices=((1,"支付宝"),(2,"银联卡")))
#1待支付--->2待发货--->3待收货--->4待评论--->5已完成
pay_status = models.SmallIntegerField("支付状态", default=1, choices=PAY_STATUS)
created_time = models.DateTimeField("创建时间", auto_now_add=True)
def __str__(self):
return "订单编号:" + self.order_id
class Meta:
db_table = "order_table"
verbose_name = "订单信息表"
verbose_name_plural = verbose_name
#订单商品表
class OrderGoods(models.Model):
#评分等级
SCORE_CHOICES = (
(0, '0分'),
(1, '20分'),
(2, '40分'),
(3, '60分'),
(4, '80分'),
(5, '100分'),
)
good = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name="商品")
count = models.IntegerField("购买数量", default=1)
price = models.DecimalField("在售价格", max_digits=10, decimal_places=2)
comments = models.TextField("商品评论", default="")
score = models.SmallIntegerField("评分", default=5, choices=SCORE_CHOICES)
#商品所属订单
order = models.ForeignKey(Orders, on_delete=models.CASCADE, verbose_name="订单编号")
#models.CASCADE 模型类的主表级联删除
def __str__(self):
return "订单%s 下的商品%s"%(self.order.order_id,self.good.sku_name)
class Meta:
db_table = "order_goods_table"
verbose_name = "订单商品表"
verbose_name_plural = verbose_name
购物车选中的商品
使用Vue的前端数据管理,也可以使用后端redis数据库查询。
创建订单
视图接口
#创建订单
class AddOrder(APIView):
"""
创建订单
1.接收前端数据
{ user: {"userName":"laufing"},
products: #获取勾选的商品[{},{}],
每个商品的数据结构{
'id': 'cart_1',
'productID': 7,
'productName':
'Redmi Note8',
'productImg':
'http://106.15.179.105:3000/public/imgs/phone/Redmi-Note8.png',
'price': 999,
'num': 3,
'maxNum': 20,
'check': True
}
addr: "10", #地址id
payMethod:"1", #支付方式,'1'支付宝 '2'银联卡
})
2. 查询用户对象user_obj, 查询地址对象addr_obj
3.以原子方式开启事务,并创建事务保存点(回滚点)
4.准备订单数据,创建订单对象,并存储订单记录
5.遍历订单中购买的商品,判断库存;
购买超出库存,则回滚事务,并返回响应{"code":204, "msg":"超出库存!创建订单失败!"}
未超库存,则更新**销量**&**库存**
good1.count += 购买的数量
good1.stock -= 购买的数量
good1.save()
total_count += 购买的数量
total_price += good1.price*购买的数量
6.创建订单商品对象,注意再次查询good对象--->good2
如果good2的销量&库存与good1一致,则创建订单商品对象,并保存
否则,回滚事务,并返回响应{"code":200, "msg":"创建订单失败!"}
"""
#接收前端数据
#@transaction.atomic #原子方式开启事务
def post(self, request):
#***1.获取前端数据***
#用户名
username = request.data.get("user").get("userName")
#订单中的商品,前端传入
order_goods = request.data.get("products")
#使用的收货地址id,字符串
selected_addr = request.data.get("addr")
#支付方式 '1'-支付宝 '2'-银联
pay_method = request.data.get("payMethod")
pay_status = "待付款"
print("购买的商品:\n", order_goods)
print("选择的地址:", selected_addr, "\n支付方式:", pay_method)
#***2.查询用户对象&地址对象***
try:
user_obj = User.objects.get(username=username)
except:
#一般不会出现该异常
return Response({"code":204, "msg":"用户不存在!"})
try:
addr_obj = UserAddr.objects.get(id=selected_addr)
except:
# 一般不会出现该异常
return Response({"code":204, "msg":"地址不存在!"})
#***3.以原子方式开启事务,并创建事务保存点(回滚点)***
#创建订单 与 添加订单商品 放入一组事务中
with transaction.atomic():
#创建事务保存点
point = transaction.savepoint()
try:
#***4.准备订单信息数据,存储订单记录***
#生成订单编号
order_id = str(uuid.uuid1())
#订单中的总商品数
total_count = 0
#订单总价格
total_price = 0
#固定运费,前端固定为0
post_price = 0
pay_method = int(pay_method) if pay_method else 1
#订单状态 固定为'待支付' 采用默认值1
#创建订单对象,并存储订单信息
order = Orders.objects.create(order_id=order_id,user=user_obj,addr=addr_obj,
total_count=total_count,total_price=total_price,
post_price=0,pay_method=pay_method,pay_status=1)
# print("xxxxxxxxxx")
#***5.遍历订单中购买的商品,判断库存***
#order_goods
for per_good in order_goods:
# ***6.创建订单商品对象,注意确认此时没有其他人改商品数据库***
# 如果good2的库存与good1一致,则创建订单商品对象,并保存
# 否则,重试;如果三次重试均失败,则抛出异常
#创建商品对象,每个商品对象的创建重试三次
for i in range(3):
#在db中查询该商品
good1 = Goods.objects.get(id=per_good.get("productID"))
origin_stock = good1.stock
#判断库存,库存过少,不可购买, 任意一个商品库存不足,订单都会创建失败!
if good1.stock < per_good.get("num"):
raise ValueError("商品库存不足!无法购买!")
#库存丰富,则可以正常购买,正常创建订单商品对象
new_stock = good1.stock - per_good.get("num")
new_count = good1.count + per_good.get("num")
#确认此时没有其他人完成购买
good2 = Goods.objects.filter(id=per_good.get("productID"), stock=origin_stock)
if good2:
#说明此时没有其他人完成购买,即没有修改数据库
#创建订单商品对象,并存储
# print(float(good1.selling_price))
selling_price = float(good1.selling_price)
#模型类定义的max_digits不够大,出错,已解决, 给一个float就可以
OrderGoods.objects.create(good=good1, count=per_good.get("num"),
price=selling_price, order=order)
# 更新商品的销量&库存
good1.stock = new_stock
good1.count = new_count
good1.save()
#累加订单中的商品总数
total_count += per_good.get("num")
#累加订单总价格
total_price += good1.selling_price * per_good.get("num")
break
else:
#说明在创建订单商品对象前,有其他人完成购买
continue
else:
#重试三次,均失败
raise ValueError("重试结束!创建订单失败!")
# print("77777777")
#***7.正常创建所有订单商品对象后,更新订单对象的总商品数和总价格***
order.total_count = total_count
order.total_price = float(total_price)
order.save()
#***8.删除redis购物车中的已经购买的商品***
redis_conn = redis.Redis(host="localhost", port=6379, db=0)
cart_key = "cart_%s"%user_obj.id
cart_selected_key = "cart_selected_%s"%user_obj.id
for per_good in order_goods:
good_id = per_good.get("productID")
#删除购物车数据
redis_conn.hdel(cart_key, good_id)
#删除购物车商品选中状态
redis_conn.srem(cart_selected_key, good_id)
redis_conn.close()
#***9.订单创建完成, 提交事务保存点***
transaction.savepoint_commit(point)
return Response({"code": 200, "msg":"创建订单成功!"})
except Exception as e:
print("创建订单异常:", e)
#创建订单异常,直接回滚事务,防止db中产生不必要的空订单
transaction.savepoint_rollback(point)
#返回响应
return Response({"code":204, "msg":"创建订单失败!"})