当前位置:网站首页>Day84: Luffy: preferential activity strategy & User Authentication & checking / settlement of shopping cart goods

Day84: Luffy: preferential activity strategy & User Authentication & checking / settlement of shopping cart goods

2020-11-09 22:15:00 iR-Poke

Catalog

1. Course list page activity and real price calculation

  1. Preferential campaign strategy model Table structure

  2. The course list page shows the name of the offer type

  3. The course list page shows the real price

  4. Display the name of the offer type and the real price on the front page

  5. The course list page shows the specific end time

2. Add cart / Check the user authentication of the shopping cart

3. Check the price of shopping cart / Settlement

  1. The real price of each course is displayed on the shopping cart page

  2. Check / Non check should be in redis In real time storage - Back end interface

  3. Check / If not, the price should be recalculated in the front page

  4. Shopping cart list shows - Back end interface

1. Course list page activity and real price calculation

Since we mentioned the event , It must be a preferential strategy 、 Promotional activities and so on .

So we need to create a separate table structure for the promotion strategy

1. Preferential campaign strategy model Table structure

class CourseDiscountType(BaseModel):
    """ Course offer type """
    name = models.CharField(max_length=32, verbose_name=" Offer type name ")
    remark = models.CharField(max_length=250, blank=True, null=True, verbose_name=" Notes ")

    class Meta:
        db_table = "ly_course_discount_type"
        verbose_name = " Course offer type "
        verbose_name_plural = " Course offer type "

    def __str__(self):
        return "%s" % (self.name)


class CourseDiscount(BaseModel):
    """ Course discount model """
    discount_type = models.ForeignKey("CourseDiscountType", on_delete=models.CASCADE, related_name='coursediscounts', verbose_name=" Type of offer ")
    condition = models.IntegerField(blank=True, default=0, verbose_name=" Meet the favorable price conditions ",help_text=" Set the price threshold for participating in the offer , Indicates that the product must be in xx Only when the price is above the price can we participate in the preferential activities ,<br> If you don't fill in , There is no threshold ") # Because some courses are not enough 100, You're reducing 100, I lost money 
    sale = models.TextField(verbose_name=" Preferential formula ",blank=True,null=True, help_text="""
     If not, it means free ;<br>
    * The beginning of the sign indicates the discount price , for example *0.82 20% off ;<br>
    - The beginning of the sign means a reduction , for example -20 It means the original price -20;<br>
     If you need to say full minus , You need to use   The original price - Favorable Price , For example, it means that the course price is higher than 100, Discount 10; Greater than 200, Discount 25, The format is as follows :<br>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; full 100-10<br>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; full 200-25<br>
    """)

    class Meta:
        db_table = "ly_course_discount"
        verbose_name = " Preferential price strategy "
        verbose_name_plural = " Preferential price strategy "

    def __str__(self):
        return " Price concessions :%s, Preferential terms :%s, Preferential value :%s" % (self.discount_type.name, self.condition, self.sale)

class Activity(BaseModel):
    """ Special offers """
    name = models.CharField(max_length=150, verbose_name=" The name of the event ")
    start_time = models.DateTimeField(verbose_name=" The start time of the discount strategy ")
    end_time = models.DateTimeField(verbose_name=" The end time of the offer ")
    remark = models.CharField(max_length=250, blank=True, null=True, verbose_name=" Notes ")

    class Meta:
        db_table = "ly_activity"
        verbose_name=" Commodity activities "
        verbose_name_plural=" Commodity activities "

    def __str__(self):
        return self.name

class CoursePriceDiscount(BaseModel):
    """ The relationship between courses and preferential strategies """
    course = models.ForeignKey("Course",on_delete=models.CASCADE, related_name="activeprices",verbose_name=" Course ")
    active = models.ForeignKey("Activity",on_delete=models.DO_NOTHING, related_name="activecourses",verbose_name=" Activities ")
    discount = models.ForeignKey("CourseDiscount",on_delete=models.CASCADE,related_name="discountcourse",verbose_name=" Discount ")

    class Meta:
        db_table = "ly_course_price_dicount"
        verbose_name=" The relationship between courses and preferential strategies "
        verbose_name_plural=" The relationship between courses and preferential strategies "

    def __str__(self):
        return " Course :%s, Special offers : %s, Starting time :%s, End time :%s" % (self.course.name, self.active.name, self.active.start_time,self.active.end_time)
Table structure design of preferential activity strategy

2. The course list page shows the name of the offer type

1.course/models.py

Write... In the model class discount_name Let the course list page show the name of the offer type

class Course:
    
    def activity(self):
        import datetime
        now = datetime.datetime.now()
        
        #  Get the name of the activity that the course participated in 
        activity_list = self.activeprices.filter(is_show=True, is_deleted=False, active__start_time__lte=now, active__end_time__gte=now)
        return activity_list

    #  Offer type name 
    def discount_name(self):
        dis_name = ''
        a = self.activity()
        if a:
            discount_n_list = []
            for i in a:
                #  Get the discount type name of the course 
                discount_n = i.discount.discount_type.name
                discount_n_list.append(discount_n)
            dis_name = discount_n_list[0]

        return dis_name

2.course/serializers.py

The serializer adds the field

class CourseModelSerializer:
    field = [,discount_name]
class CourseDetailModelSerializer:
    field = [,discount_name]

3.drf test :course/courses

3. The course list page shows the real price

1.dev.py

USE_TZ = False #  Change the time zone 

2.course/models.py

class Course:
    def real_price(self):
        price = float(self.price) #  Get the real price 
        r_price = price 

        a = self.activity() #  Get the activity name corresponding to the course 
        if a: #  If the course takes part in an activity 
            sale = a[0].discount.sale #  Check the discount formula for the event 
            condition_price = a[0].discount.condition #  Check the price conditions that meet the discount for the event 
            #  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)
            #  Full reduction   full 100-15
            elif ' full ' in sale:
                if price >= condition_price: #  Only if the price meets the preferential conditions can the price be fully reduced 
                    l1 = sale.split('\r\n')
                    dis_list = []  #10 50  25
                    for i in l1:
                        a, b = i[1:].split('-')

                        #  When the price of goods (400)  Satisfy 100-200-300  Should choose full 300 That discount 
                        if price >= float(a):
                            dis_list.append(float(b))

                    max_dis = max(dis_list) #  Get the biggest discount 
                    r_price = price - max_dis #  The original price - Full price reduction = The real price 

        return r_price

3.course/serializers.py

class CourseModelSerializer:
    field = [,discount_name,real_price]
class CourseDetailModelSerializer:
    field = [,discount_name,real_price]

4.drf test :course/courses

4. Display the name of the offer type and the real price on the front page

1. The front of the course list page

<!-- Course.vue -->
<div class="pay-box">
    <span class="discount-type" v-if="course.discount_name">{{course.discount_name}}</span>
    <span class="discount-price">¥{{course.real_price}} element </span>
    <span class="original-price" v-if="course.discount_name"> The original price :{{course.price}} element </span>
    <span class="buy-now"> Buy now </span>
</div>
    

2. At the front of the course details page

<!-- Detail.vue -->
<div class="sale-time">
    <p class="sale-type">{{course_data.discount_name}}</p>
    <p class="expire"> The end of the distance : Only left  567  God  12 Hours  52 branch  <span class="second">32</span>  second </p>
</div>
<p class="course-price">
    <span> Activity price </span>
    <span class="discount">¥{{course_data.real_price}}</span>
    <span class="original">¥{{course_data.price}}</span>
</p>

5. The course list page shows the specific end time

1.course/models.py

class Course:
    def left_time(self):
        import datetime
        now = datetime.datetime.now().timestamp() #  Get the current timestamp 
        left_t = 0
        a = self.activity() #  Get the activities of the current course 
        if a: #  If there are activities in the current course 
            end_time = a[0].active.end_time.timestamp() #  Get the end time of the current course activity 
            left_t = end_time - now #  The rest of the time = End time - current time 

            return left_t

2. The serializer puts left_time

course/serializers.py

class CourseDetailModelSerializer:
    field = [,discount_name,real_price,left_time]

3. Front end rendering to end time

Detail.vue

<!-- html -->
<div class="sale-time">
    <p class="sale-type">{{course_data.discount_name}}</p>
    <p class="expire"> The end of the distance : Only left  {{course_data.left_time/60/60/24 | pInt}} God  {{course_data.left_time/60/60 % 24| pInt}} Hours  {{course_data.left_time/60 % 60 | pInt}} branch  <span class="second">{{course_data.left_time % 60 | pInt}}</span>  second </p>
</div>

stay js Part needs to set a timer , Let time be reduced all the time 1s

Detail.vue

get_course_data(){
        this.$axios.get(`${this.$settings.Host}/course/detail/${this.course_id}/`)
        .then((res)=>{
          //console.log(res.data);
          this.course_data = res.data;
          this.playerOptions.sources[0].src = res.data.course_video
          this.playerOptions.poster = res.data.course_img

         //  Set timer 
          setInterval(()=>{
            this.course_data.left_time--;

          },1000)

        })
      },

Add : Let the page show 0x branch 0x Second effect → filter

Detail.vue

<!-- Detail.vue--html -->
         <p class="expire"> The end of the distance : Only left  {{course_data.left_time/60/60/24 | pInt}} God  
{{course_data.left_time/60/60 % 24| pInt}} Hours
{{course_data.left_time/60 % 60 | pInt}} branch
<span class="second">{{course_data.left_time % 60 | pInt}}</span> second </p>
// Detail.vue--js
 filters:{
      pInt(val){
        let a = parseInt(val);
        if (a < 10){
          a = `0${a}`;
        }
        return a
      }
    }

2. Add cart / Check the user authentication of the shopping cart

Add... To the view IsAuthenticated User authentication

 

# cart/views.py
class AddCartView:
    
    #  The request head must carry token
    permission_classes = [IsAuthenticated,] #  Add user authentication 
    def add:
        user_id = request.user.id #  Get real users id
        '''
        request = user_obj
        '''
    

drf Interface : cart/add_cart Data not available , Because of the addition of IsAuthenticated authentication

Add a shopping cart with token Otherwise, you can't add a shopping cart

// Detail.vue  Add cart 
    methods: {

      addCart(){

        let token = localStorage.token || sessionStorage.token;

        if (token){
          this.$axios.post(`${this.$settings.Host}/users/verify/`,{
              token:token,
            }).then((res)=>{
    this.$axios.post(`${this.$settings.Host}/cart/add_cart/`,{
                course_id:this.course_id,
              },{
              //  To submit data to the back end, you need to add header term , take token Also submit to the past 
              headers:{
                'Authorization':'jwt ' + token
              }
            }).then((res)=>{
                this.$message.success(res.data.msg);
                console.log('>>>>>',this.$store)
                this.$store.commit('add_cart', res.data.cart_length) ;
                console.log(this.$store.state);
              })

            }).catch((error)=>{
             ......
            })


        } else {
          ......
              })
        }

      },

You need to carry with you when you check the shopping cart token Otherwise, you can't add a shopping cart

// Cart.vue  Shopping cart page 
created() {
    let token = sessionStorage.token || localStorage.token;
    if (token){

      this.$axios.get(`${this.$settings.Host}/cart/add_cart/`,{
         //  You should also carry the shopping cart page token  Otherwise you can't view 
         headers:{
                'Authorization':'jwt ' + token
              }
      })
      .then((res)=>{
        this.cart_data_list = res.data.cart_data_list
      })
      .catch((error)=>{
        this.$message.error(error.response.data.msg);
      })

3. Check the price of shopping cart / Settlement

1. The real price of each course is displayed on the shopping cart page

# cart/views.py

class AddCartView:
def cart_list(self, request):
       ......
        cart_data_list.append({
           ......
            'real_price': course_obj.real_price(),
           ......
        })
       ......
<!-- cartitem.vue -->
<div class="cart_column column_4">¥{{cart.real_price}}</div>

2. Check / Non check should be in redis In real time storage - Back end interface

# cart/views.py
class AddCartView:
    def change_select(self, request):
        
        #  Get the course id
        course_id = request.data.get('course_id')
        
        #  Calibration course id Legitimacy 
        try:
            models.Course.objects.get(id=course_id)
        except:
            return Response({'msg': ' The curriculum doesn't exist , Don't mess with !'}, status=status.HTTP_400_BAD_REQUEST)
        
        #  Get user_id
        user_id = request.user.id
        
        #  Go to redis database :cart
        conn = get_redis_connection('cart')
        
        # redis Save the data - Save in sets : user id: Check the course id
        conn.sadd('selected_cart_%s' % user_id, course_id)
        
        return Response({'msg':' Check success '})   
    
       def cancel_select(self, request):
        course_id = request.data.get('course_id')

        try:
            models.Course.objects.get(id=course_id)
        except:
            return Response({'msg': ' The curriculum doesn't exist , Don't mess with !'}, status=status.HTTP_400_BAD_REQUEST)

        user_id = request.user.id
        conn = get_redis_connection('cart')  # 1:{1,3}
        conn.srem('selected_cart_%s' % user_id, course_id)

        return Response({'msg': ' congratulations ! Less money , But do you really stop learning !'}) 

Configure routing for both functions

# cart/urls.py
from django.urls import path,re_path
from . import views

urlpatterns = [
    path('add_cart/', views.AddCartView.as_view({'post':'add',
'get':'cart_list',
'patch':'change_select',
'put':'cancel_select'})) # Different request methods take different functions ]

3. Check / If not, the price should be recalculated in the front page

<!--  When the user clicks on the check box in front of it , Will change selected Value 
 Close and will be watch Listen to the 
 In monitoring   Whether it is checked or not, the parent label will be triggered to recalculate the price (this.$emit) -->
<el-checkbox class="my_el_checkbox" v-model="cart.selected"></el-checkbox>
// Cartitem.vue
watch:{

      'cart.selected':function (){
          
        //  Add the check 
        let token = localStorage.token || sessionStorage.token;
        if (this.cart.selected){
          this.$axios.patch(`${this.$settings.Host}/cart/add_cart/`,{
            course_id: this.cart.course_id,

          },{
            headers:{
              'Authorization':'jwt ' + token
            }
          }).then((res)=>{
            this.$message.success(res.data.msg);
            this.$emit('cal_t_p') //  Trigger cart How to calculate the total commodity price 
          }).catch((error)=>{
            this.$message.error(res.data.msg);
          })
        }
        else {
            
          //  Deselect 
          this.$axios.put(`${this.$settings.Host}/cart/add_cart/`,{
            course_id: this.cart.course_id,

          },{
            headers:{
              'Authorization':'jwt ' + token
            }
          }).then((res)=>{
            this.$message.success(res.data.msg);
            this.$emit('cal_t_p') //  Trigger cart How to calculate the total commodity price 
          }).catch((error)=>{
            this.$message.error(res.data.msg);
          })
        }
      },


    }

Trigger Cart Components ( Parent component ) How to calculate the total price of goods

<!-- 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"></CartItem>

</div>
// Cart.vue
methods:{
    cal_total_price(){
        
        let t_price = 0
        this.cart_data_list.forEach((v,k)=>{ // v Is the value  k It's the index 
          if (v.selected){
            t_price += v.real_price
          }
        })
        this.total_price = t_price

      }
}

4. Shopping cart list shows - Back end interface

# cart/views.py
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') #  Take out the course from the user's shopping cart id(redis There is )
                expire_id = eid.decode('utf-8') #  Take out the expiration date from the user's shopping cart id(redis There is )

                course_obj = models.Course.objects.get(id=course_id) #  According to the course id, adopt ORM Query to get the course object , In the following, you can use the course object . Field   Get the parameter information of the corresponding course 

                cart_data_list.append({
                    'course_id': course_obj.id,
                    'name': course_obj.name,
                    'course_img': constants.SERVER_ADDR + course_obj.course_img.url,
                    'price': course_obj.price,
                    'real_price': course_obj.real_price(),
                    'expire_id': expire_id,
                    '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})
    

BUG: Check two courses Refresh the page redis There are still two courses in id

# cart/views.py
def cart_list(self, request):
    ......
    conn = get_redis_connection('cart')
    #  When the user refreshes the page , from redis Delete the course corresponding to the user in id
    conn.delete('selected_cart_%s' % user_id)
    ret = conn.hgetall('cart_%s' % user_id)  
    ......

 

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