当前位置:网站首页>Day85: Luffy: shopping cart switching price according to different validity period & shopping cart deletion operation & price settlement & foreplay of order page

Day85: Luffy: shopping cart switching price according to different validity period & shopping cart deletion operation & price settlement & foreplay of order page

2020-11-09 23:48:00 iR-Poke

Catalog

1. Shopping cart validity switch

2. Switch the price according to the validity period

3. Shopping cart delete operation

4. Price settlement

5. Order page - initialization

1. Shopping cart validity switch

1. About the validity period of the design

1.course/models.py

class CourseExpire(BaseModel):
    """ Course validity model """
    #  You can put it in the database later course and expire_time Field set to Federated index 
    course = models.ForeignKey("Course", related_name='course_expire', on_delete=models.CASCADE, verbose_name=" Course name ")

    #  Term of validity , Days 
    expire_time = models.IntegerField(verbose_name=" The period of validity ", null=True, blank=True, help_text=" The validity period is calculated in days ")

    #  One month, and so on 
    expire_text = models.CharField(max_length=150, verbose_name=" Tip text ", null=True, blank=True)
    #  Price per validity period 
    price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" Price of course ", default=0)

    class Meta:
        db_table = "ly_course_expire"
        verbose_name = " Course validity "
        verbose_name_plural = verbose_name

    def __str__(self):
        return " Course :%s, The period of validity :%s, Price :%s" % (self.course, self.expire_text, self.price)
Structure design of course validity schedule

stay course In the table price Field with a hint text

price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" Course the original price ", default=0,help_text=' If the price is 0, So it means that when the current course is purchased , There is no permanent term of validity .')

2. Insert some test data

INSERT INTO `ly_course_expire`
(`id`,`orders`,`is_show`,`is_deleted`,`created_time`,`updated_time`,`expire_time`,`expire_text`,`course_id`,`price`)
VALUES
(1,1,1,0,'2019-08-19 02:05:22.368823','2019-08-19 02:05:22.368855',30,' For one month ',1,398.00),
(2,2,1,0,'2019-08-19 02:05:37.397205','2019-08-19 02:05:37.397233',60,'2 Months in effect ',1,588.00),
(3,3,1,0,'2019-08-19 02:05:57.029411','2019-08-19 02:05:57.029440',180,' Within half a year ',1,1000.00),
(4,4,1,0,'2019-08-19 02:07:29.066617','2019-08-19 02:08:29.156730',3,'3 Within days ',3,0.88),
(5,3,1,0,'2019-08-19 02:07:46.120827','2019-08-19 02:08:18.652452',30,'1 Months in effect ',3,188.00),
(6,3,1,0,'2019-08-19 02:07:59.876421','2019-08-19 02:07:59.876454',60,'2 Months in effect ',3,298.00);

3.xadmin register

course/adminx.py

from .models import CourseExpire
class CourseExpireModelAdmin(object):
    """ Commodity validity model """
    pass
xadmin.site.register(CourseExpire, CourseExpireModelAdmin)

2. Course validity - Back end interface

course/models.py

class Course:    
    #  Get course validity 
    def get_expire(self):
        
        #  The one to many relationship between the course schedule and the course validity schedule   Reverse query 
        expire_list = self.course_expire.all() #  Query the validity type of the current course 
        data = []
        for expire in expire_list:
            data.append({
                'id':expire.id,
                'expire_text':expire.expire_text, #  The text of the validity period   such as ' Three months '
                'price':expire.price, #  The price corresponding to the validity period 
            })

        #  When the price is 0 when , There's no permanent term , Everything else has 
        if self.price > 0:
            data.append({
                'id': 0,
                'expire_text': ' Permanent validity ',
                'price': self.price,
            })

        return data

cart/views.py

class AddCartView(ViewSet):

    def cart_list(self,request):
          
        try:
            ......
            cart_data_list.append({
               ......
                'expire_list':course_obj.get_expire(),
               ......
            })
        except Exception:
           ......
        return Response({'msg':'xxx','cart_data_list':cart_data_list})

3. Course validity - front end

cartitem.vue

<div class="cart_column column_3">
    <el-select v-model="cart.expire_id" size="mini" placeholder=" Please select the purchase validity period " class="my_el_select">
        <el-option :label="expire.expire_text" :value="expire.id" :key="expire.id" v-for="(expire,expire_index) in cart.expire_list"></el-option>
    </el-select>
</div>

2. Switch the price according to the validity period

1. Validity switch changes redis Data in the

cart/views.py

class AddCartView:
        def change_expire(self,request):

            user_id = request.user.id
            course_id = request.data.get('course_id')
            expire_id = request.data.get('expire_id')
            
           #  Testing course id Whether it works 
            try:
                course_obj = models.Course.objects.get(id=course_id)
            except:
                return Response({'msg':' The curriculum doesn't exist '},status=status.HTTP_400_BAD_REQUEST)
            #  Inspection validity period id Whether it works 
            try:
                if expire_id > 0:
                 expire_object = models.CourseExpire.objects.get(id=expire_id)
            except:
                return Response({'msg':' The course validity period does not exist '},status=status.HTTP_400_BAD_REQUEST)

            #  Calculation xx Course xx The real price of the validity period 
            real_price = course_obj.real_price(expire_id)

            #  Changed the new validity period , To save the validity period after the change to redis in 
            conn = get_redis_connection('cart')
            conn.hset('cart_%s' % user_id, course_id, expire_id)

            return Response({'msg':' Switch successful !', 'real_price': real_price})

cart/urls.py

urlpatterns = [
    ......
    path('expires/', views.AddCartView.as_view({'put':'change_expire'}))
]

2. The validity switch changes the page price

course/models.py

class Course:
        def real_price(self,expire_id=0): #  increase expire_id=0 Parameters   Let the real price display different prices according to different validity periods 
            price = float(self.price)
            '''
            1. If it's not permanent (expire_id>0), Then from course_expire Get the price corresponding to the validity period 
            2. If it's permanent (expire_id=0), Then the real price is equal to its original price 
            '''
            if expire_id > 0:
                expire_obj = self.course_expire.get(id=expire_id)
                price = float(expire_obj.price)

            r_price = price
            a = self.activity()
            if a:
                sale = a[0].discount.sale
                condition_price = a[0].discount.condition

                #  Time limited free 
                if not sale.strip():
                    r_price = 0

                #  Limited time discount   *0.5
                elif '*' in sale.strip():
                    if price >= condition_price:
                        _, d = sale.split('*')
                        r_price = price * float(d)
                #  Time limited relief   -100
                elif sale.strip().startswith('-'):
                    if price >= condition_price:
                        _, d = sale.split('-')
                        r_price = price - float(d)

                elif ' full ' in sale:
                    if price >= condition_price:
                        l1 = sale.split('\r\n')
                        dis_list = []  #10 50  25
                        for i in l1:
                            a, b = i[1:].split('-')

                            #400
                            if price >= float(a):
                                dis_list.append(float(b))

                        max_dis = max(dis_list)
                        r_price = price - max_dis

            return r_price

cartitem.vue

// js
 watch:{
      ......
      //  When the validity period of the course selected by the user changes ( Select another validity period in the drop-down box at the front end )
      'cart.expire_id':function (){
        let token = localStorage.token || sessionStorage.token;

          this.$axios.put(`${this.$settings.Host}/cart/expires/`,{
            //  Will the course id The validity period corresponding to the course id Commit to the backend 
            course_id: this.cart.course_id,
            expire_id:this.cart.expire_id,
          },{
            headers:{
              'Authorization':'jwt ' + token
            }

      }
      ).then((res)=>{
        this.$message.success(res.data.msg);
              
        //  Modification of validity period succeeded , Return the real price to the front end 
        this.cart.real_price = res.data.real_price;
              
        //  Modification of validity period succeeded , Trigger cart Event that the parent component recalculates the total price 
        this.$emit('change_expire_handler',) 

          }).catch((error)=>{
            this.$message.error(error.response.data.msg)
          })
    }
},

cart.vue

<div class="cart_course_list">
    <CartItem v-for="(value,index) in cart_data_list" :key="index" :cart="value" @cal_t_p="cal_total_price" @change_expire_handler="cal_total_price" @delete_course_handler="delete_c(index)"></CartItem>

</div>

3. page refresh The expiration date should be reset

cart/views.py

class AddCartView:
    def cart_list(self,request):

        user_id = request.user.id

        conn = get_redis_connection('cart')

        conn.delete('selected_cart_%s' % user_id)
        ret = conn.hgetall('cart_%s' % user_id)  #dict {b'1': b'0', b'2': b'0'}
        cart_data_list = []

        try:
            for cid, eid in ret.items():
                course_id = cid.decode('utf-8')
                
                #  When the user looks at the shopping cart page -> The default display is permanent 
                conn.hset('cart_%s' % user_id, course_id, 0)
                expire_id = 0
                
                course_obj = models.Course.objects.get(id=course_id)

                cart_data_list.append({
                    'course_id':course_obj.id,
                    'name':course_obj.name,
                    'course_img':contains.SERVER_ADDR + course_obj.course_img.url ,
                    'price':course_obj.price,
                    'real_price':course_obj.real_price(),
                    'expire_id':expire_id,
                    'expire_list':course_obj.get_expire(),
                    'selected':False,  #  By default, it is not checked 
                })
        except Exception:
            logger.error(' Failed to get cart data ')
            return Response({'msg':' There's something wrong with the background database , Please contact the Administrator '},status=status.HTTP_507_INSUFFICIENT_STORAGE)

        return Response({'msg':'xxx','cart_data_list':cart_data_list})
    

3. Shopping cart delete operation

cartitem.vue

<div class="cart_column column_4" id="delete" @click="delete_course"> Delete </div>
delete_course(){
        let token = localStorage.token || sessionStorage.token;

          this.$axios.delete(`${this.$settings.Host}/cart/add_cart/`,{
            params:{
              course_id: this.cart.course_id,  // request.query_params.get('course_id')
            },
            headers:{
              'Authorization':'jwt ' + token
            }
          })
        .then((res)=>{
          this.$message.success(res.data.msg);
              
          //  When a user wants to delete a course record in the shopping cart   perform Cart Delete course event of parent component 
          this.$emit('delete_course_handler')
        })
        .catch((error)=>{
          this.$message.error(error.response.data.msg);
        })
      }

1. Back end redis Delete

cart/urls.py

urlpatterns = [
    path('add_cart/', views.AddCartView.as_view(
        {'post':'add','get':'cart_list',
         'patch':'change_select','put':'cancel_select',
         'delete':'delete_course'
         })),
    path('expires/', views.AddCartView.as_view({'put':'change_expire'}))

]

cart/views.py

class AddCartView:
    def delete_course(self,request):
        user_id = request.user.id
        course_id = request.query_params.get('course_id')
        
        conn = get_redis_connection('cart')
        pipe = conn.pipeline()
        pipe.hdel('cart_%s' % user_id, course_id)
        pipe.srem('selected_cart_%s' % user_id, course_id)
        pipe.execute()

        return Response({'msg':' Delete successful '})

2. Front end synchronization to achieve deletion effect

User delete a shopping cart data The back end has been removed But the front page should also be deleted synchronously

Cart.vue

<div class="cart_course_list">
    <CartItem v-for="(value,index) in cart_data_list" :key="index" :cart="value" @cal_t_p="cal_total_price" @change_expire_handler="cal_total_price" @delete_course_handler="delete_c(index)"></CartItem>

</div>
delete_c(index){
        this.cart_data_list.splice(index,1)
        this.cal_total_price() //  After deleting   Re trigger the method of calculating the total price 
      }

Cartitem.vue

delete_course(){
        let token = localStorage.token || sessionStorage.token;

          this.$axios.delete(`${this.$settings.Host}/cart/add_cart/`,{
            params:{
              course_id: this.cart.course_id, 
            },
            headers:{
              'Authorization':'jwt ' + token
            }
          })
        .then((res)=>{ //  deleted   Trigger Cart Delete event of parent component 
          this.$message.success(res.data.msg);
          this.$emit('delete_course_handler')
        })
        .catch((error)=>{
          this.$message.error(error.response.data.msg);
        })
      }
  }

4. Price settlement

1. Price settlement page - preparation

1. Settlement initial page

<!--  Initial interface of settlement page  -->

2. Configure the routing

index.js

import Vue from 'vue'
import Order from "@/components/Order"

Vue.use(Router)
export default new Router({
  mode:'history',
  routes: [
    ......
    {
      path: '/order/',   
      component: Order
    },
  ]
})

3. Click the go to checkout button See the page

cart.vue

<span class="goto_pay"><router-link to="/order/"> To settle accounts </router-link></span>          

2. Price settlement page - Back end

cart/urls.py

urlpatterns = [
    ...
    path('expires/', views.AddCartView.as_view({'get':'show_pay_info'}))

]

cart/views.py

def show_pay_info(self,request):
    
    user_id = request.user.id
    
    conn = get_redis_connection('cart')
    
    #  Get all courses selected by the user's shopping cart id
    select_list = conn.smembers('selected_cart_%s' % user_id)
    data = []

    #  Get user shopping cart course id: The period of validity id
    ret = conn.hgetall('cart_%s' % user_id)  # dict {b'1': b'0', b'2': b'0'}

    
    for cid, eid in ret.items():
        expire_id = int(eid.decode('utf-8'))
        if cid in select_list: #  If the course is checked 

            course_id = int(cid.decode('utf-8'))
            #  Access to this ' Selected courses 'model object 
            course_obj = models.Course.objects.get(id=course_id)
           
            #  If the validity period of the course is not permanent 
            if expire_id > 0:
                expire_obj = models.CourseExpire.objects.get(id=expire_id)
                data.append({
                    'course_id':course_obj.id,
                    'name':course_obj.name,
                    'course_img':contains.SERVER_ADDR + course_obj.course_img.url ,
                    'real_price':course_obj.real_price(expire_id),
                    'expire_text':expire_obj.expire_text,
                })
                
            #  If the course is valid for a permanent period 
            else:
                data.append({
                    'course_id': course_obj.id,
                    'name': course_obj.name,
                    'course_img': contains.SERVER_ADDR + course_obj.course_img.url,
                    'real_price': course_obj.real_price(expire_id),
                    'expire_text': ' Permanent validity ',
                })


    return Response({'data':data})

3. Price settlement page - front end

order.vue

// js
methods: {
      get_order_data(){
        let token = localStorage.token || sessionStorage.token;
        this.$axios.get(`${this.$settings.Host}/cart/expires/`,{
          headers:{
              'Authorization':'jwt ' + token
            }
        }).then((res)=>{
          this.course_list = res.data.data;
        })
      },
<!-- html --> 
<div class="cart-item" v-for="(course,index) in course_list">
          <el-row>
             <el-col :span="2" class="checkbox">&nbsp;&nbsp;</el-col>
             <el-col :span="10" class="course-info">
               <img :src="course.course_img" alt="">
                <span>{{course.name}}</span>
             </el-col>
             <el-col :span="8"><span>{{course.expire_text}}</span></el-col>
             <el-col :span="4" class="course-price">¥{{course.real_price}}</el-col>
           </el-row>
        </div>

4. Switching payment methods - WeChat / Alipay

It's just a few pictures Click to display the picture

Order.vue

<el-col :span="8">
    <span class="alipay"  v-if="pay_type===1"><img src="../../static/img/alipay2.png"  alt=""></span>
    <span class="alipay" @click="pay_type=1"  v-else><img src="../../static/img/alipay.png" alt=""></span>
    <span class="alipay wechat" v-if="pay_type===2"><img src="../../static/img/wechat2.png" alt="" ></span>
    <span class="alipay wechat"  @click="pay_type=2" v-else><img src="../../static/img/wechat.png" alt=""></span>

</el-col>

5. Order page - initialization

order/models.py

from django.db import models

# Create your models here.


from lyapi.utils.models import BaseModel
from users.models import User
from course.models import Course
class Order(BaseModel):
    """ Order model """
    status_choices = (
        (0, ' Did not pay '),
        (1, ' Paid '),
        (2, ' Cancelled '),
        (3, ' Time out cancellation '),
    )
    pay_choices = (
        (0, ' Alipay '),
        (1, ' Wechat payment '),
    )
    order_title = models.CharField(max_length=150,verbose_name=" Order title ")
    total_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" The order price ", default=0)
    real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" Amount paid ", default=0)
    order_number = models.CharField(max_length=64,verbose_name=" The order number ")
    order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name=" The order status ")
    pay_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name=" Method of payment ")
    credit = models.IntegerField(default=0, verbose_name=" The number of points used ")
    coupon = models.IntegerField(null=True, verbose_name=" User coupons ID")
    order_desc = models.TextField(max_length=500, verbose_name=" Order description ",null=True,blank=True)
    pay_time = models.DateTimeField(null=True, verbose_name=" Time of payment ")
    user = models.ForeignKey(User, related_name='user_orders', on_delete=models.DO_NOTHING,verbose_name=" Order user ")

    class Meta:
        db_table="ly_order"
        verbose_name= " Order records "
        verbose_name_plural= " Order records "

    def __str__(self):
        return "%s, The total price : %s, Paid in : %s" % (self.order_title, self.total_price, self.real_price)


class OrderDetail(BaseModel):
    """
     Order details 
    """
    order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, verbose_name=" Order ID")
    course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.CASCADE, verbose_name=" Course ID")
    expire = models.IntegerField(default='0', verbose_name=" Validity period ",help_text="0 Permanent ")
    price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" Course the original price ")
    real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" The course is real ")
    discount_name = models.CharField(max_length=120,default="",verbose_name=" Type of offer ")

    class Meta:
        db_table="ly_order_detail"
        verbose_name= " Order details "
        verbose_name_plural= " Order details "

    def __str__(self):
        return "%s" % (self.course.name)
Order table structure design

order/adminx.py

import xadmin
from .models import Order
class OrderModelAdmin(object):
    """ Order model management class """
    pass

xadmin.site.register(Order, OrderModelAdmin)


from .models import OrderDetail
class OrderDetailModelAdmin(object):
    """ Order detail model management class """
    pass

xadmin.site.register(OrderDetail, OrderDetailModelAdmin)

order/__init__.py

default_app_config = "order.apps.OrderConfig"

order/app.py

from django.apps import AppConfig


class OrderConfig(AppConfig):
    name = 'order'
    verbose_name = ' Order management '

order/urls.py

from django.urls import path,re_path
from . import views

urlpatterns = [
    path('add_money/',views.OrderView.as_view(),)

]

order/views.py

from django.shortcuts import render
from rest_framework.generics import CreateAPIView
# Create your views here.
from . import models
from .serializers import OrderModelSerializer
from rest_framework.permissions import IsAuthenticated

class OrderView(CreateAPIView):
    queryset = models.Order.objects.filter(is_deleted=False,is_show=True)
    serializer_class = OrderModelSerializer
    permission_classes = [IsAuthenticated, ]

order/serializers.py

import datetime
from rest_framework import serializers
from . import models
from django_redis import get_redis_connection
from course.models import Course
from course.models import CourseExpire
from django.db import transaction


class OrderModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Order
        fields = ['id', 'order_number', 'pay_type', 'coupon', 'credit']
        
        '''
        1. The user needs to submit the data in the order page :
             Payment type : WeChat / Alipay 
             Coupon 
             integral 
        2. After the user submits the data successfully , The front page needs to return the data :
            id
             The order number 
        '''
        extra_kwargs = {
            'id':{'read_only':True},
            'order_number':{'read_only':True},
            'pay_type':{'write_only':True},
            'coupon':{'write_only':True},
            'credit':{'write_only':True},
        }

    def validate(self, attrs):

        #  Get the payment method used by the user 
        pay_type = int(attrs.get('pay_type',0))  #
        
        #  Verify whether the payment method used by users is one of Alipay or WeChat 
        if pay_type not in [i[0] for i in models.Order.pay_choices]:
            raise serializers.ValidationError(' The payment method is wrong !')

            
        # todo  Coupon verification , See if it's overdue, etc 


        # todo  Integral upper limit check 

        return attrs

    def create(self, validated_data):
        try:
            #  Generate order number   [ date , user id, Self increasing data ]
            current_time = datetime.datetime.now()
            now = current_time.strftime('%Y%m%d%H%M%S')
            user_id = self.context['request'].user.id
            
            conn = get_redis_connection('cart')
            num = conn.incr('num')
            
            #  Generate a unique order number 
            order_number = now + "%06d" % user_id + "%06d" % num

            with transaction.atomic():  #  Add transaction 

                #  Generate order 
                order_obj = models.Order.objects.create(**{
                    'order_title': '31 Time order ',
                    'total_price': 0,
                    'real_price': 0,
                    'order_number': order_number,
                    'order_status': 0,
                    'pay_type': validated_data.get('pay_type', 0),
                    'credit': 0,
                    'coupon': 0,
                    'order_desc': ' Girl friend ',
                    'pay_time': current_time,
                    'user_id': user_id,
                    # 'user':user_obj,
                })

                select_list = conn.smembers('selected_cart_%s' % user_id)

                ret = conn.hgetall('cart_%s' % user_id)  # dict {b'1': b'0', b'2': b'0'}

                for cid, eid in ret.items():
                    expire_id = int(eid.decode('utf-8'))
                    if cid in select_list:

                        course_id = int(cid.decode('utf-8'))
                        course_obj = Course.objects.get(id=course_id)
                        if expire_id > 0:
                            expire_text = CourseExpire.objects.get(id=expire_id).expire_text

                        #  Generate order details 
                        models.OrderDetail.objects.create(**{
                            'order': order_obj,
                            'course': course_obj,
                            'expire': expire_id,
                            'price': course_obj.price,
                            'real_price': course_obj.real_price(expire_id),
                            'discount_name': course_obj.discount_name(),
                        })
            # print('xxxxx')
        except Exception:
            raise models.Order.DoesNotExist

        return order_obj

 

版权声明
本文为[iR-Poke]所创,转载请带上原文链接,感谢