通过互联技术,变革传统教育方式,依赖强大的导师服务系统,提高在线教育的效率与成功率。
项目策略:
团队构成:
周期:
简要描述如下:
Django中实现:
支付需求起始于用户点击立即支付或者加入购物车
当用户选择需要购买的课程和价格策略后,有两种方式购买课程:
将课程id和选择的价格策略放到redis中,跳到去支付页面,从redis中获取购买课程的id和价格策略id,如果该用户要使用优惠券和贝利,则选择当前用户所拥有并且未使用和没过期的优惠券,得到折后价格,点击去支付完成支付,
前端需要提供的数据包括:所选课程的id和所选择的价格策略id。约定的数据格式如下:
继续将该课程的所有信息主要包括:课程id,课程图片地址,课程标题,所有价格策略,默认价格策略封装在car_dict中
下面是redis中保存用户数据的key
SHOPPING_CART_KEY="luffy_shopping_cart_%s_%s"PAYMENT_KEY="luffy_payment_%s_%s"PAYMENT_COUPON_KEY="luffy_payment_coupon_%s"
car_key=settings.SHOPPING_CART_KEY%(request.auth.user_id,course_id,)car_dict={'title':course.name,'img':course.course_img,'default_policy':policy_id,'policy':json.dumps(price_policy_dict)}
完成后,在redis中用户购物车的数据如下:
{luffy_shopping_car_6_11:{'title':'21天入门到放弃','src':'xxx.png','policy':{1:{id:'xx'.....},2:{id:'xx'.....},3:{id:'xx'.....},4:{id:'xx'.....},},'default_policy':3},luffy_shopping_car_6_13:{...}}
购物车完整的CURD接口:
修改购物车总的价格策略(patch):
向后台发课程id和要修改为的价格策略id,判断课程是否在购物车中,判断传递过来的价格策略是否在当前课程的价格策略中,在的话将redis中的当前课程的默认价格策略修改为当前的价格策略具体将上面代码
在用户完成购物车的后,点击结算,前端服务器发送结算数据("courseids":["1","2"],1和2对应的就是课程的ID)给我们后端的Django服务器,约定的数据格如下:
{"courseids":["1","2"]}
1.获取用户提交的课程id,[1,2]
判断是否选择要结算的课程,没选择则抛出异常
course_id_list=request.data.get('course_list')ifnotcourse_id_listornotisinstance(course_id_list,list):raiseException('请选择要结算的课程')
2.检测购物车中检查是否已经有课程(应该有课程的)
product_dict=redis_pool.conn.hget(settings.LUFFY_SHOPPING_CAR,request.user.id)ifnotproduct_dict:raiseException('购物车无课程')
3.检测购物车中是否有用户要购买的课程
product_dict=json.loads(product_dict.decode('utf-8'))#######课程、价格和优惠券#######policy_course_dict={}#循环用户传递过来的要结算的课程ID列表forcourse_idincourse_id_list:course_id=str(course_id)product=product_dict.get(course_id)ifnotproduct:raiseException('购买的课程必须先加入购物车')如果所结算的课程在购物车中,
4.获取选中价格策略的价格详细,
选择购物车中当前课程下的所有价格策略和当前课程的所选择的价格策略相等的价格策略,获取其信息,
#c.购物车中是否有用户要购买的课程product_dict=json.loads(product_dict.decode('utf-8'))#######课程、价格和优惠券#######policy_course_dict={}#循环用户传递过来的要结算的课程ID列表forcourse_idincourse_id_list:course_id=str(course_id)product=product_dict.get(course_id)ifnotproduct:raiseException('购买的课程必须先加入购物车')#获取选中价格策略的价格详细policy_exist=Falseforpolicyinproduct['price_policy_list']:ifpolicy['id']==product['choice_policy_id']:policy_price=policy['price']policy_period=policy['period']policy_valid_period=policy['valid_period']policy_exist=Truebreakifnotpolicy_exist:raiseException('购物车中的课程无此价格')将上面我们获取的课程信息和价格策略信息封装在policy_course中
policy_course={'course_id':course_id,'course_name':product['name'],'course_img':product['course_img'],'policy_id':product['choice_policy_id'],'policy_price':policy_price,'policy_period':policy_period,'policy_valid_period':policy_valid_period,'default_coupon_id':0,'coupon_record_list':{0:{'id':0,'text':'请选择优惠券'},},}5.获取当前用户所有的优惠券
user_coupon_list=models.CouponRecord.objects.filter(account=request.user,status=0)6.区分用户的优惠券种类,课程优惠券添加到课程中;全局优惠券添加到全局
#######全局优惠券#######global_coupon_record_dict={}循环遍历当前用户的所有优惠券,判断他们是否过期
begin_date=record.coupon.valid_begin_dateend_date=record.coupon.valid_end_dateifbegin_date:ifcurrent_date
,如果没过期,判断他们是全局优惠券还是针对某个课程的优惠券,区分好是什么优惠券以后还的区分该优惠券是什么类型,
如果是通用券
ifrecord.coupon.coupon_type==0:temp={'type':0,'text':"通用优惠券",'id':record.id,'begin_date':begin_date,'end_date':end_date,'money_equivalent_value':record.coupon.money_equivalent_value}如果是满减券:
elifrecord.coupon.coupon_type==1:temp={'type':1,'text':"满减券",'id':record.id,'begin_date':begin_date,'end_date':end_date,'minimum_consume':record.coupon.minimum_consume,'money_equivalent_value':record.coupon.money_equivalent_value}如果是折扣券:
elifrecord.coupon.coupon_type==2:temp={'type':2,'text':"折扣券",'id':record.id,'begin_date':begin_date,'end_date':end_date,'off_percent':record.coupon.off_percent}如果是全局优惠券,则
global_coupon_record_dict[record.id]=temp如果但是针对课程的优惠券:
policy_course_dict[cid]['coupon_record_list'][record.id]=temp最后将所有数据封装在user_pay中放到redis上
user_pay={'policy_course_dict':policy_course_dict,'global_coupon_record_dict':global_coupon_record_dict,'default_global_coupon_id':0,}redis_pool.conn.hset(settings.LUFFY_PAYMENT,request.user.id,json.dumps(user_pay))user_pay数据结构
2.循环遍历每一个课程
开始总价格totalprice和折扣价totaldiscount都为0,
总价=0总折扣=02.1.如果该课程没有使用优惠券,则总价格=totalprice+课程原价,totaldiscount=0,
如果使用了优惠券:去数据库查询:指定优惠券是否已经使用、是否已经过期如果优惠券可不用:raiseException('优惠券不可用')如果是通用优惠券:discount=通用优惠券(如果大于课程原价,课程原价)elif如果是满减优惠券:if价格是否大于最小满减要求:discount=通用优惠券(如果大于课程原价,课程原价)elif如果是折扣优惠券:discount=课程原价*(1-折扣)使用的优惠券ID列表.append(绑定可以的优惠券ID)temp={课程ID:1,原价:10,折扣价:9,有效期:30}课程信息.append(temp)总价+=课程原价折扣+=discount到此为止
pay=总价-总折扣3.继续计算看是否使用全局优惠券
全站优惠券ID=choice_global_coupon数据库获取并检查是否可用(优惠券是否已经使用、是否已经过期)如果优惠券可不用:raise('全站优惠券不可用')g_discount=0如果是通用优惠券:g_discount=通用优惠券(如果大于pay,pay)elif如果是满减优惠券:if价格是否大于最小满减要求:g_discount=通用优惠券(如果大于pay,pay)elif如果是折扣优惠券:g_discount=pay*(1-折扣)总折扣+=g_discount使用的优惠券ID列表.append(全站优惠券ID)4.贝利支付
ifbalance<=request.user.balance:总折扣+=balance5.总结算
总价-总折扣=alipayifalipay==0:贝里&优惠券pay_type=0else:支付宝支付pay_type=1如果最后支付=0,就直接修改支付状态为已支付,否则改为待支付
6.点击立即支付以后进行数据库操作
最后
ifpay_type==1:生成支付宝链接(自动生成自己的订单号),并返回给前端Vue######################################支付宝的回调######################################defcallback(request,*args,**kwargs):models.Order.objects.filter(订单号).update(status=0)