コード例 #1
0
ファイル: views.py プロジェクト: Jioani/meiduo
 def get(self, request):
     code = request.GET.get("code")
     oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
                     client_secret=settings.QQ_CLIENT_SECRET,
                     redirect_uri=settings.QQ_REDIRECT_URI)
     try:
         access_token = oauth.get_access_token(code)
         openid = oauth.get_open_id(access_token)
     except:
         return JsonResponse({"code": 400, "message": "登陆失败"})
     try:
         qq_user = OAuthQQUser.objects.get(openid=openid)
     except:
         secret_openid = generate_secret_openid(openid)
         return JsonResponse({
             "code": 300,
             "message": "OK",
             "secret_openid": secret_openid
         })
     else:
         user = qq_user.user
         login(request, user)
         response = JsonResponse({"code": 0, "message": "OK"})
         response.set_cookie("username",
                             user.username,
                             max_age=14 * 24 * 3600)
         cart_helper = CartHelper(request, response)
         cart_helper.merge_cookie_cart_to_redis()
         return response
コード例 #2
0
    def delete(self, request):
        """
        购物车记录删除:
        ① 获取参数并进行校验
        ② 根据用户是否登录,删除对应的购物车记录
        ③ 返回响应
        """
        # ① 获取参数并进行校验
        req_data = json.loads(request.body)
        sku_id = req_data.get('sku_id')

        if not sku_id:
            return JsonResponse({'code': 400,
                                 'message': '缺少必传参数'})

        # ② 根据用户是否登录,删除对应的购物车记录
        response = JsonResponse({'code': 0,
                                 'message': '购物车记录删除成功'})

        try:
            cart_helper = CartHelper(request, response)
            cart_helper.remove_cart(sku_id)
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': '购物车记录删除失败'})

        # ③ 返回响应
        return response
コード例 #3
0
    def get(self, request):
        """ 获取QQ登录用户的openid并进行处理 """
        # 1.获取code
        code = request.GET.get('code')

        # 2.获取QQ登陆用户的openid
        oauth = OAuthQQ(
            client_id=settings.QQ_CLIENT_ID,
            client_secret=settings.QQ_CLIENT_SECRET,
            redirect_uri=settings.QQ_REDIRECT_URI,
        )

        try:
            # 根据code获取access_token
            access_token = oauth.get_access_token(code)

            # 根据access_token获取openid
            openid = oauth.get_open_id(access_token)

        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': 400, 'message': 'QQ登陆失败'})

        # 3.根据openid是否已经和本网站用户进行绑定进行处理
        try:
            qq_user = OAuthQQUser.objects.get(openid=openid)
        except OAuthQQUser.DoesNotExist:
            # 如果未进行绑定, 将openid加密并进行返回
            secret_openid = generate_secret_openid(openid)
            return JsonResponse(
                {
                    'code': 300,
                    'message': 'OK',
                    'secret_openid': secret_openid
                }, )
        else:
            # 如果已经绑定,保存用户的登陆状态信息
            user = qq_user.user
            login(request, user)

            response = JsonResponse({
                'code': 0,
                'message': 'OK',
            })

            # 设置cookie
            response.set_cookie(
                'username',
                user.username,
                max_age=14 * 24 * 3600,
            )

            # 增加代码:合并购物车数据
            cart_helper = CartHelper(request, response)
            cart_helper.merge_cookie_cart_to_redis()

            return response
コード例 #4
0
    def put(self, request):
        """
        购物车记录修改:
        ① 获取参数并进行校验
        ② 根据用户是否登录,分别修改购物车记录的数据
        ③ 返回响应
        """
        # ① 获取参数并进行校验
        req_data = json.loads(request.body)
        sku_id = req_data.get('sku_id')
        count = req_data.get('count')
        selected = req_data.get('selected', True)

        if not all([sku_id, count]):
            return JsonResponse({'code': 400,
                                 'message': '缺少必传参数'})

        try:
            count = int(count)
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': 'count参数有误'})

        if count <= 0:
            return JsonResponse({'code': 400,
                                 'message': 'count参数有误'})

        try:
            sku = SKU.objects.get(id=sku_id)
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': 'sku商品不存在'})

        # ② 根据用户是否登录,分别修改购物车记录的数据
        cart_sku = {
            'sku_id': sku_id,
            'count': count,
            'selected': selected
        }

        response = JsonResponse({'code': 0,
                                 'message': '购物车记录修改成功',
                                 'cart_sku': cart_sku})

        try:
            cart_helper = CartHelper(request, response)
            cart_helper.update_cart(sku_id, count, selected)
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': '购物车记录修改失败'})

        return response
コード例 #5
0
ファイル: views.py プロジェクト: Jioani/meiduo
 def get(self, request):
     """订单结算页面"""
     # ① 获取当前用户的收货地址信息
     addresses = Address.objects.filter(user=request.user, is_delete=False)
     # ② 从redis中获取用户所要结算的商品信息
     try:
         cart_helper = CartHelper(request)
         cart_data = cart_helper.get_redis_selected_cart()
     except Exception as e:
         return JsonResponse({"code": 400,
                              "message": "获取购物车数据失败"})
     # ③ 查询数据库获取对应的商品数据
     # 商品数据
     sku_li = []
     try:
         skus = SKU.objects.filter(id__in=cart_data.keys())
         for sku in skus:
             sku_li.append({
                 "id": sku.id,
                 "name": sku.name,
                 "default_image_url": "http://192.168.19.131:8888/" + sku.default_image.name,
                 "price": sku.price,
                 "count": cart_data[sku.id]
             })
     except Exception as e:
         return JsonResponse({"code": 400,
                              "message": "获取商品数据失败"})
     freight = Decimal(10.00)
     address_li = []
     try:
         for address in addresses:
             address_li.append({
                 "id": address.id,
                 "province": address.province.name,
                 "city": address.city.name,
                 "district": address.district.name,
                 "place": address.place,
                 "receiver": address.receiver,
                 "mobile": address.mobile
             })
     except Exception as e:
         return JsonResponse({"code": 400,
                              "message": "地址信息获取错误"})
     context = {
         "addresses": address_li,
         "skus": sku_li,
         "freight": freight,
         "nowsite": request.user.default_address_id
     }
     return JsonResponse({"code": 0,
                          "message": "OK",
                          "context": context})
コード例 #6
0
    def post(self, request):
        """
        购物车数据新增:
        ① 获取参数并进行校验
        ② 根据用户是否登录,分别进行购物车数据的保存
        ③ 返回响应,购物车记录添加成功
        """
        # ① 获取参数并进行校验
        req_data = json.loads(request.body)
        sku_id = req_data.get('sku_id')
        count = req_data.get('count')
        selected = req_data.get('selected', True)

        if not all([sku_id, count]):
            return JsonResponse({'code': 400,
                                 'message': '缺少必传参数'})

        try:
            count = int(count)
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': 'count参数有误'})

        if count <= 0:
            return JsonResponse({'code': 400,
                                 'message': 'count参数有误'})

        try:
            sku = SKU.objects.get(id=sku_id)
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': 'sku商品不存在'})

        # ② 根据用户是否登录,分别进行购物车数据的保存
        response = JsonResponse({'code': 0,
                                 'message': '购物车添加成功',
                                 'count': count})

        try:
            cart_helper = CartHelper(request, response)
            cart_helper.add_cart(sku_id, count, selected)
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': '购物车添加失败'})

        # ③ 返回响应,购物车记录添加成功
        return response
コード例 #7
0
    def post(self, request):
        """ 用户登录 """
        # 1.获取参数并进行校验(参数完整性,用户名和密码是否正确)
        req_data = json.loads(request.body)

        username = req_data.get('username')
        password = req_data.get('password')
        remember = req_data.get('remember')

        if not all([username, password, ]):
            return JsonResponse({'code': 400,
                                 'message': '缺少必传参数!'})

        # 判断客户端传递的username参数是否符合手机号格式
        if re.match(r'^1[3-9]\d{9}$', username):
            User.USERNAME_FIELD = 'mobile'
        else:
            User.USERNAME_FIELD = 'username'

        # 用户名和密码是否正确
        user = authenticate(username=username, password=password)

        if user is None:
            return JsonResponse({'code': 400,
                                 'message': '用户名或密码错误!'})

        # 2.保存登陆用户的状态信息
        login(request, user)

        if not remember:
            # 如果未选择记住登录,浏览器关闭即失效
            request.session.set_expiry(0)

        # 3.返回响应
        response = JsonResponse({'code': 0,
                                 'message': 'OK'})
        # cookie 保存 username 用户名
        response.set_cookie('username',
                            user.username,
                            max_age=14 * 24 * 3600)

        # 增加代码:合并购物车数据
        cart_helper = CartHelper(request, response)
        cart_helper.merge_cookie_cart_to_redis()

        return response
コード例 #8
0
 def create(self, validated_data):
     username = validated_data["username"]
     password = validated_data["password"]
     mobile = validated_data["mobile"]
     try:
         user = User.objects.create_user(username=username,
                                         password=password,
                                         mobile=mobile)
     except Exception as e:
         raise serializers.ValidationError('数据库保存错误')
     from django.contrib.auth import login
     login(self.context("request"), user)
     # ③ 返回响应
     response = Response({'code': 0, 'message': 'OK'})
     response.set_cookie("username", user.username, max_age=30 * 24 * 3600)
     cart_helper = CartHelper(self.context("request"), response)
     cart_helper.merge_cookie_cart_to_redis()
     return user
コード例 #9
0
ファイル: views.py プロジェクト: Jioani/meiduo
 def post(self, request):
     req_data = json.loads(request.body.decode())
     mobile = req_data.get("mobile")
     password = req_data.get("password")
     sms_code = req_data.get("sms_code")
     secret_openid = req_data.get("secret_openid")
     if not all([mobile, password, sms_code, secret_openid]):
         return JsonResponse({"code": 400, "message": "缺少必传参数"})
     if not re.match(r"^1[3-9]\d{9}$", mobile):
         return JsonResponse({'code': 400, 'message': '请输入正确的手机号码'})
     if not re.match(r"^[0-9A-Za-z]{8,20}$", password):
         return JsonResponse({'code': 400, 'message': '请输入8-20位的密码'})
     redis_conn = get_redis_connection("verify_code")
     sms_code_redis = redis_conn.get("sms_%s" % mobile)
     if not sms_code_redis:
         return JsonResponse({"code": 400, "message": "短信验证码已失效"})
     if sms_code_redis != sms_code:
         return JsonResponse({"code": 400, "message": "短信验证码错误"})
     openid = check_secret_openid(secret_openid)
     if not openid:
         return JsonResponse({'code': 400, 'message': 'secret_openid有误'})
     try:
         user = User.objects.get(mobile=mobile)
     except:
         import base64
         username = base64.b64encode(mobile.encode()).decode()
         user = User.objects.create(mobile=mobile,
                                    username=username,
                                    password=password)
     else:
         if not user.check_password(password):
             return JsonResponse({"code": 400, "message": "登录密码错误"})
     try:
         OAuthQQUser.objects.create(openid=openid, user=user)
     except Exception as e:
         print(e)
         return JsonResponse({'code': 400, 'message': '数据库操作失败'})
     login(request, user)
     response = JsonResponse({'code': 0, 'message': 'OK'})
     # 设置cookie
     response.set_cookie('username', user.username, max_age=3600 * 24 * 14)
     cart_helper = CartHelper(request, response)
     cart_helper.merge_cookie_cart_to_redis()
     return response
コード例 #10
0
    def put(self, request):
        """
        购物车记录全选和取消全选:
        ① 获取参数并进行校验
        ② 根据用户是否登录,对购物车记录进行全选和取消全选操作
        ③ 返回响应
        """
        # ① 获取参数并进行校验
        req_data = json.loads(request.body)
        selected = req_data.get('selected', True)

        # ② 根据用户是否登录,对购物车记录进行全选和取消全选操作
        response = JsonResponse({'code': 0, 'message': '购物车记录操作成功'})
        try:
            cart_helper = CartHelper(request, response)
            cart_helper.select_cart(selected)
        except Exception as e:
            return JsonResponse({'code': 400, 'message': '购物车记录操作失败'})
        # 返回响应
        return response
コード例 #11
0
    def get(self, request):
        """
        购物车记录获取:
        ① 根据用户是否登录,分别进行购物车数据的获取
        ② 组织数据并返回响应
        """
        # ① 根据用户是否登录,分别进行购物车数据的获取
        try:
            cart_helper = CartHelper(request)
            cart_dict = cart_helper.get_cart()
        except Exception as e:
            return JsonResponse({'code': 400, 'message': '购物车获取失败!'})
        try:
            skus = SKU.objects.filter(id__in=cart_dict.keys())
        except Exception as e:
            return JsonResponse({'code': 400, 'message': '获取sku商品信息失败!'})

        cart_skus = []
        for sku in skus:
            cart_skus.append({
                'id':
                sku.id,
                'name':
                sku.name,
                'price':
                sku.price,
                'default_image_url':
                'http://192.168.19.131:8888/' + sku.default_image.name,
                'count':
                cart_dict[sku.id]['count'],
                'selected':
                cart_dict[sku.id]['selected'],
            })
        return JsonResponse({
            'code': 0,
            'message': 'OK',
            'cart_skus': cart_skus
        })
コード例 #12
0
 def post(self, request):
     requ_data = json.loads(request.body)
     username = requ_data.get("username")
     password = requ_data.get("password")
     remember = requ_data.get("remember")
     if not all([username, password]):
         return JsonResponse({"code": 400, "message": "缺少必传参数"})
     if re.match("^1[3-9]\d{9}$", username):
         User.USERNAME_FIELD = "mobile"
     else:
         User.USERNAME_FIELD = "username"
     user = authenticate(username=username, password=password)
     if user is None:
         return JsonResponse({"code": "400", "message": "账号用户名或密码错误"})
     login(request, user)
     response = JsonResponse({"code": 0, "message": "登录成功"})
     if not remember:
         request.session.set_expiry(0)
         response.set_cookie("username", user.username)
         return response
     response.set_cookie("username", user.username, max_age=30 * 24 * 3600)
     cart_helper = CartHelper(request, response)
     cart_helper.merge_cookie_cart_to_redis()
     return response
コード例 #13
0
ファイル: views.py プロジェクト: Jioani/meiduo
 def post(self, request):
     req_data = json.loads(request.body)
     address_id = req_data.get("address_id")
     pay_method = req_data.get("pay_method")
     if not all([address_id, pay_method]):
         return JsonResponse({"code": 400,
                              "message": "缺少必传参数"})
     try:
         address = Address.objects.get(id=address_id)
     except Exception as e:
         return JsonResponse({"code": 400,
                              "message": "地址信息有误"})
     if pay_method not in [1, 2]:
         return JsonResponse({"code": 400,
                              "message": "支付方式有误"})
     user = request.user
     order_id = timezone.now().strftime("%Y%m%d%H%M%S") + "%09d" % user.id
     total_count = 0
     total_amount = 0
     if pay_method == 1:
         status = 2
     else:
         status = 1
     freight = Decimal(10.00)
     # 开启事务
     with transaction.atomic():
         # 设置数据库操作时, 事务中的保存点
         sid = transaction.savepoint()
         order = OrderInfo.objects.create(order_id=order_id,
                                          user=user,
                                          address=address,
                                          total_count=total_count,
                                          total_amount=total_amount,
                                          freight=freight,
                                          pay_method=pay_method,
                                          status=status)
         cart_helper = CartHelper(request)
         cart_dict = cart_helper.get_redis_selected_cart()
         sku_ids = cart_dict.keys()
         for sku_id in sku_ids:
             for i in range(3):
                 sku = SKU.objects.get(id=sku_id)
                 count = cart_dict[sku_id]
                 # 记录原始库存和销量
                 origin_stock = sku.stock
                 origin_sales = sku.sales
                 print('下单用户:%s 商品库存:%d 购买数量:%d 尝试第 %d 次' % (
                     user.username, sku.stock, count, i + 1))
                 if count > sku.stock:
                     # 数据库操作时,撤销事务中指定保存点之后的操作
                     transaction.savepoint_commit(sid)
                     return JsonResponse({"code": 400,
                                          "message": "商品库存不足"})
                 import time
                 time.sleep(10)
                 # 更新商品库存和销量
                 new_stock = origin_stock - count
                 new_sales = origin_sales + count
                 # 注意:update 方法返回的是被更新的行数
                 # update tb_sku set stock=<new_stock>, sales=<news_sales>
                 # where id=<sku_id> and stock=<origin_stock>;
                 res = SKU.objects.filter(id=sku.id,
                                          stock=origin_stock).update(stock=new_stock,
                                                                     sales=new_sales)
                 if res == 0:
                     if i == 2:
                         # 尝试下单更新了 3 次,仍然失败,报下单失败错误
                         # 数据库操作时,撤销事务中指定保存点之后的操作
                         transaction.savepoint_commit(sid)
                         return JsonResponse({"code": 400,
                                              "message": "下单失败!"})
                     continue
                 sku.spu.sales += count
                 sku.spu.save()
                 OrderGoods.objects.create(order=order,
                                           sku=sku,
                                           count=count,
                                           price=sku.price)
                 total_count += count
                 total_amount += count * sku.price
                 break
             total_amount += freight
             order.total_count = total_count
             order.total_amount = total_amount
             order.save()
             cart_helper.clear_redis_selected_cart()
             return JsonResponse({"code": 0,
                                  "message": "下单成功",
                                  "order_id": order_id})
コード例 #14
0
    def post(self, request):
        """ 绑定QQ登陆用户信息 """
        # 1.获取参数并进行校验
        req_data = json.loads(request.body)
        mobile = req_data.get('mobile')
        password = req_data.get('password')
        sms_code = req_data.get('sms_code')
        secret_openid = req_data.get('secret_openid')

        if not all([mobile, password, sms_code, secret_openid]):
            return JsonResponse({'code': 400, 'message': '缺少必传参数!'})

        # 判断手机号格式是否正确
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400, 'message': '请输入正确的手机号!'})
        # 判断密码是否合格
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return JsonResponse({'code': 400, 'message': '请输入8-20位的密码!'})

        # 短信验证码是否正确
        redis_conn = get_redis_connection('verify_code')
        sms_code_redis = redis_conn.get('sms_%s' % mobile)

        if not sms_code_redis:
            return JsonResponse({'code': 400, 'message': '短信验证码已过期!'})

        if sms_code != sms_code_redis:
            return JsonResponse({'code': 400, 'message': '短信验证码错误!'})

        # 对access_token进行解密
        openid = check_secret_openid(secret_openid)
        if not openid:
            return JsonResponse({'code': 400, 'message': 'secret_openid有误!'})

        # 2.绑定保存QQ登陆用户信息
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # 注册信息用户
            import base64
            # 手机号进行 base64 编码生成用户名
            username = base64.b64encode(mobile.encode()).decode()
            user = User.objects.create_user(
                username=username,
                password=password,
                mobile=mobile,
            )

        else:
            # 校验密码是否正确
            if not user.check_password(password):
                return JsonResponse({'code': 400, 'message': '登陆密码错误!'})
        try:
            OAuthQQUser.objects.create(openid=openid, user_id=user.id)

        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': 400, 'message': '数据库操作失败!'})

        # 返回响应,登陆成功
        login(request, user)

        response = JsonResponse({'code': 0, 'message': 'OK'})

        # 设置cookie
        response.set_cookie(
            'username',
            user.username,
            max_age=14 * 24 * 3600,
        )

        # 增加代码:合并购物车数据
        cart_helper = CartHelper(request, response)
        cart_helper.merge_cookie_cart_to_redis()

        return response
コード例 #15
0
    def post(self, request):
        """ 注册用户信息保存 """
        # 1.获取参数并进行校验
        req_data = json.loads(request.body)
        username = req_data.get('username')
        password = req_data.get('password')
        password2 = req_data.get('password2')
        mobile = req_data.get('mobile')
        allow = req_data.get('allow')
        sms_code = req_data.get('sms_code')

        if not all([username, password, password2, mobile, allow, sms_code]):
            return JsonResponse({'code': 400,
                                 'message': '缺少必传参数'})

        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return JsonResponse({'code': 400,
                                 'message': 'username格式错误!'})
        if not re.match(r'^[a-zA-Z0-9]{8,20}$', password):
            return JsonResponse({'code': 400,
                                 'message': 'password格式错误!'})
        if password != password2:
            return JsonResponse({'code': 400,
                                 'message': '两次密码不一致!'})

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400,
                                 'message': '手机号格式错误!'})
        if not allow:
            return JsonResponse({'code': 400,
                                 'message': '请统一协议'})

        # 短信验证码检验
        redis_conn = get_redis_connection('verify_code')
        sms_code_redis = redis_conn.get('sms_%s' % mobile)

        if not sms_code_redis:
            return JsonResponse({'code': 400,
                                 'message': '短信验证码过期!'})

        if sms_code != sms_code_redis:
            return JsonResponse({'code': 400,
                                 'message': '短信验证码错误!'})

        # 保存新增用户数据到数据库
        try:
            user = User.objects.create_user(username=username,
                                            password=password,
                                            mobile=mobile)
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': 400,
                                 'message': '数据库保存错误!'})
        # 只要调用login方法,传入request和user对象
        # login方法就会将user用户的信息存储到session中
        login(request, user)

        # 返回响应
        response = JsonResponse({'code': 0,
                                 'message': 'OK'})

        # 设置cookie保存username用户名
        response.set_cookie('username',
                            user.username,
                            max_age=14 * 24 * 3600)

        # 增加代码:合并购物车数据
        cart_helper = CartHelper(request, response)
        cart_helper.merge_cookie_cart_to_redis()

        return response
コード例 #16
0
    def get(self, request):
        """订单结算页面"""
        user = request.user
        try:
            # ① 获取当前用户的收货地址信息
            addresses = Address.objects.filter(user=user, is_delete=False)

            # 地址信息
            address_li = []
            for address in addresses:
                address_li.append({
                    'id': address.id,
                    'province': address.province.name,
                    'city': address.city.name,
                    'district': address.district.name,
                    'place': address.place,
                    'receiver': address.receiver,
                    'mobile': address.mobile,
                })
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': '地址信息获取有误'})

        # ② 从redis中获取用户所要结算的商品信息
        try:
            cart_helper = CartHelper(request)
            cart_dict = cart_helper.get_redis_selected_cart()
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': '获取购物车数据失败'})

        # ③ 查询数据库获取对应的商品数据
        # 商品数据
        sku_li = []
        try:
            skus = SKU.objects.filter(id__in=cart_dict.keys())

            for sku in skus:
                sku_li.append({
                    'id': sku.id,
                    'name': sku.name,
                    'default_image_url': 'http://192.168.19.131:8888/' + sku.default_image.name,
                    'price': sku.price,
                    'count': cart_dict[sku.id]
                })
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': '获取商品数据失败'})

        # 订单运费
        freight = Decimal(10.00)

        # ④ 组织并返回响应数据
        context = {
            'addresses': address_li,
            'skus': sku_li,
            'freight': freight,
            'nowsite': user.default_address_id,
        }

        return JsonResponse({'code': 0,
                             'message': 'OK',
                             'context': context, })
コード例 #17
0
    def post(self, request):
        """订单创建"""
        user = request.user
        # ① 获取参数并进行校验
        req_data = json.loads(request.body)
        address_id = req_data.get('address_id')
        pay_method = req_data.get('pay_method')

        if not all([address_id, pay_method]):
            return JsonResponse({'code': 400,
                                 'message': '缺少必传参数'})

        # 地址是否存在
        try:
            address = Address.objects.get(id=address_id, user=user)
        except Exception as e:
            return JsonResponse({'code': 400,
                                 'message': '地址信息有误'})

        # 1:货到付款 2:支付宝
        if pay_method not in (1, 2):
            return JsonResponse({'code': 400,
                                 'message': '支付方式有误'})

        # ② 组织订单数据
        # 生成订单id
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + '%09d' % user.id

        # total_count和total_amount
        total_count = 0
        total_amount = 0

        # 订单状态
        if pay_method == 1:
            # 货到付款:待发货
            status = 2
        else:
            # 支付宝:待支付
            status = 1

        # 运费(此处固定)
        freight = Decimal(10.00)

        with transaction.atomic():
            # 设置数据库操作时,事务中的保存点
            sid = transaction.savepoint()
            # ③ 向 tb_order_info 数据表中添加一行记录
            order = OrderInfo.objects.create(order_id=order_id,
                                             user=user,
                                             address=address,
                                             total_count=total_count,
                                             total_amount=total_amount,
                                             freight=freight,
                                             pay_method=pay_method,
                                             status=status)

            # ④ 遍历用户要购买的商品记录,循环向 tb_order_goods 表中添加记录
            # 从 redis 中获取用户要购买的商品信息
            cart_helper = CartHelper(request)
            cart_dict = cart_helper.get_redis_selected_cart()
            sku_ids = cart_dict.keys()

            for sku_id in sku_ids:
                for i in range(3):
                    sku = SKU.objects.get(id=sku_id)
                    count = cart_dict[sku_id]

                    # 记录原始库存和销量
                    origin_stock = sku.stock
                    origin_sales = sku.sales

                    # 打印下单提示信息
                    print('下单用户:%s 商品库存:%d 购买数量:%d 尝试第 %d 次' % (
                        user.username, sku.stock, count, i + 1))

                    # 判断库存是否充足
                    if count > sku.stock:
                        # 数据库操作时,撤销事务中指定保存点之后的操作
                        transaction.savepoint_rollback(sid)
                        return JsonResponse({'code': 400,
                                             'message': '商品库存不足'})

                    # 进行休眠操作,让 CPU 调度其它进程或线程,模拟订单并发问题
                    # import time
                    # time.sleep(10)

                    # 更新商品库存和销量
                    new_stock = origin_stock - count
                    news_sales = origin_sales + count

                    # 注意:update 方法返回的是被更新的行数
                    # update tb_sku set stock=<new_stock>, sales=<news_sales>
                    # where id=<sku_id> and stock=<origin_stock>;
                    res = SKU.objects.filter(id=sku.id,
                                             stock=origin_stock).update(stock=new_stock,
                                                                        sales=news_sales)
                    if res == 0:
                        if i == 2:
                            # 尝试下单更新了 3 次,仍然失败,报下单失败错误
                            # 数据库操作时,撤销事务中指定保存点之后的操作
                            transaction.savepoint_rollback(sid)
                            return JsonResponse({'code': 400,
                                                 'message': '下单失败!'})
                        # 更新失败,重新进行尝试
                        continue

                    # 增加对应SPU商品的销量
                    sku.spu.sales += count
                    sku.spu.save()

                    # 保存订单商品信息
                    OrderGoods.objects.create(order=order,
                                              sku=sku,
                                              count=count,
                                              price=sku.price, )

                    # 累加计算订单商品的总数量和总价格
                    total_count += count
                    total_amount += count * sku.price

                    # 更新成功,跳出循环
                    break

                total_amount += freight
                order.total_count = total_count
                order.total_amount = total_amount
                order.save()

        # ⑤ 清除用户购物车中已购买的记录
        cart_helper.clear_redis_selected_cart()

        # ⑥ 返回响应
        return JsonResponse({'code': 0,
                             'message': '下单成功',
                             'order_id': order_id})