既然提到了活动,那与之对应的肯定是优惠策略、优惠活动等等。
所以我们要为优惠活动策略单独建立表结构
class CourseDiscountType(BaseModel):
"""课程优惠类型"""
name = models.CharField(max_length=32, verbose_name="优惠类型名称")
remark = models.CharField(max_length=250, blank=True, null=True, verbose_name="备注信息")
class Meta:
db\_table = "ly\_course\_discount\_type"
verbose\_name = "课程优惠类型"
verbose\_name\_plural = "课程优惠类型"
def \_\_str\_\_(self):
return "%s" % (self.name)
class CourseDiscount(BaseModel):
"""课程优惠模型"""
discount_type = models.ForeignKey("CourseDiscountType", on_delete=models.CASCADE, related_name='coursediscounts', verbose_name="优惠类型")
condition = models.IntegerField(blank=True, default=0, verbose_name="满足优惠的价格条件",help_text="设置参与优惠的价格门槛,表示商品必须在xx价格以上的时候才参与优惠活动,
如果不填,则不设置门槛") #因为有的课程不足100,你减免100,还亏钱了
sale = models.TextField(verbose_name="优惠公式",blank=True,null=True, help_text="""
不填表示免费;
*号开头表示折扣价,例如*0.82表示八二折;
-号开头则表示减免,例如-20表示原价-20;
如果需要表示满减,则需要使用 原价-优惠价格,例如表示课程价格大于100,优惠10;大于200,优惠25,格式如下:
满100-10
满200-25
""")
class Meta:
db\_table = "ly\_course\_discount"
verbose\_name = "价格优惠策略"
verbose\_name\_plural = "价格优惠策略"
def \_\_str\_\_(self):
return "价格优惠:%s,优惠条件:%s,优惠值:%s" % (self.discount\_type.name, self.condition, self.sale)
class Activity(BaseModel):
"""优惠活动"""
name = models.CharField(max_length=150, verbose_name="活动名称")
start_time = models.DateTimeField(verbose_name="优惠策略的开始时间")
end_time = models.DateTimeField(verbose_name="优惠策略的结束时间")
remark = models.CharField(max_length=250, blank=True, null=True, verbose_name="备注信息")
class Meta:
db\_table = "ly\_activity"
verbose\_name="商品活动"
verbose\_name\_plural="商品活动"
def \_\_str\_\_(self):
return self.name
class CoursePriceDiscount(BaseModel):
"""课程与优惠策略的关系表"""
course = models.ForeignKey("Course",on_delete=models.CASCADE, related_name="activeprices",verbose_name="课程")
active = models.ForeignKey("Activity",on_delete=models.DO_NOTHING, related_name="activecourses",verbose_name="活动")
discount = models.ForeignKey("CourseDiscount",on_delete=models.CASCADE,related_name="discountcourse",verbose_name="优惠折扣")
class Meta:
db\_table = "ly\_course\_price\_dicount"
verbose\_name="课程与优惠策略的关系表"
verbose\_name\_plural="课程与优惠策略的关系表"
def \_\_str\_\_(self):
return "课程:%s,优惠活动: %s,开始时间:%s,结束时间:%s" % (self.course.name, self.active.name, self.active.start\_time,self.active.end\_time)
优惠活动策略的表结构设计
1.course/models.py
在模型类中写入discount_name 让课程列表页页面显示优惠类型名称
class Course:
def activity(self):
import datetime
now = datetime.datetime.now()
# 获取课程参加的活动名称
activity\_list = self.activeprices.filter(is\_show=True, is\_deleted=False, active\_\_start\_time\_\_lte=now, active\_\_end\_time\_\_gte=now)
return activity\_list
# 优惠类型名称
def discount\_name(self):
dis\_name = ''
a = self.activity()
if a:
discount\_n\_list = \[\]
for i in a:
# 获取课程的折扣类型名称
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
序列化器加入该字段
class CourseModelSerializer:
field = [,discount_name]
class CourseDetailModelSerializer:
field = [,discount_name]
3.drf测试:course/courses
1.dev.py
USE_TZ = False # 修改时区
2.course/models.py
class Course:
def real_price(self):
price = float(self.price) # 获取真实价格
r_price = price
a = self.activity() # 获取课程对应的活动名称
if a: # 如果课程参加了活动
sale = a\[0\].discount.sale # 查看活动对应的优惠公式
condition\_price = a\[0\].discount.condition # 查看活动对应的满足优惠的价格条件
# 限时免费
if not sale.strip():
r\_price = 0
# 限时折扣 \*0.5
elif '\*' in sale.strip():
if price >= condition\_price:
\_, d = sale.split('\*')
r\_price = price \* float(d)
# 限时减免 -100
elif sale.strip().startswith('-'):
if price >= condition\_price:
\_, d = sale.split('-')
r\_price = price - float(d)
# 满减 满100-15
elif '满' 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) 满足100-200-300 应该选择满300那个优惠
if price >= float(a):
dis\_list.append(float(b))
max\_dis = max(dis\_list) # 取到最大的那个满减优惠
r\_price = price - max\_dis # 原价格-满减价格=真实价格
return r\_price
3.course/serializers.py
class CourseModelSerializer:
field = [,discount_name,real_price]
class CourseDetailModelSerializer:
field = [,discount_name,real_price]
4.drf测试:course/courses
{{course\_data.discount\_name}}
距离结束:仅剩 567 天 12小时 52分 32 秒
活动价 ¥{{course\_data.real\_price}} ¥{{course\_data.price}}
1.course/models.py
class Course:
def left_time(self):
import datetime
now = datetime.datetime.now().timestamp() # 获取当前时间戳
left_t = 0
a = self.activity() # 获取当前课程参加的活动
if a: # 如果当前课程有参加活动
end_time = a[0].active.end_time.timestamp() # 获取当前课程活动的结束时间
left_t = end_time - now # 剩余时间=结束时间-当前时间
return left\_t
2.序列化器放入left_time
course/serializers.py
class CourseDetailModelSerializer:
field = [,discount_name,real_price,left_time]
3.前端渲染距离结束时间
Detail.vue
{{course\_data.discount\_name}}
距离结束:仅剩 {{course\_data.left\_time/60/60/24 | pInt}}天 {{course\_data.left\_time/60/60 % 24| pInt}}小时 {{course\_data.left\_time/60 % 60 | pInt}}分 {{course\_data.left\_time % 60 | pInt}} 秒
在js部分需要设置一个定时器,让时间能够一直减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
// 设置计时器
setInterval(()=>{
this.course\_data.left\_time--;
},1000)
})
},
补充:让页面可以显示出0x分0x秒的效果→过滤器
Detail.vue
距离结束:仅剩 {{course_data.left_time/60/60/24 | pInt}}天 {{course_data.left_time/60/60 % 24| pInt}}小时 {{course_data.left_time/60 % 60 | pInt}}分 {{course_data.left_time % 60 | pInt}} 秒
// Detail.vue--js
filters:{
pInt(val){
let a = parseInt(val);
if (a < 10){
a = `0${a}`;
}
return a
}
}
为视图添加IsAuthenticated用户认证
# cart/views.py
class AddCartView:
# 请求头必须携带着token
permission\_classes = \[IsAuthenticated,\] # 添加用户认证
def add:
user\_id = request.user.id # 获取真实的用户id
'''
request = user\_obj
'''
drf接口: cart/add_cart 获取不到数据,因为加了IsAuthenticated认证
添加购物车时需要携带token 否则不能添加购物车
// Detail.vue 添加购物车
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,
},{
// 向后端提交数据需要加上header项,将token也要提交过去
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 {
......
})
}
},
查看购物车时需要携带token 否则不能添加购物车
// Cart.vue 购物车页面
created() {
let token = sessionStorage.token || localStorage.token;
if (token){
this.$axios.get(\`${this.$settings.Host}/cart/add\_cart/\`,{
// 查看购物车页面也要携带token 否则不能查看
headers:{
'Authorization':'jwt ' + token
}
})
.then((res)=>{
this.cart\_data\_list = res.data.cart\_data\_list
})
.catch((error)=>{
this.$message.error(error.response.data.msg);
})
# cart/views.py
class AddCartView:
def cart_list(self, request):
……
cart_data_list.append({
……
'real_price': course_obj.real_price(),
……
})
……
# cart/views.py
class AddCartView:
def change_select(self, request):
# 拿到课程id
course\_id = request.data.get('course\_id')
# 校验课程id合法性
try:
models.Course.objects.get(id=course\_id)
except:
return Response({'msg': '课程不存在,不要乱搞!'}, status=status.HTTP\_400\_BAD\_REQUEST)
# 拿到user\_id
user\_id = request.user.id
# 去redis数据库:cart
conn = get\_redis\_connection('cart')
# redis存数据-用集合存:用户id:勾选课程id
conn.sadd('selected\_cart\_%s' % user\_id, course\_id)
return Response({'msg':'勾选成功'})
def cancel\_select(self, request):
course\_id = request.data.get('course\_id')
try:
models.Course.objects.get(id=course\_id)
except:
return Response({'msg': '课程不存在,不要乱搞!'}, 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': '恭喜你!少花钱了,但是你真的不学习了吗!'})
为两个函数配置路由
# 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'})) # 不同的请求方法走不同函数
]
// Cartitem.vue
watch:{
'cart.selected':function (){
// 添加选中
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') // 触发cart组件计算商品总价格的方法
}).catch((error)=>{
this.$message.error(res.data.msg);
})
}
else {
// 取消选中
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') // 触发cart组件计算商品总价格的方法
}).catch((error)=>{
this.$message.error(res.data.msg);
})
}
},
}
触发Cart组件(父组件)的计算商品总价格的方法
// Cart.vue
methods:{
cal_total_price(){
let t\_price = 0
this.cart\_data\_list.forEach((v,k)=>{ // v是值 k是索引
if (v.selected){
t\_price += v.real\_price
}
})
this.total\_price = t\_price
}
}
# 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') # 取出用户购物车里的课程id(redis中存着呢)
expire\_id = eid.decode('utf-8') # 取出用户购物车里的有效期id(redis中存着呢)
course\_obj = models.Course.objects.get(id=course\_id) # 根据课程id,通过ORM查询得到课程对象,在下面就可以通过课程对象.字段 取到对应课程的参数信息
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, # 默认没有勾选
})
except Exception:
logger.error('获取购物车数据失败')
return Response({'msg': '后台数据库出问题了,请联系管理员'}, status=status.HTTP\_507\_INSUFFICIENT\_STORAGE)
return Response({'msg': 'xxx', 'cart\_data\_list': cart\_data\_list})
BUG:勾选两个课程 刷新页面 redis中仍然存着两个课程id
# cart/views.py
def cart_list(self, request):
……
conn = get_redis_connection('cart')
# 用户刷新页面时,从redis中删除用户对应的课程id
conn.delete('selected_cart_%s' % user_id)
ret = conn.hgetall('cart_%s' % user_id)
……
手机扫一扫
移动阅读更方便
你可能感兴趣的文章