def get(self,request,mobile): # 接收 uuid=request.GET.get('image_code_id') image_code=request.GET.get('image_code') # 进行sms_code_flage验证 redis_cli = get_redis_connection('sms_code') if redis_cli.get(mobile+'_flage') is not None: return http.JsonResponse({ 'code':RETCODE.SMSCODERR, 'errmsg':'操作频繁,稍后再试' }) # 验证:非空,图形验证码是否正确 if not all([uuid,image_code]): return http.JsonResponse({ 'code':RETCODE.PARAMERR, 'errmsg':'参数不完整' }) redis_cli = get_redis_connection('image_code') image_code_redis=redis_cli.get(uuid) if image_code_redis is None: return http.JsonResponse({ 'code':RETCODE.IMAGECODEERR, 'errmsg':'图形验证码过期,点击刷新' }) redis_cli.delete(uuid) # 存储在Redis中的数据取出后为二进制数据,不能直接与接收的String类型数据进行对比, # 因此要对Redis取出的数据解码操作 .decode(), # 以及对两边的字符串同时转换为同样的大小写形式 if image_code.lower()!=image_code_redis.decode().lower(): return http.JsonResponse({ 'code':RETCODE.IMAGECODEERR, 'errmsg':'图形验证码错误' }) # 处理:随机生成6位数 sms_code='%06d'%random.randint(0,999999) # 存入Redis redis_cli=get_redis_connection('sms_code') # redis_cli.setex(mobile,constants.SMS_CODE_EXPIRES,sms_code) # 写发送标记,为了防止短信验证码频发发送,前端的倒计时对于刷新网页的操作无法防止 # 因此在后端进行验证 # redis_cli.setex(mobile+'_flage',constants.SMS_CODE_FLAGE_EXPIRES,1) # 优化 # 当需要向Redis写入多条数据时,会与其进行多次交互:效率低 # 解决:使用 管道 pipeline # 实现:先将命令存入管道,再一次性发给redis执行 redis_pl = redis_cli.pipeline() redis_pl.setex(mobile,constants.SMS_CODE_EXPIRES,sms_code) redis_pl.setex(mobile+'_flage',constants.SMS_CODE_FLAGE_EXPIRES,1) redis_pl.execute() # 发送短信 print(sms_code) # 响应 return http.JsonResponse({ 'code':RETCODE.OK, 'errmsg':'OK' })
def get(self, request, mobile): """ :param mobile: 接收短信的手机号 :return: """ # 每次发短信验证码之前先拿当前要发短信的手机号获取redis的短信标记,如果没有标记就发,有标记提前响应 # 创建redis连接对象 redis_conn = get_redis_connection('verify_code') send_flag = redis_conn.get('send_flag_%s' % mobile) # 校验标记 if send_flag: return http.JsonResponse({ 'code': RETCODE.THROTTLINGERR, 'errmsg': '频繁发送短信' }) # 接收到前端传入的mobile, image_code, uuid image_code_client = request.GET.get('image_code') uuid = request.GET.get('uuid') # 判断参数是否齐全 if all([image_code_client, uuid]) is False: return http.JsonResponse({ 'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必传参数' }) # 根据uuid作为key 获取到redis中当前用户的图形验证值 image_code_server = redis_conn.get('img_%s' % uuid) # 删除图形验证码,避免恶意测试图形验证码 redis_conn.delete('img_%s' % uuid) # 从redis中取出的数据都是byte类型,需要decode解码 # 判断用户写的图形验证码和redis存的是否一致 if image_code_client is None or image_code_client.lower( ) != image_code_server.decode().lower(): return http.JsonResponse({ 'code': RETCODE.IMAGECODEERR, 'errmsg': '验证码错误' }) # 发送短信 # 利用随机数生成一个6位数 sms_code = '%06d' % randint(0, 999999) logger.info(sms_code) # 创建Redis管道 pl = redis_conn.pipeline() # 将Redis请求添加到队列 # 将生成好的短信验证码也存储到redis,以备后期校验 # redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code) pl.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code) # 手机发送验证码后在redis设置一个标记 计时60秒,防止刷新重发 # redis_conn.setex('send_flag_%s' % mobile, 60, 1) pl.setex('send_flag_%s' % mobile, 60, 1) # 执行请求 pl.execute() # # 利用SDK容联云发短信 # CCP().send_template_sms(手机号, [验证码, 提示用户验证码有效期为多少分钟], 短信模版ID) # CCP().send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID) # 需要把CCP这行代码先加入到一个指定的仓库中,后续在单独的一个线程 进程去异步执行, 不在当下去执行 # 由生产者 往经纪人里面存任务 send_sms_code.delay(mobile, sms_code) # 响应 return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '短信发送成功'})
def post(self, request): #获取参数 json_data = request.body.decode() data = json.loads(json_data) sku_id = data.get("sku_id") sku_count = data.get("count") selected = data.get("selected", True) specs = data.get("specs") spec = [key + ':' + value for key, value in specs.items()] #验证参数 #判断参数是否齐全 if not all([sku_id, sku_count]): return http.HttpResponseForbidden("参数不全") #判断sku是否存在 try: Sku.objects.get(id=sku_id) except Sku.DoesNotExist: return http.HttpResponseForbidden("商品不存在") #判断selected是否为bool if selected: if not isinstance(selected, bool): return http.HttpResponseForbidden("参数有误") user = request.user #判断用户是否登录 if user.is_authenticated: #已登录加入redis4号库 ''' user_id:{ sku_id1:{ count:0, } sku_id2:{ count:0, } } selected:[sku_id1,sku_id2] ''' redis_conn = get_redis_connection("carts") pipeline = redis_conn.pipeline() pipeline.hincrby("cart_user_%s" % user.id, sku_id, sku_count) pipeline.hset("spec_user_%s" % user.id, sku_id, str(spec)) # pipeline.set("cart_spec_%s" % sku_id, str(spec)) if selected: pipeline.sadd("cart_selected_%s" % user.id, sku_id) pipeline.execute() # 响应结果 return http.JsonResponse({'code': 'ok', 'errmsg': '添加购物车成功'}) else: #未登录加入cookies中 ''' { sku_id1:{ count:1, selected:True }, sku_id2:{ count:1, selected:True } } ''' #获取carts的cookies值 carts_str = request.COOKIES.get("carts") if carts_str: #存在把cookie值转换为字典 carts_dict = pickle.loads(base64.b64decode(carts_str.encode())) else: #不存在设置cart_dict为空 carts_dict = {} #从购物车中获取sku cart_sku = carts_dict.get(sku_id) if cart_sku: #购物车存在这sku则将加数量 carts_dict[sku_id]['count'] += sku_count # carts_dict[sku_id]['selected'] = selected # carts_dict[sku_id]['selected'] = selected else: #不存在则添加sku carts_dict[sku_id] = { "count": sku_count, "selected": selected, "spec": spec } #将字典转换成base64的字符串 carts = base64.b64encode(pickle.dumps(carts_dict)).decode() #构造响应 response = http.JsonResponse({'code': 'ok', 'errmsg': "添加购物车成功"}) response.set_cookie('carts', carts, max_age=constants.CARTS_COOKIE_EXPIRES) return response
def post(self, request): """实现地址新增逻辑""" #判断是否超过地址上限:最多20个 user = request.user count = Address.objects.filter(user=user, is_deleted=False).count() if count >= 20: return http.JsonResponse({ 'code': RETCODE.MAXNUM, 'errmsg': '收货地址超限' }) #接收参数 json_dict = json.loads(request.body.decode()) title = json_dict.get('title') receiver = json_dict.get('receiver') province_id = json_dict.get('province_id') city_id = json_dict.get('city_id') district_id = json_dict.get('district_id') place = json_dict.get('place') mobile = json_dict.get('mobile') tel = json_dict.get('tel') email = json_dict.get('email') #校验参数 if all([ title, receiver, province_id, city_id, district_id, place, mobile ]) is False: return http.HttpResponseForbidden('缺少必传参数') if not re.match(r'^1[3-9]\d{9}$', mobile): return http.HttpResponseForbidden('参数mobile有误') if tel: if not re.match( r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel): return http.HttpResponseForbidden('参数tel有误') if email: if not re.match( r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email): return http.HttpResponseForbidden('参数email有误') #保存地址信息 try: address = Address.objects.create(user=user, title=title, receiver=receiver, province_id=province_id, city_id=city_id, district_id=district_id, place=place, mobile=mobile, tel=tel, email=email) except DatabaseError as e: logger.error(e) return http.HttpResponseForbidden('添加收货地址失败') #设置默认地址 if user.default_address is None: user.default_address = address user.save() #新增地址成功,将新增地址响应给前段实现局部刷新 # 把新增的address模型对象转换成字典,并响应给前端 address_dict = { 'id': address.id, 'title': address.title, 'receiver': address.receiver, 'province_id': address.province_id, 'province': address.province.name, 'city_id': address.city_id, 'city': address.city.name, 'district_id': address.district_id, 'district': address.district.name, 'place': address.place, 'mobile': address.mobile, 'tel': address.tel, 'email': address.email, } #响应保存结果 return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': '添加收货地址成功', 'address': address_dict })
def post(self,request): """美多商城用户绑定到openid""" # 1.接收参数 json_dict = json.loads(request.body.decode()) mobile = json_dict.get('mobile') password = json_dict.get('password') sms_code_client = json_dict.get('sms_code') access_token = json_dict.get('access_token') # 2.校验参数 # 判断参数是否齐全 # if not all([mobile,password,sms_code_client,access_token]): # return http.JsonResponse({'code':400, 'errmsg':'缺少必传参数'}) if not all([mobile, password, sms_code_client, access_token]): return http.JsonResponse({'code': 400, 'errmsg': '缺少必传参数'}) # 判断手机号是否合法 if not re.match(r'^1[3-9]\d{9}$',mobile): return http.JsonResponse({'code': 400, 'errmsg':'请输入正确的手机号码'}) # 判断密码是否合格 if not re.match(r'^[0-9a-zA-Z]{8,20}$',password): return http.JsonResponse({'code': 400, 'errmsg': '请输入8-20位的密码'}) # 3.判断短信验证码是否一致 # 创建 redis 链接对象: redis_conn = get_redis_connection('verify_code') # 从 redis 中获取 sms_code 值: sms_code_server = redis_conn.get('sms_%s' % mobile) # 判断获取出来的有没有: if not sms_code_server: # 如果没有, 直接返回: return http.JsonResponse({'code': 400, 'errmsg': '短信验证码失效'}) # 如果有, 则进行判断: if sms_code_client != sms_code_server.decode(): # 如果不匹配, 则直接返回: return http.JsonResponse({'code': 400, 'errmsg': '短信验证码有误'}) # 校验openid openid = check_access_token_openid(access_token) if not openid: return http.JsonResponse({'code': 400, 'errmsg': '参数openid有误'}) # 判断手机号对应的手机号是否存在 try: user = User.objects.get(mobile=mobile) except User.DoesNotExist: user = User.objects.create_user(username=mobile,password=password,mobile=mobile) else: if not user.check_password(password): return http.JsonResponse({'code': 400, 'errmsg': '密码有误'}) try: OAuthQQUser.objects.create(user=user,openid=openid) except Exception as e: return http.JsonResponse({'code': 400, 'errmsg': 'QQ登录失败'}) # 实现状态保持 login(request=request, user=user) response = http.JsonResponse({'code': 0, 'errmsg': 'OK'}) response.set_cookie('username', user.username, max_age=3600 * 24 * 14) # 响应结果 return response
def post(self, request): #1,获取参数 dict_data = json.loads(request.body.decode()) address_id = dict_data.get("address_id") pay_method = dict_data.get("pay_method") #2,校验参数 #2,1 为空校验 if not all([address_id, pay_method]): return http.JsonResponse(status=400) #2,2 地址是否存在 try: address = Address.objects.get(id=address_id) except Exception as e: return http.JsonResponse(status=400) #2,3 支付是否正确 if pay_method not in [ OrderInfo.PAY_METHODS_ENUM["CASH"], OrderInfo.PAY_METHODS_ENUM["ALIPAY"] ]: return http.JsonResponse(status=400) #3,数据入库 #3,1 订单编号 user = request.user order_id = timezone.now().strftime( "%Y%m%d%H%m%s") + "%06d%s" % (random.randint(0, 999999), user.id) #3,2 订单状态 if pay_method == OrderInfo.PAY_METHODS_ENUM["CASH"]: status = OrderInfo.ORDER_STATUS_ENUM["UNSEND"] #货到付款 else: status = OrderInfo.ORDER_STATUS_ENUM["UNPAID"] #待支付 #TODO 设置保存点 sid = transaction.savepoint() #3,3 创建订单信息对象,入库 order_info = OrderInfo.objects.create( order_id=order_id, user=user, address=address, total_count=0, total_amount=Decimal(0.0), freight=Decimal(10.0), pay_method=pay_method, status=status, ) #3,4 获取要结算的商品,并入库 redis_conn = get_redis_connection("cart") cart_dict = redis_conn.hgetall("cart_%s" % user.id) selected_list = redis_conn.smembers("selected_%s" % user.id) for sku_id in selected_list: while True: #4,1 获取商品,数量 sku = SKU.objects.get(id=sku_id) count = int(cart_dict.get(sku_id)) #4,2 判断库存 if count > sku.stock: #TODO 回滚 transaction.savepoint_rollback(sid) return http.JsonResponse({"errmsg": "库存不足"}) #TODO 模拟并发下单 import time time.sleep(5) #4,3 减少库存,增加销量 # sku.stock -= count # sku.sales += count # sku.save() #TODO 使用乐观锁解决并发下单 #4,3,1 准备数据新,老库存 old_stock = sku.stock old_sales = sku.sales new_stock = old_stock - count new_sales = old_sales + count #4,3,2 跟新数据 ret = SKU.objects.filter(id=sku_id, stock=old_stock).update( stock=new_stock, sales=new_sales) #4,3,3 判断是否更新成功 if ret == 0: # transaction.savepoint_rollback(sid) # return http.JsonResponse({"errmsg":"下单失败"}) continue #4,4 创建订单商品对象,入库 OrderGoods.objects.create( order=order_info, sku=sku, count=count, price=sku.price, ) #4,5 累加数量,价格 order_info.total_count += count order_info.total_amount += (count * sku.price) #一定要跳出 break #5,提交订单信息 order_info.save() transaction.savepoint_commit(sid) #TODO 提交事务 #6,清除redis数据 redis_conn.hdel("cart_%s" % user.id, *selected_list) redis_conn.srem("selected_%s" % user.id, *selected_list) #7,返回响应 return http.JsonResponse({"code": RET.OK, "order_id": order_id})
def get(self, request, mobile): """ :param request: :param mobile:手机号 :return: json """ # 接收参数 img_code_client = request.GET.get('image_code') # print(img_code_client) uuid = request.GET.get('uuid') # 校验参数 if not all([img_code_client, uuid]): return http.HttpResponseForbidden('缺少必要参数') # 创建链接redis的对象 redis_conn = get_redis_connection('verify_code') # 判断用户是否频繁发送短信验证码 send_flg = redis_conn.get('send_flg_%s' % mobile) if send_flg: # 已经发送过了 return http.JsonResponse({ 'code': RETCODE.THROTTLINGERR, 'errmsg': '发送短信过于频繁' }) # 提取图形验证码 img_code_server = redis_conn.get("img_%s" % uuid) # 这是一个bytes类型 if img_code_server is None: return http.JsonResponse({ 'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码已经失效' }) # 删除图形验证码 redis_conn.delete("img_%s" % uuid) # 对比图形验证码,将bytes类型转为字符串再比较 img_code_server = img_code_server.decode() if img_code_server.lower() != img_code_client.lower(): return http.JsonResponse({ 'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码有误' }) # 生产短信验证码 %06d代表,如果不够6位,前面补0 sms_code = '%06d' % random.randint(0, 999999) # 手动输出日志,记录短信验证码 logger.info(sms_code) # # 保存短信验证码 # redis_conn.setex("sms_%s" % mobile,constants.SMS_CODE_REDIS_EXPIRES,sms_code) # # 保存发送短信验证码的标记 # redis_conn.setex('send_flg_%s' % mobile,constants.SEND_SMS_CODE_INTERVAL,1) # 创建redis管道 pl = redis_conn.pipeline() # 将命令添加到队列中 # 保存短信验证码 pl.setex("sms_%s" % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code) # 保存发送短信验证码的标记 pl.setex('send_flg_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1) # 执行 pl.execute() # 发送短信验证码 CPP().send_message_sms( mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID) return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})
def put(self, request, address_id): ''' 接收前端传入的参数, 修改数据库数据, 返回 :param request: :param address_id: :return: ''' # 1.接收参数(json) json_dict = json.loads(request.body.decode()) receiver = json_dict.get('receiver') province_id = json_dict.get('province_id') city_id = json_dict.get('city_id') district_id = json_dict.get('district_id') place = json_dict.get('place') mobile = json_dict.get('mobile') tel = json_dict.get('tel') email = json_dict.get('email') # 2.校验(整体 + 单个) if not all( [receiver, province_id, city_id, district_id, place, mobile]): return http.HttpResponseForbidden('缺少必传参数') if not re.match(r'^1[3-9]\d{9}$', mobile): return http.HttpResponseForbidden('手机号格式不正确') if tel: if not re.match( r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel): return http.HttpResponseForbidden('固定电话格式不正确') if email: if not re.match( r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email): return http.HttpResponseForbidden('email格式不正确') # 3.修改数据库数据 try: result = Address.objects.filter(id=address_id).update( user=request.user, province_id=province_id, city_id=city_id, district_id=district_id, title=receiver, receiver=receiver, place=place, mobile=mobile, tel=tel, email=email) except Exception as e: return http.JsonResponse({ 'code': RETCODE.DBERR, 'errmsg': '修改数据库出错' }) # print(result) address = Address.objects.get(id=address_id) # 4.拼接参数, 准备返回 address_dict = { 'id': address.id, 'receiver': address.receiver, 'province': address.province.name, 'city': address.city.name, 'district': address.district.name, 'place': address.place, 'mobile': address.mobile, 'tel': address.tel, 'email': address.email } # 5.返回 return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'ok', 'address': address_dict })
def post(self, request): ''' 接收参数, 保存到mysql中, 新增地址 :param request: :return: ''' # 1.判断数据库中是否已经有20个地址了 # count = request.user.addresses.count() # count = request.user.addresses.model.objects.filter(is_deleted=False).count() count = request.user.addresses.filter(is_deleted=False).count() if count >= 20: return http.JsonResponse({ 'code': RETCODE.THROTTLINGERR, 'errmsg': '超过地址上限' }) # 2.接收参数 json_dict = json.loads(request.body.decode()) receiver = json_dict.get('receiver') province_id = json_dict.get('province_id') city_id = json_dict.get('city_id') district_id = json_dict.get('district_id') place = json_dict.get('place') mobile = json_dict.get('mobile') tel = json_dict.get('tel') email = json_dict.get('email') # 3.校验(整体 + 单个) if not all( [receiver, province_id, city_id, district_id, place, mobile]): return http.HttpResponseForbidden('缺少必传参数') if not re.match(r'', mobile): return http.HttpResponseForbidden('手机号不符合规定') if tel: if not re.match(r'', tel): return http.HttpResponseForbidden('固定电话不符合规定') if email: if not re.match(r'', email): return http.HttpResponseForbidden('email不符合规定') province = Area.objects.get(id=province_id) try: address = Address.objects.create(user=request.user, province=province, city_id=city_id, district_id=district_id, title=receiver, receiver=receiver, place=place, mobile=mobile, tel=tel, email=email) # 4.判断是否有默认地址, 如果没有, 把当前地址作为默认地址 if not request.user.default_address: request.user.default_address = address request.user.save() except Exception as e: return http.JsonResponse({ 'code': RETCODE.DBERR, 'errmsg': '保存地址出错' }) # 5.拼接参数, 准备返回 address_dict = { 'id': address.id, 'receiver': address.receiver, 'province': address.province.name, 'city': address.city.name, 'district': address.district.name, 'place': address.place, 'mobile': address.mobile, 'tel': address.tel, 'email': address.email } # 6.返回 return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'ok', 'address': address_dict })
def handle_no_permission(self): return http.JsonResponse({ 'code': RETCODE.SESSIONERR, 'errmsg': '您未登录' })
def remove(self): pks = request.POST.getlist('pk') bks = Bookmark.objects.filter(pk__in=pks, user=request.user) bks.delete() return http.JsonResponse(self.ajax_response)
def put(self, request, address_id): """修改地址""" # 接受请求体数据 json_dict = json.loads(request.body.decode()) title = json_dict.get('title') receiver = json_dict.get('receiver') province_id = json_dict.get('province_id') city_id = json_dict.get('city_id') district_id = json_dict.get('district_id') place = json_dict.get('place') mobile = json_dict.get('mobile') tel = json_dict.get('tel') email = json_dict.get('email') # 校验 if all([ title, receiver, province_id, city_id, district_id, place, mobile ]) is False: return http.HttpResponseForbidden('缺少必传参数') if not re.match(r'^1[3-9]\d{9}$', mobile): return http.HttpResponseForbidden('参数mobile有误') if tel: if not re.match( r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel): return http.HttpResponseForbidden('参数tel有误') if email: if not re.match( r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email): return http.HttpResponseForbidden('参数email有误') # 修改收货地址数据 try: Addresses.objects.filter(id=address_id).update( title=title, receiver=receiver, province_id=province_id, city_id=city_id, district_id=district_id, place=place, mobile=mobile, tel=tel, email=email) except Exception as e: logger.error(e) return http.JsonResponse({ 'code': RETCODE.PARAMERR, 'errmsg': '修改收货地址失败' }) # 获取到修改后的地址模型对象 address_model = Addresses.objects.get(id=address_id) address_dict = { 'id': address_model.id, 'title': address_model.title, "receiver": address_model.receiver, "province": address_model.province.name, "province_id": address_model.province.id, "city": address_model.city.name, "city_id": address_model.city.id, "district": address_model.district.name, "district_id": address_model.district.id, "place": address_model.place, "mobile": address_model.mobile, "tel": address_model.tel, "email": address_model.email } # 响应 return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'ok', 'address': address_dict })
def post(self, request): # 判断用户的收货地址是否上线 user = request.user # count = user.address.filter(is_deleted=False).count() count = Addresses.objects.filter(user=user, is_deleted=False).count() if count > 20: return http.JsonResponse({ 'code': RETCODE.THROTTLINGERR, 'errmsg': '收货地址超过上限' }) # 接受请求体数据 json_dict = json.loads(request.body.decode()) title = json_dict.get('title') receiver = json_dict.get('receiver') province_id = json_dict.get('province_id') city_id = json_dict.get('city_id') district_id = json_dict.get('district_id') place = json_dict.get('place') mobile = json_dict.get('mobile') tel = json_dict.get('tel') email = json_dict.get('email') # 校验 if all([ title, receiver, province_id, city_id, district_id, place, mobile ]) is False: return http.HttpResponseForbidden('缺少必传参数') if not re.match(r'^1[3-9]\d{9}$', mobile): return http.HttpResponseForbidden('参数mobile有误') if tel: if not re.match( r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel): return http.HttpResponseForbidden('参数tel有误') if email: if not re.match( r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email): return http.HttpResponseForbidden('参数email有误') # 保存收货地址数据 try: address_model = Addresses.objects.create(user=request.user, title=title, receiver=receiver, province_id=province_id, city_id=city_id, district_id=district_id, place=place, mobile=mobile, tel=tel, email=email) # 如果当前用户还没有默认收货地址,就把当前新增的这个收货地址设置为它的默认地址 if not user.default_address: user.default_address = address_model user.save() except Exception as e: logger.error(e) return http.JsonResponse({ 'code': RETCODE.PARAMERR, 'errmsg': '新增收货地址失败' }) # 把保存好的模型对象转换成字段,再响应给前端 address_dict = { 'id': address_model.id, 'title': address_model.title, "receiver": address_model.receiver, "province": address_model.province.name, "province_id": address_model.province.id, "city": address_model.city.name, "city_id": address_model.city.id, "district": address_model.district.name, "district_id": address_model.district.id, "place": address_model.place, "mobile": address_model.mobile, "tel": address_model.tel, "email": address_model.email } # 响应 return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address': address_dict })
def get(self, request, mobile): count = User.objects.filter(mobile=mobile).count() if count != 0: return http.HttpResponseForbidden('手机号已存在') response_data = {'count': count, 'code': RETCODE.OK, 'errmsg': 'OK'} return http.JsonResponse(response_data)
def get(self, request): # 判断当前是查询省份数据还是市区数据 area_id = request.GET.get('area_id') if not area_id: # 读取省份缓存数据 province_list = cache.get('province_list') if not province_list: # 查询省份数据 try: province_model_list = models.Area.objects.filter( parent__isnull=True) province_list = [] for province_model in province_model_list: province_dict = { 'id': province_model.id, 'name': province_model.name } province_list.append(province_dict) # 缓存省份字典列表数据:默认存储到名为'default'的配置中 cache.set('province_list', province_list, 3600) except Exception as e: logger.error(e) return http.JsonResponse({ 'code': RETCODE.DBERR, 'errmsg': '查询省份数据错误' }) # 响应省级JSON数据 return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'OK', 'province_list': province_list }) else: # 判断是否有缓存 sub_data = cache.get('sub_area_' + area_id) if not sub_data: # 查询城市或区县数据 try: parent_model = models.Area.objects.get(id=area_id) # model_list = parent_model.area_set.all() sub_model_list = parent_model.subs.all() subs = [] for sub_model in sub_model_list: sub_dict = { 'id': sub_model.id, 'name': sub_model.name, } subs.append(sub_dict) sub_data = { 'id': parent_model.id, 'name': parent_model.name, 'subs': subs } # 缓存城市或者区县 cache.set('sub_area_' + area_id, sub_data, 3600) except Exception as e: logger.error(e) return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': '查询城市或区县数据错误' }) return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'OK', 'sub_data': sub_data })
def post(self, request): json_dict = json.loads(request.body.decode()) sku_id = json_dict.get('sku_id') count = json_dict.get('count') selected = json_dict.get('selected', True) try: sku = SKU.objects.get(id=sku_id) except Exception as e: return http.HttpResponseForbidden("商品不村咋") try: count = int(count) except: return http.HttpResponseForbidden('COUNT不是整形') if selected: if not isinstance(selected, bool): return http.HttpResponseForbidden('selected不是bool类型') user = request.user response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加购物车成功'}) if user.is_authenticated: redis_carts_client = get_redis_connection('carts') client_data = redis_carts_client.hgetall(user.id) # print('哦按段之前',client_data) if str(sku_id).encode() in client_data: # print(client_data) bytes_carts = client_data[str(sku_id).encode()] str_carts = bytes_carts.decode() dict_carts = json.loads(str_carts) dict_carts['count'] += count redis_carts_client.hset(user.id, sku_id, json.dumps(dict_carts)) else: redis_carts_client.hset( user.id, sku_id, json.dumps({ 'count': count, "selected": selected })) else: #weidenglu #获取cookie中所有的数据 cookie_str = request.COOKIES.get('carts') if cookie_str: carts_dict = CookieSecret.loads(cookie_str) else: carts_dict = {} #解密 # carts_dict = CookieSecret.loads(cookie_str) # 判断是否存在 if sku_id in carts_dict: # carts_dict[sku_id]['count'] += count origi_count = carts_dict[sku_id]['count'] count += origi_count carts_dict[sku_id] = {'count': count, 'selected': selected} dumps_str = CookieSecret.dumps(carts_dict) response.set_cookie('carts', dumps_str, max_age=14 * 24 * 3600) #存在 累加 # 不存在新增 return response
def put(self, request): # 修改 # 接收、验证 json_dict = json.loads(request.body.decode()) sku_id = json_dict.get('sku_id') count = json_dict.get('count') selected = json_dict.get('selected') # 验证 if not all([sku_id, count]): return http.JsonResponse({ 'code': RETCODE.PARAMERR, 'errmsg': '参数不完整' }) # sku_id有效 try: sku = SKU.objects.get(pk=sku_id) except: return http.JsonResponse({ 'code': RETCODE.PARAMERR, 'errmsg': '商品编号无效' }) # 数量 count = int(count) if count > 5: return http.JsonResponse({ 'code': RETCODE.PARAMERR, 'errmsg': '购买数量不能超过5个' }) elif count < 1: return http.JsonResponse({ 'code': RETCODE.PARAMERR, 'errmsg': '购买数量不能少于1个' }) elif count > sku.stock: return http.JsonResponse({ 'code': RETCODE.PARAMERR, 'errmsg': '商品库存不足' }) # 选中状态 # if selected == 'true': # selected = True # else: # selected = False # 处理、响应 response = http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'OK', 'cart_sku': { 'id': sku.id, 'selected': str(selected), 'default_image_url': sku.default_image.url, 'name': sku.name, 'price': str(sku.price), 'count': count, 'total_amount': str(count * sku.price) } }) if request.user.is_authenticated: # 登录状态,将购物车存入redis redis_cli = get_redis_connection('cart') # 向hash中存商品编号、数量 redis_cli.hset('cart%d' % request.user.id, sku_id, count) # 向set中存商品编号,表示选中此商品 if selected: redis_cli.sadd('selected%d' % request.user.id, sku_id) else: redis_cli.srem('selected%d' % request.user.id, sku_id) else: # 未登录,存入cookie # 1.读取cookie中的购物车数据 cart_str = request.COOKIES.get('cart') if cart_str: # 如果购物车数据已经存在,则转字典 cart_dict = meiduo_json.loads(cart_str) else: # 如果第一次加入购物车数据,则新建字典 cart_dict = {} # 2.向字典中添加或修改数据 # cart_dict = { # sku_id: { # 'count': count, # 'selected': True # } # } cart_dict[sku_id] = {'count': count, 'selected': selected} # 3.写cookie cart_str = meiduo_json.dumps(cart_dict) # cart_str = json.dumps(cart_dict) response.set_cookie('cart', cart_str, max_age=constants.CART_COOKIE_EXPIRES) return response
def get(self, request): area_id = request.GET.get('area_id') if not area_id: province_list = cache.get('province_list') if not province_list: try: # 没有area_id,代表是省份 province_model_list = Area.objects.filter( parent_id__isnull=True) province_list = [] for province in province_model_list: province_dict = { 'id': province.pk, 'name': province.name } province_list.append(province_dict) cache.set('province_list', province_list, 3600) except Exception as e: logger.error(e) return http.JsonResponse({ 'code': RETCODE.DBERR, 'errmsg': '查询省份数据失败' }) return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'ok', 'province_list': province_list }) else: sub_data = cache.get('sub_area_' + area_id) if not sub_data: try: # 没有area_id,代表是省份 parent_area = Area.objects.get(pk=area_id) sub_area_model = parent_area.subs.all() subs = [] sub_data = { 'id': parent_area.pk, 'name': parent_area.name, 'subs': subs } for sub in sub_area_model: subs.append({'id': sub.pk, 'name': sub.name}) cache.set('sub_area_' + area_id, sub_data, 3600) except Exception as e: logger.error(e) return http.JsonResponse({ 'code': RETCODE.DBERR, 'errmsg': '查询市区数据失败' }) return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'ok', 'sub_data': sub_data })
def model_wrapper(request, model_name): if model_name in BLACKLIST: raise http.Http404("Don't know what you're talking about!") for source in MODELS_MODULES: try: model = getattr(source, model_name) break except AttributeError: pass else: raise http.Http404('no service called `%s`' % model_name) if not is_valid_model_class(model): raise http.Http404('no service called `%s`' % model_name) required_permissions = getattr(model(), 'API_REQUIRED_PERMISSIONS', None) if isinstance(required_permissions, basestring): required_permissions = [required_permissions] if ( required_permissions and ( not request.user.is_active or not has_permissions(request.user, required_permissions) ) ): permission_names = [] for permission in required_permissions: codename = permission.split('.', 1)[1] try: permission_names.append( Permission.objects.get( codename=codename ).name ) except Permission.DoesNotExist: permission_names.append(codename) # you're not allowed to use this model return http.JsonResponse({ 'error': "Use of this endpoint requires the '%s' permission" % ( ', '.join(permission_names), ) }, status=403) # it being set to None means it's been deliberately disabled if getattr(model, 'API_WHITELIST', False) is False: raise APIWhitelistError('No API_WHITELIST defined for %r' % model) instance = model() # Any additional headers we intend to set on the response headers = {} # Certain models need to know who the user is to be able to # internally use that to determine its output. instance.api_user = request.user if request.method == 'POST': function = instance.post else: function = instance.get if not function: return http.HttpResponseNotAllowed([request.method]) # assume first that it won't need a binary response binary_response = False request_data = request.method == 'GET' and request.GET or request.POST form = FormWrapper(model, request_data) if form.is_valid(): try: result = function(**form.cleaned_data) except ValueError as e: if ( # built in json module ValueError 'No JSON object could be decoded' in e or # ujson module ValueError 'Expected object or value' in e ): return http.HttpResponseBadRequest( json.dumps({'error': 'Not a valid JSON response'}), content_type='application/json; charset=UTF-8' ) raise except NOT_FOUND_EXCEPTIONS as exception: return http.HttpResponseNotFound( json.dumps({'error': unicode(exception)}), content_type='application/json; charset=UTF-8' ) except BAD_REQUEST_EXCEPTIONS as exception: return http.HttpResponseBadRequest( json.dumps({'error': unicode(exception)}), content_type='application/json; charset=UTF-8' ) # Some models allows to return a binary reponse. It does so based on # the models `BINARY_RESPONSE` dict in which all keys and values # need to be in the valid query. For example, if the query is # `?foo=bar&other=thing&bar=baz` and the `BINARY_RESPONSE` dict is # exactly: {'foo': 'bar', 'bar': 'baz'} it will return a binary # response with content type `application/octet-stream`. for key, value in model.API_BINARY_RESPONSE.items(): if form.cleaned_data.get(key) == value: binary_response = True else: binary_response = False break if binary_response: # if you don't have all required permissions, you'll get a 403 required_permissions = model.API_BINARY_PERMISSIONS if isinstance(required_permissions, basestring): required_permissions = [required_permissions] if ( required_permissions and not has_permissions(request.user, required_permissions) ): permission_names = [] for permission in required_permissions: codename = permission.split('.', 1)[1] try: permission_names.append( Permission.objects.get( codename=codename ).name ) except Permission.DoesNotExist: permission_names.append(codename) # you're not allowed to get the binary response return http.HttpResponseForbidden( "Binary response requires the '%s' permission\n" % (', '.join(permission_names)) ) elif not request.user.has_perm('crashstats.view_pii'): clean_scrub = getattr(model, 'API_CLEAN_SCRUB', None) if callable(model.API_WHITELIST): whitelist = model.API_WHITELIST() else: whitelist = model.API_WHITELIST if result and whitelist: cleaner = Cleaner( whitelist, clean_scrub=clean_scrub, # if True, uses warnings.warn() to show fields # not whitelisted debug=settings.DEBUG, ) cleaner.start(result) else: # custom override of the status code return {'errors': dict(form.errors)}, 400 if binary_response: assert model.API_BINARY_FILENAME, 'No API_BINARY_FILENAME set on model' response = http.HttpResponse( result, content_type='application/octet-stream' ) filename = model.API_BINARY_FILENAME % form.cleaned_data response['Content-Disposition'] = ( 'attachment; filename="%s"' % filename ) return response if ( getattr(model, 'deprecation_warning', False) ): if isinstance(result, dict): result['DEPRECATION_WARNING'] = model.deprecation_warning # If you return a tuple of two dicts, the second one becomes # the extra headers. # return result, { headers['DEPRECATION-WARNING'] = ( model.deprecation_warning.replace('\n', ' ') ) if model.cache_seconds: # We can set a Cache-Control header. # We say 'private' because the content can depend on the user # and we don't want the response to be collected in HTTP proxies # by mistake. headers['Cache-Control'] = 'private, max-age={}'.format( model.cache_seconds, ) return result, headers
def ___jsonresponse___list___tree___(request, ___utils___module___, ___utils___module_model___): try: pk = int(request.GET.get('pk')) int___parent = int(request.GET.get('parent')) int___position = int(request.GET.get('position')) except ValueError: messages.add_message(request, messages.ERROR, _('APPLICATION___SECURITY___MESSAGE ERROR.')) return ___jsonresponse___error___(request=request) dict___data = dict() instance = ___utils___module_model___.___MODEL___.objects.___instance___(request=request, pk=pk) if instance is None: messages.add_message(request, messages.ERROR, _('APPLICATION___SECURITY___MESSAGE ERROR.')) return ___jsonresponse___error___(request=request) if int___parent < 0: messages.add_message(request, messages.ERROR, _('APPLICATION___SECURITY___MESSAGE ERROR.')) return ___jsonresponse___error___(request=request) instance_parent = None if 0 < int___parent: # when int___parent==0: this instance have parent==null in the data base instance_parent = ___utils___module_model___.___MODEL___.objects.___instance___(request=request, pk=int___parent) if instance_parent is None: messages.add_message(request, messages.ERROR, _('APPLICATION___SECURITY___MESSAGE ERROR.')) return ___jsonresponse___error___(request=request) instances = ___utils___module_model___.___MODEL___.objects.___instances___(request=request) if not (0 < int___position <= len(instances)): messages.add_message(request, messages.ERROR, _('APPLICATION___SECURITY___MESSAGE ERROR.')) return ___jsonresponse___error___(request=request) # get parents of instance list_int___parent = [] temporal_int___parent = instance.parent while temporal_int___parent != 0: list_int___parent.append(temporal_int___parent) temporal_instance___parent = ___utils___module_model___.___MODEL___.objects.___instance___(request=request, pk=temporal_int___parent) temporal_int___parent = temporal_instance___parent.parent list_int___parent.append(0) # count children of the instance int___children_amount = 0 for temporal_instance in instances[instance.position:]: if temporal_instance.parent in list_int___parent: break int___children_amount += 1 # change positions if instance.position < int___position: for temporal_instance in instances[instance.position + int___children_amount:int___position + int___children_amount]: temporal_instance.position -= (1 + int___children_amount) temporal_instance.save() instances[instance.position - 1].parent = int___parent temporal_int___position = int___position for temporal_instance in instances[instance.position - 1:instance.position + int___children_amount]: temporal_instance.position = temporal_int___position temporal_int___position += 1 temporal_instance.save() elif int___position < instance.position: instances[instance.position - 1].parent = int___parent temporal_int___position = int___position for temporal_instance in instances[instance.position - 1:instance.position + int___children_amount]: temporal_instance.position = temporal_int___position temporal_int___position += 1 temporal_instance.save() for temporal_instance in instances[int___position - 1:instance.position - 1]: temporal_instance.position += (int___children_amount + 1) temporal_instance.save() else: # instance.position == position instances[instance.position - 1].parent = int___parent instances[instance.position - 1].save() # change is_active if 0 < int___parent: # when int___parent==0: this instance have parent==null in the data base if instance_parent.is_active is False and instance.is_active is True: instances = ___utils___module_model___.___MODEL___.objects.___instances___(request=request) for temporal_instance in instances[int___position - 1:int___position + int___children_amount]: temporal_instance.is_active = False temporal_instance.save() dict___data['LOCALE___is_active'] = dict() dict___data['LOCALE___is_active']['option_no'] = '%s' % (_('APPLICATION___ADMINISTRATION___CONTENT___OPTION_NO'),) # if hasattr(___utils___module_model___, '___void___list___tree___'): instance = ___utils___module_model___.___MODEL___.objects.___instance___(request=request, pk=pk) ___utils___module_model___.___void___list___tree___(request=request, dict___data=dict___data, instance=instance) # messages.add_message(request, messages.SUCCESS, _('APPLICATION___ADMINISTRATION___CONTENT___MESSAGE %(instance)s was successfully updated.') % {'instance': instance, }) dict___data['___HTML___APPLICATION___ADMINISTRATION___MODAL___'] = ___html___template_modal___message___(request=request) dict___data['___HTML___APPLICATION___ADMINISTRATION___MODAL___MESSAGE___'] = ___html___template_message___(request=request) dict___data['___BOOLEAN___ERROR___'] = False return http.JsonResponse(dict___data)
def post(self, request): """保存订单信息和订单商品信息""" # 获取当前要保存的订单信息 user = request.user json_dict = json.loads(request.body.decode()) address_id = json_dict.get('address_id') pay_method = json_dict.get('pay_method') # 校验 if all([address_id, pay_method]) is False: return http.HttpResponseForbidden('缺少必传参数') try: address = Address.objects.get(id=address_id) except Address.DoesNotExist: return http.HttpResponseForbidden('address不存在') if int(pay_method) not in OrderInfo.PAY_METHODS_ENUM.values(): return http.HttpResponseForbidden('付款方式有误') # 生成订单编号 年月日时分秒+user.id order_id = timezone.now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id) # 订单状态 status = OrderInfo.ORDER_STATUS_ENUM['UNPAID'] \ if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] \ else OrderInfo.ORDER_STATUS_ENUM['UNCOMMENT'] # 开启事物 with transaction.atomic(): # 创建保存点 save_id = transaction.savepoint() # 暴力回滚 try: # 保存生成订单 order = OrderInfo.objects.create( user = user, order_id = order_id, address = address, total_count = 0, total_amount = Decimal('0.00'), freight = Decimal('10.00'), pay_method = pay_method, status=status ) # 连接redis服务器 redis_coon = get_redis_connection('carts') # 获取hash 和 set 中所有数据 redis_cart = redis_coon.hgetall('carts_%s' % user.id) cart_selected = redis_coon.smembers('selected_%s' % user.id) cart_dict = {} # 遍历set把要购买的sku_id和count包装到一个新字典中 组建新的已勾选{sku_id:count} 字典 for sku_id_bytes in cart_selected: cart_dict[int(sku_id_bytes)] = int(redis_cart[sku_id_bytes]) # 遍历用来包装所有要购买商品的字典 for sku_id in cart_dict.keys(): while True: # 通过sku_id获取到sku模型 sku = SKU.objects.get(id=sku_id) # 获取当前商品要购买的数量 buy_count = cart_dict[sku_id] # 获取当前商品的库存和销量 origin_stock = sku.stock origin_sales = sku.sales # 判断库存 if buy_count > origin_stock: return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '库存不足'}) # 对数据库中库存和销量进行计算 并修改 new_stock = origin_stock - buy_count new_sales = origin_sales + buy_count # sku.stock = new_stock # sku.sales = new_sales # sku.save() result = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock, sales=new_sales) # 如果下单失败,但是库存足够时,继续下单,直到下单成功或者库存不足为止 if result == 0: continue sku.spu.sales += buy_count sku.spu.save() # 存储订单商品记录 OrderGoods.objects.create( order = order, sku = sku, count = buy_count, price = sku.price ) order.total_count += buy_count order.total_amount += (order.total_count * sku.price) # 下单成功或失败跳出循环 break order.total_amount += order.freight order.save() except Exception as e: # 报错即回滚 transaction.rollback(save_id) # 订单提交成功,提交事物 transaction.savepoint_commit(save_id) # 删除已购买数据 pl = redis_coon.pipeline() pl.hdel('carts_%s' % user.id,*cart_selected) pl.delete('selected_%s' % user.id) pl.execute() # 响应 return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'order_id': order_id})
def put(self, request): """ 一 把需求写下来 (前端需要收集什么 后端需要做什么) 前端 收集修改的sku_id, count, selected传递给后端, 发送ajax请求 后端 从数据库/cookie里面更新数据,并且返回 二 把大体思路写下来(后端的大体思路) 1.接收数据 2.验证数据 3.获取用户的信息 4.登陆用户更新redis数据 4.1 连接redis 4.2 hash 4.3 set 4.4 返回相应 5.未登录更新cookie数据 5.1 获取cart数据,并判断 5.2 更新指定数据 5.3 对字典数据进行处理,并设置cookie 5.4 返回相应 三 把详细思路完善一下(纯后端) 1. 接收数据(sku_id, count, selected) 2. 验证数据 3. 获取用户的信息 4. 登录用户更新redis数据 hash set 5. 非登录用户更新cookie信息 carts: {sku_id1: {'count': xxx, 'selected: xxx}, sku_id2:{.........}} 6. 返回响应 四 确定我们请求方式和路由 PUT carts/ """ # 1.接收数据 json_dict = json.loads(request.body.decode()) sku_id = json_dict.get('sku_id') count = json_dict.get('count') selected = json_dict.get('selected') # 2.验证数据 # 2.1 是否有空值 if not all([sku_id, count]): return http.JsonResponse({ 'cdoe': RETCODE.NODATAERR, 'errmsg': '参数不齐全' }) # 2.2 判断商品是否存在 try: sku = SKU.objects.get(id=sku_id) except Exception as e: return http.JsonResponse({ 'cdoe': RETCODE.NODATAERR, 'errmsg': '商品不存在' }) # 2.3 判断传入的数量是否为数值 try: count = int(count) except Exception as e: return http.JsonResponse({ 'cdoe': RETCODE.NODATAERR, 'errmsg': '参数错误' }) # 3.获取用户的信息 if request.user.is_authenticated: # 4.登陆用户更新redis数据 # 4.1 连接redis redis_conn = get_redis_connection('carts') pl = redis_conn.pipeline() # 4.2 hash user_id sku_id: count pl.hset('carts_%s' % request.user.id, sku_id, count) pl.execute() # 4.3 set selected_user_id: selected_id # 这里的原则就是:如果勾选,则将sku_id加进集合(集合是无重复),如果没有勾选,则从集合中原有的进行删除 if selected: pl.sadd('selected_%s' % request.user.id, sku_id) else: # todo 这里原来就是未勾选怎么办?---------------------------------------------------- pl.srem('selected_%s' % request.user.id, sku_id) # 4.4 返回相应 cart_sku = { 'id': sku_id, 'count': count, 'selected': selected, 'default_image_url': sku.default_image.url, 'price': sku.price, 'amount': count * sku.price } return http.JsonResponse({ 'code': RETCODE, 'errmsg': 'ok', 'cart_sku': cart_sku }) # 5.未登录更新cookie数据 else: # 5.1 获取cart数据,并判断 cart_cookie = request.COOKIES.get('carts') # todo ========================================================================== print(type(cart_cookie)) # carts: sku_id: {'count': count, 'selected': xxx },......... # 判断cart_cookie是否为空,为空则初始化一个空字典, 不为空的话进行解码 if cart_cookie is None: carts = {} else: carts = pickle.loads(base64.b64decode(cart_cookie)) # 5.2 更新指定数据 if sku_id in carts: carts[sku_id] = {'count': count, 'selected': selected} # carts[sku_id]['count'] = count # carts[sku_id]['selected'] = selected en = base64.b64encode(pickle.dumps(carts)) # 5.3 对字典数据进行处理,并设置cookie cart_sku = { 'id': sku_id, 'count': count, 'selected': selected, 'name': sku.name, 'default_image_url': sku.default_image.url, 'price': sku.price, 'amount': sku.price * count, } response = http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': 'ok', 'cart_sku': cart_sku }) response.set_cookie('carts', en) # 5.4 返回相应 return response
def put(self, request, address_id): """修改地址业务逻辑""" #校验address_id try: address = Address.objects.get(id=address_id, user=request.user, is_deleted=False) print(address) except Address.DoesNotExist: return http.HttpResponseForbidden('修改收货地址失败') # 接收参数 json_dict = json.loads(request.body.decode()) title = json_dict.get('title') receiver = json_dict.get('receiver') province_id = json_dict.get('province_id') city_id = json_dict.get('city_id') district_id = json_dict.get('district_id') place = json_dict.get('place') mobile = json_dict.get('mobile') tel = json_dict.get('tel') email = json_dict.get('email') # 校验参数 if all([ title, receiver, province_id, city_id, district_id, place, mobile ]) is False: return http.HttpResponseForbidden('缺少必要参数') if not re.match(r'^1[3-9]\d{9}$', mobile): return http.HttpResponseForbidden('参数mobile有误') if tel: if not re.match( r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel): return http.HttpResponseForbidden('参数tel有误') if email: if not re.match( r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email): return http.HttpResponseForbidden('参数email有误') #判断地址是否存在,并更新地址信息 try: address.title = title address.receiver = receiver address.province_id = province_id address.city_id = city_id address.district_id = district_id address.place = place address.mobile = mobile address.tel = tel address.email = email address.save() except DatabaseError as e: logger.error(e) return http.HttpResponseForbidden('修改收获地址失败') #构造响应数据 #把新增的address模型对象转换成字典,并响应给前端 address_dict = { 'id': address.id, 'title': address.title, 'receiver': address.receiver, 'province_id': address.province_id, 'province': address.province.name, 'city_id': address.city_id, 'city': address.city.name, 'district_id': address.district_id, 'district': address.district.name, 'place': address.place, 'mobile': address.mobile, 'tel': address.tel, 'email': address.email, } #响应更新地址结果 # return http.JsonResponse({'code':RETCODE.OK,'errmsg':'修改收货地址成功','address':address_dict}) return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': '修改收货地址成功', 'address': address_dict })
def delete(self, request): """ 一 把需求写下来 (前端需要收集什么 后端需要做什么) 前端 传要删除的sku_id 后端 删除数据 二 把大体思路写下来(后端的大体思路) 1.接收数据 sku_id 2.根据用户信息进行判断 3.登陆用户删除redis 3.1 连接redis 3.2 hash 3.3 set 3.4 返回相应 4.未登陆用户删除cookie 4.1 读取cookie中的数据,并且判断 4.2 删除数据 4.3 字典数据处理,并设置cookie 4.4 返回相应 三 把详细思路完善一下(纯后端) 1. 接收数据 sku_id 2. 校验数据 看有没有这个商品 3. 判断用户是否登录 4. 登录用户 链接redis hash set 返回响应 5. 未登录用户 读取cookie中的数据,看是否有值 删除后进行加密 组织响应数据 设置cookie 返回响应 四 确定我们请求方式和路由 DELETE carts/ """ # 1.接收数据 json_dict = json.loads(request.body.decode()) sku_id = json_dict.get('sku_id') # 2.根据用户信息进行判断 user = request.user if user.is_authenticated: # 3.登陆用户删除redis # 3.1 连接redis redis_conn = get_redis_connection('carts') # 3.2 hash redis_conn.hdel('carts_%s' % request.user.id, sku_id) # 3.3 set redis_conn.srem('selected_%s' % request.user.id, sku_id) # 3.4 返回相应 return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'}) else: # 4.未登陆用户删除cookie carts { sku_id1:{'count': xxx, 'selected': xxx}, sku_id2:} # 4.1 读取cookie中的数据,并且判断 cookie_data = request.COOKIES.get('carts') if cookie_data is None: carts = {} else: carts = pickle.loads(base64.b64decode(cookie_data)) if sku_id in carts: # 4.2 删除字典里面的数据 del carts[sku_id] en = base64.b64encode(pickle.dumps(carts)) response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'}) response.set_cookie('carts', en) # 4.3 返回响应 return response
def form_invalid(self, form): if self.request.is_ajax(): return http.JsonResponse(form.errors, status=400)
def post(self, request): """ 1.需求 前端: 收集信息(商品id, 数量count, 选中状态是可选的,默认为选中) 如果用户登录了,则请求中携带session_id 如果用户没有登录,请求不带session_id 后端: 增加数据到购物车 2.大体思路 接收数据 验证数据 存储数据 返回响应 3.详细思路 1.接收数据(sku_id, count) 2.验证数据 3.根据用户是否登录来判断存储数据的位置和细节 4.登录用户redis里面 4.1 链接redis 4.2 存储(hash,set) 4.3 返回响应 5.非登录用户 cookie里面 5.1 转为二进制 5.2 加密 5.3 存在cookie里面 5.4 返回响应 4.请求方式和路由 POST carts/ """ # 1.接收数据(sku_id, count) json_data = json.loads(request.body.decode()) sku_id = json_data.get('sku_id') count = json_data.get('count') # 2.验证数据 # 2.1 判断参数是否齐全 if not all([sku_id, count]): return http.JsonResponse({ 'code': RETCODE.NODATAERR, 'errmsg': '参数不齐' }) # 2.2 判断是否存在此商品 try: SKU.objects.get(id=sku_id) except Exception as e: return http.JsonResponse({ 'code': RETCODE.NODATAERR, 'errmsg': '商品不存在' }) # 2.3 传过来的count数量需要是数值而不是字符串 try: count = int(count) except Exception as e: return http.JsonResponse({ 'code': RETCODE.NODATAERR, 'errmsg': '商品数量参数错误' }) # 3.根据用户是否登录来判断存储数据的位置和细节 if request.user.is_authenticated: # 4.登录用户redis里面 # 4.1链接redis redis_conn = get_redis_connection('carts') # 创建管道 pl = redis_conn.pipeline() # 4.2存储(hash, set) # HSET key field value # 将哈希表key中的域field的值设为value 。 # pl.hset('carts_%s' % request.user.id, sku_id, count) # todo 这里需要注意.如果这样写的话,再次往购物车加之前加过得商品.不会累计数量,只会覆盖原有数量 # 所以不满足要求,这里可以使用哈希类型自带的hincrby(),实现在原有基础上,进行加本次的数据操作 pl.hincrby('carts_%s' % request.user.id, sku_id, count) # SADD key member[member...] # 将一个或多个member元素加入到集合key当中,已经存在于集合的member元素将被忽略。 pl.sadd('selected_%s' % request.user.id, sku_id) # 执行管道 pl.execute() # 4.3返回响应 return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'}) else: # 5.非登录用户cookie里面 # # 5.1 组织数据 # carts = { # sku_id: {'count': count, 'selected': True}, # } # 这里需要进行判断, 先判断cookie里面的carts是否存在 cookie_str = request.COOKIES.get('carts') if cookie_str is None: # 如果不存在,则进行增加 # 如果cookie数据不存在,先初始化一个 空的carts carts = {} else: # 如果存在.对数据进行解码,并更新count数据 # 对数据进行base64解码 de = base64.b64decode(cookie_str) # 再将bytes类型的数据转换为字典 carts = pickle.loads(de) if sku_id in carts: origin_count = carts[sku_id]['count'] count = count + origin_count carts[sku_id] = {'count': count, 'selected': True} # 5.2 加密 # 将数据转换为bytes类型 a = pickle.dumps(carts) # 将bytes类型转换为base64类型 b = base64.b64encode(a) # 5.3 存在cookie里面 response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'}) # todo 这里注意设置cookie时: set_cookie(key,string_value) 里面第二个值应为字符串 # 而base64为bytes类型的数据,所以要对其进行解码 response.set_cookie('carts', b.decode()) # 5.4返回响应 return response
def put(self, request): # 获取参数 json_data = request.body.decode() data = json.loads(json_data) sku_id = data.get("sku_id") sku_count = data.get("count") sku_price = data.get("price") sku_amount_price = str(float(sku_price) * int(sku_count)) sku_amount_price = str( Decimal(sku_amount_price).quantize(Decimal("0.00"))) selected = data.get("selected", True) if not all([sku_id, sku_count]): return http.HttpResponseForbidden("参数不全") # 判断sku是否存在 try: Sku.objects.get(id=sku_id) except Sku.DoesNotExist: return http.HttpResponseForbidden("商品不存在") user = request.user # 判断用户是否登录 if user.is_authenticated: # 已登录加入redis4号库 redis_conn = get_redis_connection("carts") pipeline = redis_conn.pipeline() #直接覆盖当前用户sku的数量 pipeline.hset("cart_user_%s" % user.id, sku_id, sku_count) if selected: #如果选中,添加 pipeline.sadd("cart_selected_%s" % user.id, sku_id) else: #没选中,删除 pipeline.srem("cart_selected_%s" % user.id, sku_id) pipeline.execute() # 响应结果 cart_sku = { "sku_id": sku_id, "count": sku_count, "selected": selected, "sku_amount_price": sku_amount_price } return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku }) else: # 未登录加入cookies中 # 获取carts的cookies值 carts_str = request.COOKIES.get("carts") if carts_str: # 存在把cookie值转换为字典 carts_dict = pickle.loads(base64.b64decode(carts_str.encode())) else: # 不存在设置cart_dict为空 carts_dict = {} # 从购物车中获取sku cart_sku = carts_dict.get(sku_id) if cart_sku: # 购物车存在这sku则将覆盖 carts_dict[sku_id]['count'] = sku_count carts_dict[sku_id]['selected'] = selected else: # 不存在则添加sku carts_dict[sku_id] = { "count": sku_count, "selected": selected, } # 将字典转换成base64的字符串 carts = base64.b64encode(pickle.dumps(carts_dict)).decode() # 构造响应数据 cart_sku = { "sku_id": sku_id, "count": sku_count, "selected": selected, "sku_amount_price": sku_amount_price } response = http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku }) response.set_cookie('carts', carts, max_age=constants.CARTS_COOKIE_EXPIRES) return response
def process_exception(self, request, exception): if request.environ.get('CONTENT_TYPE') == 'application/json': return http.JsonResponse(data={'error': exception.message}, content_type='application/json', status=400, charset='utf-8')
def get(self, request, mobile): # 来发短信之前先判断此手机号有没有在60s之前发过 # 0. 创建redis连接对象 redis_conn = get_redis_connection('verify_code') # 尝试性去获取此手机号是否有发过短信的标记 send_flag = redis_conn.get('send_flag_%s' % mobile) # 如果胡提前响应 if send_flag: return http.JsonResponse({ 'code': RETCODE.THROTTLINGERR, 'errmsg': '频繁发送短信' }) # 1.接收前端传入的数据 image_code_client = request.GET.get('image_code') uuid = request.GET.get('uuid') # 2.校验数据 if all([image_code_client, uuid]) is False: return http.HttpResponseForbidden('缺少必传参数') # 2.2 获取redis中的图形验证码 image_code_server = redis_conn.get("img_%s" % uuid) # 从redis获取出来的数据都是bytes类型 # 2.3 把redis中图形验证码删除 redis_conn.delete(uuid) # 只让图形验证码使用一次 # 2.4 判断短信验证码是否过期 if image_code_server is None: return http.HttpResponseForbidden('图形验证码过期') # 2.5 注册必须保证image_code_server它不会None再去调用decode print("aaa") image_code_server = image_code_server.decode() # 2. 6 判断用户输入验证码是否正确 注意转换大小写 if image_code_client.lower() != image_code_server.lower(): return http.HttpResponseForbidden('图形验证码输入有误') # 3. 随机生成一个6位数字作为验证码 sms_code = '%06d' % randint(0, 999999) logger.info(sms_code) # redis管道技术 pl = redis_conn.pipeline() # 将短信验证码存储到redis,以备后期注册时校验 # redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code) pl.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code) # 向redis多存储一个此手机号已发送过短信的标记,此标记有效期60秒 # redis_conn.setex('send_flag_%s' % mobile, 60, 1) pl.setex('send_flag_%s' % mobile, 60, 1) # 执行管道 pl.execute() # 给当前手机号发短信 # CCP().send_template_sms(要收短信的手机号, [短信验证码, 短信中提示的过期时间单位分钟], 短信模板id) # CCP().send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], 1) send_sms_code.delay(mobile, sms_code) # 生产任务 # 响应 return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信验证码成功'})
def post(self, request): '''保存订单信息和订单商品信息''' '''获取要保存的信息''' #接收参数 json_dict = json.loads(request.body.decode()) #获取参数 address_id = json_dict.get('address_id') pay_method = json_dict.get('pay_method') #校验参数 if not all([address_id, pay_method]): return http.HttpResponseForbidden('缺少必传参数') try: address = Address.objects.get(id=address_id) except Exception: return http.HttpResponseForbidden('参数address_id错误') if pay_method not in [ OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY'] ]: return http.HttpResponseForbidden('参数pay_method错误') #获取用户 user = request.user '''保存订单信息''' #生成订单号 order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id) '''保存订单信息''' order = OrderInfo.objects.create( order_id=order_id, user=user, address=address, total_count=0, total_amount=Decimal('0'), freight=Decimal('10.00'), pay_method=pay_method, status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else OrderInfo.ORDER_STATUS_ENUM['UNSEND']) '''保存订单商品信息''' # 从redis读取购物车中被勾选的商品信息 redis_conn = get_redis_connection('carts') redis_cart = redis_conn.hgetall('carts_%s' % user.id) selected = redis_conn.smembers('selected_%s' % user.id) carts = {} for sku_id in selected: carts[int(sku_id)] = int(redis_cart[sku_id]) sku_ids = carts.keys() #遍历购物车中被勾选商品信息 for sku_id in sku_ids: # 查询SKU信息 sku = SKU.objects.get(id=sku_id) # 判断SKU库存 sku_count = carts[sku.id] if sku_count > sku.stock: return http.JsonResponse({ 'code': RETCODE.STOCKERR, 'errmsg': '库存不足' }) # SKU减少库存,增加销量 sku.stock -= sku_count sku.sales += sku_count sku.save() # 修改SPU销量 spu = SPU.objects.get(id=sku.spu_id) spu.sales += sku_count spu.save() # 保存订单商品信息 OrderGoods(多) OrderGoods.objects.create( order=order, sku=sku, count=sku_count, price=sku.price, ) # 保存商品订单中总价和总数量 order.total_count += sku_count order.total_amount += (sku_count * sku.price) # 添加邮费和保存订单信息 order.total_amount += order.freight order.save() # 清除购物车中已结算的商品 pl = redis_conn.pipeline() pl.hdel('carts_%s' % user.id, *selected) pl.srem('selected_%s' % user.id, *selected) pl.execute() # 响应提交订单结果 return http.JsonResponse({ 'code': RETCODE.OK, 'errmsg': '下单成功', 'order_id': order.order_id })