前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >美多商城项目(九)

美多商城项目(九)

作者头像
小闫同学啊
发布2019-07-18 15:02:27
9360
发布2019-07-18 15:02:27
举报
文章被收录于专栏:小闫笔记小闫笔记

正文共: 5342字 7图 预计阅读时间: 14分钟

项目仓库

https://github.com/EthanYan6/E-commerce-sites.git

结合代码查看笔记,效果更佳。笔记只是记录重点或者难点。

每日分享

Dream big dreams. Small dreams have no magic.

要拥有大梦想,小的想法没有魔力。

小闫语录

梦想还是要有的,万一实现了呢?先定一个小目标,挣他一个亿......

高三的时候,老师曾说『定目标要定的大一点,一个较低的目标影响你的实际发挥,难以激发你的潜力』。你的梦想应该有足够的驱动力,促使你的成长,前行。轻易就实现的目标适合在逆境时,为自己加油打气。

美多商城项目(九)

1.获取用户结算商品的信息

用户所要结算的商品就是购物车中被勾选的商品。

API: GET /orders/settlement/
参数:
    通过请求头传递jwt token
响应:
    {
        "freight": "订单运费",
        "skus": [
            {
                "id": "商品id",
                "name": "商品名称",
                "price": "商品价格",
                "default_image_url": "默认图片",
                "count": "结算数量"
            },
            ...
        ]
    }

1.1业务逻辑

1.获取登录用户。

2.从登陆用户的redis购物车记录中获取用户购物车中被勾选的商品id和对应数量count。

2.1获取redis链接。

2.2从redis set中获取用户购物车中被勾选的商品的id。

2.3从redis hash中获取用户购物车中添加的所有商品id和对应数量count。

返回的是一个字典

{
    b'<sku_id>': b'<count>',
    ...
}

2.4组织数量,将上面字典中bytes类型的数据,转换成int类型。

3.根据商品id获取对应的商品数据并组织运费。

3.1遍历出每个商品。

3.2给sku对象增加属性count,保存该商品所要结算的数量。

3.3定义『订单结算商品序列化器类』

3.4将商品数据序列化。

3.5组织运费,固定为10元。

decimal意思为十进制,这个模块提供了十进制浮点运算支持。 可以传递给Decimal整型或者字符串参数,但不能是浮点数据,因为浮点数据本身就不准确。

from decimal import *
# 下面是常用的两种情况
# 可以限定有效数字位数,让其按我们的需求输出
getcontext().prec = 6
Decimal(1)/Decimal(3) 
>>> 0.333333  # 输出结果,有效数字为6位
# 可以决定保留几位小数
Decimal('265.2548').quantize(Decimal('0.000'))
>>> 265.255  # 输出结果

4.将数据序列化并返回。

2.订单数据的保存(订单创建)

API: POST /orders/
参数:
    通过请求头传递jwt token
    {
        "address": "收获地址id",
        "pay_method": "支付方式"
    }
响应:
    {
        "order_id": "订单编号"
    }

2.1订单保存的基本流程

1.向订单基本信息表中添加一条记录。

2.订单中包含几个商品就需要向订单商品表中添加几条记录。

3.删除redis中对应购物车记录。

2.2业务逻辑

1.获取参数并进行校验(参数完整性,address是否存在,pay_method是否合法)。

1.1创建『订单序列化器类』

2.保存订单的数据。

2.1『订单序列化器类』中定义create方法保存订单的数据。

2.2获取address和pay_method。

2.3获取登录用户

2.4订单的id设置为年月日时分秒+用户id

# 把一个日期格式化为一个字符串
datetime.now().strftime('%Y%m%d%H%M%S')
# 用户id长度不一致,我们可以格式化
'%010d' % user.id

2.5定义订单商品总数和实付款接收参数。

2.6设置运费。

2.7判断支付状态,待发货还是待支付。从而做出相应逻辑。

2.8向订单基本信息表中添加一条记录。

2.9订单中包含几个商品,就需要向订单商品表中添加几条记录。

2.9.1从redis购物车中获取用户所需要购买的商品id(redis set购物车中勾选的商品id)

2.9.2从redis hash中获取用户购物车中添加的商品的id和对应数量count

2.9.3遍历商品的id

2.9.4获取用户所要购买的该商品的数量count。

2.9.5根据sku_id获取商品对象。

2.9.6商品库存判断。

2.9.7减少商品库存,增加销量。

2.9.8向订单商品表中添加一条记录。

2.9.9累加计算订单中商品的总数量和总金额。

2.9.10计算实付款(添加运费)。

2.9.11更新订单商品的总数量和实付款。

2.10删除redis中对应购物车记录。

此处可以使用管道,将所有的数据一次性删除。

hdel

hdel <key> <field> ...

删除redis hash中指定的field属性和值。

3.返回应答,订单创建成功。

3.订单事务

对于订单保存中,涉及到数据库操作的过程,应该放在同一个事务中,要么都成功,要么都失败。

mysql事务:一组sql语句,要么都成功,要么都失败。

3.1mysql事务基本操作

开启事务:

begin;或 start transaction;

事务提交,让事务中sql语句的执行结果永久生效:

commit;

事务回滚,撤销事务中sql语句的执行结果:

rollback;

3.2mysql事务保存点

在事务中,可以设置事务的保存点,设置了事务保存点之后,在进行事务的回滚时,可以不回滚整个事务,而是回滚到指定的保存点,该保存点之后的sql语句执行结果会撤销。

设置事务的保存点

savepoint <保存点名称>

回滚到指定的保存点,该保存点之后的sql语句执行结果会撤销:

rollback to <保存点名称>

3.3django中事务使用

from django.db import transaction

with transaction.atomic():
    # with语句块下面的代码,凡是涉及到数据库操作的代码,在进行数据库操作时,都会放在同一个事务中
    # 设置事务的保存点
    sid = transaction.savepoint()

    ...(代码)

    # 回滚到指定的保存点
    transaction.savepoint_rollback(sid)

atomic会自动进行事务的提交commit和回滚rollback。 只有操作数据库时sql语句有错的时候才能自动进行提交和回滚。 我们可以将涉及到数据库操作的部分进行错误捕获,有错统一返回下单失败;如果想让代码部分中的涉及到不同的异常抛出,可以在统一返回下单失败之前再进行一次捕获异常,抛出不同的异常。

4.订单并发

4.1问题描述

当多个人同时购买同一件商品时,有可能会产生订单并发问题。

4.2举例说明

id为16的商品库存有10件,两人同时购买这件商品,每人购买5件,产生订单并发问题之后,两个下单都能成功,但是商品的库存变为了5件,而不是0件。

4.3原因分析

用户A:进程1

用户B:进程2

从上到下模拟cpu进程切换,用户A和B同时下单过程的模拟

过程1-用户A

1.向tborderinfo中添加一条记录。

2.获取商品的信息(库存为10)。

3.判断商品库存(5<10)。

4进程切换,调度进程2,开始处理用户B的请求。

过程2-用户B

5.向tborderinfo中添加一条记录。

6.获取商品的信息(库存为10)。

7.判断商品库存(5<10)。

8.进程切换,重新调度进程1,处理用户A的请求。

过程3-用户A

9.减少商品库存,增加销量(10-5=5)。

10.向tborderinfo中添加一条记录。

11.下单成功,开始调度进程2处理用户B请求。

过程4-用户B

12.由于用户之前的过程已经处理完,继续进行下面步骤-->减少商品库存,增加销量(10-5=5)。

13.向tborderinfo中添加一条记录。

14.下单成功。

结果

14.库存显示5。

4.4订单并发-解决方案

4.4.1解决方案-悲观『锁』

在事务中查询数据的时候尝试对数据进行加锁(互斥锁),获取到锁的事务可以对数据进行操作,获取不到锁的事务会阻塞,直到锁被释放。

结果

用户A和用户B还是按照之前的举例过程进行操作。用户A先获取到锁,可以进行操作,用户B却拿不到锁,事务会阻塞,等待用户A操作完释放锁之后再进行事务操作,所以库存就不会出现问题。

使用

MySQL数据库中sql语句:

select stock from tb_sku where id=1 for update;

Django中使用selectforupdate()可以获取锁:

sku = SKU.objects.select_for_update().get(id=sku_id)
悲观锁在实际开发中不会使用
4.4.2解决方案-乐观『锁』

乐观锁并不是真实存在的锁,而是在更新的时候判断此时的库存是否是之前查询出的库存,如果相同,表示没人修改,可以更新库存,否则表示别人抢过资源,不再执行库存更新。

乐观锁需要配合mysql事务的隔离级别,将mysql事务的隔离级别改为Read committed

结果

用户A和用户B按照之前的举例过程进行操作。此次,用户A和用户B在获取商品信息之后都记录一下原始库存,在下单成功之前,再进行一次库存查询。用户A执行完后,用户B进行操作时,两次库存不一致,更新失败,重新进行尝试。

更新失败需要重新进行尝试,最多尝试3次,否则下单失败。

使用

MySQL数据库中的sql语句:

update tb_sku set stock=2 where id=1 and stock=7;

Django中的使用:

SKU.objects.filter(id=1, stock=7).update(stock=2)
MySQL事务隔离级别

事务隔离级别指的是在处理同一个数据的多个事务中,一个事务修改数据后,其他事务何时能看到修改后的结果。

MySQL数据库事务隔离级别主要有四种:

Serializable 串行化,一个事务一个事务的执行

Repeatable read 可重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响

Read committed 读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值

Read uncommitted 读取未提交,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。

MySQL数据库默认使用可重复读( Repeatable read),而使用乐观锁的时候,如果一个事务修改了库存并提交了事务,那其他的事务应该可以读取到修改后的数据值,所以不能使用可重复读的隔离级别,应该修改为读取已提交Read committed。

隔离级别

说明

Repeatable read 可重复读

在事务中执行同一个查询语句时,获取到的结果永远和第一次获取的结果一致,即使其他事务修改了对应的数据并且进行了提交,当前事务仍然获取不到更新之后的结果。

Read committed 读取为提交

其他事务修改了对应的数据并进行了提交,当前事务就能获取更新之后的结果。

MySQL事务的默认隔离级别为:Repeatable read可重复读

修改默认隔离级别

进入下面的路径:

/etc/mysql/mysql.conf.d

执行下面命令:

sudo vim mysqld.cnf

在105行添加下列一句命令:

transaction-isolation=READ-COMMITTED

重启mysql服务

sudo service mysql restart
4.4.3解决方案-任务队列

采用异步下单,将下单的过程封装成celery任务函数,同时在启动worker时只创建一个进程。

5.支付宝支付

5.1对接支付宝

线上环境

1.登录支付宝开发平台。

2.创建开发者应用,提交相关配置信息并等待审核。

3.审核通过之后,获取appid,然后就可以开发支付宝相关功能。

电脑网站支付功能需要签约

沙箱环境

对线上环境的模拟,提供给开发者使用,让开发者在不创建线上应用的请求就可以完成相应功能的开发。

SDK:软件开发工具包

具体对应操作请查看开发者文档:

支付宝开发者文档

文档主页:

https://openhome.alipay.com/developmentDocument.htm

产品介绍:https://docs.open.alipay.com/270

快速接入:https://docs.open.alipay.com/270/105899/

SDK:https://docs.open.alipay.com/270/106291/ python

对接支付宝SDK:https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

python对接支付宝SDK安装:

pip install python-alipay-sdk --upgrade

API列表:https://docs.open.alipay.com/270/105900/

5.2配置秘钥

5.2.1生成应用的私钥和公钥
openssl
OpenSSL> genrsa -out app_private_key.pem 2048  # 私钥RSA2
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
OpenSSL> exit
5.2.2保存应用私钥文件

在payment应用中新建keys目录,用来保存秘钥文件。

将应用私钥文件appprivatekey.pem复制到payment/keys目录下。

5.2.3查看公钥
cat app_publict_key.pem

将公钥内容复制给支付宝

5.2.4保存支付宝公钥

在创建的支付子应用payment/keys目录下新建alipaypublickey.pem文件,用于保存支付宝的公钥文件。

将支付宝的公钥内容复制到alipaypublickey.pem文件中

注意:还需要在公钥文件中补充开始与结束标志

-----BEGIN PUBLIC KEY-----
此处是公钥内容
-----END PUBLIC KEY-----

优质文章推荐:

公众号使用指南

redis操作命令总结

前端中那些让你头疼的英文单词

Flask框架重点知识总结回顾

项目重点知识点详解

难点理解&面试题问答

flask框架中的一些常见问题

团队开发注意事项

浅谈密码加密

Django框架中的英文单词

Django中数据库的相关操作

DRF框架中的英文单词

重点内容回顾-DRF

Django相关知识点回顾

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈技术精选 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 项目仓库
  • 每日分享
  • 美多商城项目(九)
    • 1.获取用户结算商品的信息
      • 1.1业务逻辑
    • 2.订单数据的保存(订单创建)
      • 2.1订单保存的基本流程
      • 2.2业务逻辑
    • 3.订单事务
      • 3.1mysql事务基本操作
      • 3.2mysql事务保存点
      • 3.3django中事务使用
    • 4.订单并发
      • 4.1问题描述
      • 4.2举例说明
      • 4.3原因分析
      • 4.4订单并发-解决方案
    • 5.支付宝支付
      • 5.1对接支付宝
      • 5.2配置秘钥
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档