【DailyFresh】课程记录7---订单模块(支付)

P94 用户中心——订单页面


本来更新商品库存实在添加商品之后,在使用乐观锁的时候将其提到前面来

# todo: 返回受影响的行数res, 0为失败
res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock).update(stock=new_stock, sales=new_sales)  # 乐观锁
if res == 0:  # 返回0表示更新失败
    if i == 2:  # 尝试到第三次
        transaction.savepoint_rollback(save_id)
        return JsonResponse({'res': 8, 'errmsg': '下单失败2'})
    continue

# TODO: 向df_order_goods表中添加一条记录
OrderGoods.objects.create(order=order,
                      sku=sku,
                      count=count,
                      price=sku.price
)


假如将其放在添加一条记录之后,如下:

# TODO: 向df_order_goods表中添加一条记录
OrderGoods.objects.create(order=order,
                      sku=sku,
                      count=count,
                      price=sku.price
)
# todo: 返回受影响的行数res, 0为失败
res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock).update(stock=new_stock, sales=new_sales)  # 乐观锁
if res == 0:  # 返回0表示更新失败
    if i == 2:  # 尝试到第三次
        transaction.savepoint_rollback(save_id)
        return JsonResponse({'res': 8, 'errmsg': '下单失败2'})
    continue


分析会出现的情况,假如依旧以两个用户同时买一件商品,库存数量为3的场景进行分析。 
点击完之后有一次一定是成功的,此时把这条记录往表中加了一次 ,第二次做尝试,成功之后又加了一次,这是下单成功,表中的信息重复了这一单在表中添加了两次。
因此需要将乐观锁放到前面,如果没有下单成功则不会往表中添加信息,此时不会出现重复。这就是为什么将更新放到添加记录的前面,防止尝试的时候内容是重复的。

在提交订后,目前place_order.html中的js代码部分是打印了创建成功。

这里在订单提交成功之后,会弹出一个弹窗,写着订单提交成功,紧接着3s之后跳转到一个页面,
在这个页面我们就可以让它跳转到用户中心的提交订单页面,在下单成功之后,我们就会让你跳转到用户中心的提交订单页面《---这个页面对应的地址是/user/order


{% block bottom %}
    <div class="popup_con">
        <div class="popup">
            <p>订单提交成功!</p>
        </div>
        
        <div class="mask"></div>
    </div>
{% endblock bottom %}

$.post('/order/commit', params, function (data) {
    // 进行处理
    if (data.res == 5){
        {#alert('订单创建成功')#}
        localStorage.setItem('order_finish',2);

        $('.popup_con').fadeIn('fast', function() {
        setTimeout(function(){
            $('.popup_con').fadeOut('fast',function(){
                window.location.href = '/user/order/1';
            });
        },3000)
            });
    }
    else{
        alert(data.errmsg)
    }
})

这里进行重新下单测试,在测试之前,将之前进行测试的订单信息都删除
select * from df_order_info;
select * from df_order_goods;

在清除这两个表中数据的时候,先删除df_order_goods中的数据,再删除df_order_info中的数据,否则会报外键错误《---有外键约束
delete from df_order_goods; 
delete from df_order_info;
此时两个表均为空,重新下订单

下单之后会提示订单成功,3s之后跳转到了用户中心--全部订单页面,在做用户模块时,仅做了展示。
该页面需要的信息
订单的创建时间,订单的编号,订单的状态,实付订单总额,以及订单中每个商品的信息,包括数目以及小计,除此之外,信息是分页显示的获取第几页的内容,需要告知页码,此时去修改URL
在user.urls中
path('order/(?P<page>\d+)', UserOrderView.as_view(), name='order') # 用户中心-订单页

在user/views.py中的UserOrderView捕获参数,接收页码page
# /user/order
class UserOrderView(LoginRequiredMixin, View):
    """用户中心-订单页"""
    def get(self, request, page):
    # 获取用户的订单信息
    return render(request, 'df_user/user_center_order.html')

在base.html和用户中心页base_user_center.html中修改{% url 'user:order' 1 %},默认是获取第一页的订单信息

在views.py中要拿到的内容
首先需要将用户中所有的内容查出来
from django.core.paginator import Paginator

# /user/order
class UserOrderView(LoginRequiredMixin, View):
    """用户中心-订单页"""
    def get(self, request, page):
    # 获取用户的订单信息
    
    # 先获取用户的信息
    user = request.user
    # 获取该用户的所有订单
    orders = OrderInfo.objects.filter(user=user)

    # 页面中还需要订单商品的信息
    # 遍历获取订单商品的信息
    for order in orders:
        # 根据order_id查询订单商品信息
        # order_skus是一个查询集,遍历出来的每个内容都是一个OrderGoods,在OrderGoods中包含属性count和price,在显示时还有一个商品的小计,可以通过这两个属性计算得到小计
        order_skus = OrderGoods.objects.filter(order_id=order.order_id)
        # 可以遍历order_skus计算商品的小计 
        for order_sku in order_skus:
            # 计算商品的小计
            amount = order_sku.count * order_sku.price
            # 动态给order_sku增加属性amount,保存订单商品的小计
            order_sku.amount = amount

            # <--此时订单order_sku中的小计都计算出来了
        # 这里处理支付状态标题*
        # 动态给order增加属性, 保存订单状态标题
                order.status_name = OrderInfo.ORDER_STATUS[order.order_status]
           

        # 除此之外,订单商品的查询集是根order相关的,也要把它保存起来
        # 方式:动态给order增加属性,保存订单商品的信息
        order.order_skus = order_skus
        # <--当遍历完之后,页面需要的所有信息都获取到了

    # 当查询完之后需要进行分页
    # 对orders进行分页,每页显示一条信息
    paginator = Paginator(orders, 1)
    # 接下来就是获取第xx页的内容
    # 页码通过get传过来
    # 先要判断其是否合法
    # 处理页码

    try:
        page = int(page)
    except Exception as e:
        page = 1

    if page > paginator.num_pages or page <= 0:
        page = 1

    # 获取第page页的Page实例对象
    order_page = paginator.page(page)

    # todo: 进行页码的控制,页面上最多显示5个页码
    # 1. 总数不足5页,显示全部
    # 2. 如当前页是前3页,显示1-5页
    # 3. 如当前页是后3页,显示后5页
    # 4. 其他情况,显示当前页的前2页,当前页,当前页的后2页
    num_pages = paginator.num_pages
    if num_pages < 5:
        pages = range(1, num_pages)
    elif page <= 3:
        pages = range(1, 6)
    elif num_pages - page <= 2:
        pages = range(num_pages-4, num_pages+1)
    else:
        pages = range(page-2, page+3)
    

    # 组织上下文
    context = {'order_page': order_page,# pager对象
           'pages': pages,  # 页面范围控制
           'page': 'order'  # 告诉我们显示的是用户中心的哪一个页面
    }


        
            
        
    # 使用模板
    return render(request, 'df_user/user_center_order.html', context)

处理user_center_order.html
注意点1:
 <td width="15%">{{ order.total_price |add:order.transit_price }}(含运费:{{ order.transit_price }})元</td>

注意点2:支付状态展示的是序号,要展示状态则在order/models.py的OrderInfo中定义字典:
  ORDER_STATUS = {
        1: '待支付',
        2: '待发货',
        3: '待收货',
        4: '待评价',
        5: '已完成'
    }
# 这里处理支付状态标题*<----已经在views.py中处理,动态增加属性
# 动态给order增加属性, 保存订单状态标题
order.status_name = OrderInfo.ORDER_STATUS[order.order_status]

在页面上展示的时候
<td width="15%">{{ order.status_name }}</td>

刷新页面,展示成功
重新下一单,订单提交成功,在进行跳转的时候报错

需要去修改place_order.html中的跳转地址'/user/order/1'
此时会跳转到第一页,之前的订单
要显示最新的订单在user.views.UserOrderView中获取orders是按照创建时间倒叙排序

orders = OrderInfo.objects.filter(user=user).order_by('-create_time')

此时最新的订单页面展示的是最新订单。


P95 订单支付——支付宝简介

此时订单页面显示完成,接下来应该是点击【去付款】,进行支付操作。
这里介绍支付宝的支付


支付宝开放平台
https://open.alipay.com/platform/home.htm


P96 订单支付——网站对接支付宝流程图

 

此时用户中心可以显示用户的全部订单,当用户点击去付款的时候,就会去付款,付款的时候就会访问我们的网站
这里的【去付款】应该写我们网站的地址,点击去付款是向我们的网站发起一个请求,网站再去调用alipay的接口

/order/pay代表订单支付,在网站对应的后台,一个地址就应该有一个对应的处理函数。
在视图函数中我们就知道用户要进行付款了,在我们函数内部与支付宝平台进行对接,他是要支付,所以我们在这里面调用支付宝的一个接口alipay.trade.page.pay【PC场景下单并支付】,帮助我们生成一个支付的订单页面。
在生成支付的订单时,我们需要传递一个参数过去包括三个必传的,和两个可选的
订单id
总金额
订单标题

return_url
notify_url

这两个URL是django网站的地址

传入这些参数之后,支付宝平台会帮你生成一个支付订单,在处理完成之后,最终会返回一个应答
这个应答:返回一个支付页面的地址
拿到支付页面的地址之后给客户用,在客户的浏览器上要把页面显示出来。
因此拿到支付页面的地址有一个过程,该过程就是把用户引导到页面,让用户的浏览器能够打开支付页面。
接下来用户到支付页面登录账号,输入支付密码,这一过程和我们的网站无关,我们负责将用户引导至支付页面,下面是用户和支付宝的交互:用户登录支付宝,输入支付密码,点击确认支付。
当用户和支付宝进行交互的时候,我们无法确认用户是否支付完毕《---这里考虑上面提到的两个URL:return_url和 notify_url
只要用户付完款之后一点击确认支付,传过去的参数中传过去地址return_url,那么支付宝的平台在用户支付完成之后就会马上同步访问网站的return_url,并传递参数,告诉网站用户支付的结果《---此为同步访问
异步访问网站的notify_url,并传递参数,告诉网站用户支付的结果《---此为异步访问

支付宝在传递支付结果的时候是以异步通知为准

网站如果想让支付宝平台访问,需要有公网的IP,单纯的127.0.0.1支付宝无法访问。
这里没有公网IP的情况下,如何获取支付的结果?
目前我们可以访问支付宝,但是支付宝无法访问我们的网站。
这里有个alipay.trade.query统一收单线下交易查询接口,虽然return_url和 notify_url这两个地址不起作用,但是我们获取支付结果的话可以通过网站自己去访问支付宝,调用支付宝的接口alipay.trade.query,这个接口在处理完之后就会返回用户支付的结果

PS:最后5min是精髓《--总结整个支付流程


P97 订单支付——订单支付代码


目前支付流程已经明朗,如果我去付款的话,访问网站的地址/order/pay需不需要传参数?
需要传递订单id,用户要支付的是哪个订单,需要把订单id传过来,这一步涉及到传参数:订单id
传递参数的方式分析:
用户点击去付款,需要给django传递参数,因为用户支付完之后,会有一个支付的交易编号,我们最终要把支付的交易编号保存到表中,所以这里采用post。

在django网站中调用支付宝接口alipay.trade.page.pay,原生的参数需要自己调用,自己传参,这里有一个SDK的Python包:
https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

别人封装好的,已经生成预付订单需要使用的签名

安装
pip3 install python-alipay-sdk --upgrade

在代码中使用包

截图:采用网络请求方式


生成自己电脑的公钥和私钥
使用openssl
openssl
OpenSSL> genrsa -out app_private_key.pem   2048  # 私钥 gen生成rsa代表算法 -out代表输出一个秘钥文件,输出到app_private_key.pem中
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥 -in代表输入 输入刚才生成的私钥文件的内容,对它进行输出,输出到app_public_key.pem公钥文件中《---生成一个与私钥文件对应的公钥文件
OpenSSL> exit
# 查看私钥内容
cat app_private_key.pem

# 查看公钥内容
cat app_public_key.pem

接下来去沙箱环境中设置公钥,拷贝刚才生成的公钥内容到沙箱中
之后会看到有:查看支付宝公钥,将该公钥保存到项目的文件中

该公钥与订单相关,在订单下新建文件,
order/alipay_public_key.pem<---将支付宝的公钥放到文件该文件中,代表支付宝的公钥
注:这里在文件的开头和结尾加上-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----

加密还需要电脑的私钥,将其获取到,放入order文件夹下order/app_private_key.pem

请求的时候使用自己的私钥加密,收到信息之后使用支付宝的公钥解密

创建初始化对象
appid=""<---沙箱中的APPid
# notify_url
# 应用文件中私钥的路径
# 支付宝的公钥路径
# 签名的类型、算法RSA或RSA2
#debug=False代表不是调试模式,即访问的是实际环境地址,如果是沙箱的话,要改成True


# 电脑网站支付:需要调用alipay.trade.page.pay接口,
这里帮我们封装了一个函数,函数名为alipay.api_alipay_trade_page_pay接口(按照命名转化规则进行转化),该方法是上面初始化对象的一个方法,该方法内部帮我们调用支付宝的支付接口,传参:订单id、总金额、订单标题、return_url、notify_url,最终会返回一个order_string。电脑网站支付,需要跳转到xxx页面,该页面就是支付页面。

以上为支付的使用,接下来分析在views中订单支付的视图该如何实现


# 对应地址: /order/pay
# 请求方式:ajax post
# 前端传递的参数:订单id(order_id)
class OrderPayView(View):
    '''订单支付'''
    def post(self, request):
        '''订单支付'''
        # 关于订单支付,同样的当用户未登录时不能支付
        # 校验用户是否登录
        user = request.user
        if not user.is_authenticated():
            return JsonResponse({'res': 0, 'errmsg': '用户未登录'})

        # 接收参数 订单id
        order_id = request.POST.get('order_id')

        # 校验参数
        # 先判断有没有订单id,处理没有订单id的情况
        if not order_id:
            return JsonResponse({'res': 0, 'errmsg': '无效的订单id'})

        # 查询一下订单id是不是确实存在
        # 订单id到底是不是该用户的
        # 支付结果需要对接支付宝,这里判断支付方式到底是不是支付宝
        # 查看支付状态
        # 满足以上条件且订单确实存在,则说明该订单为有效订单
        try:
            order = OrderInfo.objects.get(order_id=order_id,
                                          user=user,
                                          pay_method=3,
                                          order_status=1)
        except OrderInfo.DoesNotExist:
            return JsonResponse({'res': 2, 'errmsg': '订单错误'})
        
        
        # 业务处理:使用Python sdk调用支付宝的支付接口
        # 使用我们安装的包

        # 先去初始化一个对象
        alipay = AliPay(...)
        # 调用对象的方法---调用支付接口
        # 电脑支付接口《---拿过来并传入必要的参数
        # 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string<---实际地址
        # https://openapi.alipaydev.com/gateway.do? + order_string<---沙箱地址
        # 我们在模型类中使用的类型是Decimal,这里拿过来的也是Decimal类型
        total_pay = order.total_price + order.transit_price

        # 直接放在这里的话,内部都会将数据转化成json,Decimal是不能被序列化的,如果直接写Decimal是会报错的
        # 这里将其直接转化成字符串
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no="order_id, # 订单id
            total_amount=str(total_pay), # 支付总金额,应该等于总金额+运费,在上面进行计算
            subject='天天生鲜%s' %order_id, # 标题
            return_url=None,
            notify_url=None # 可选, 不填则使用默认notify url
        )
        

        # 返回应答
        pay_url = 'https://openapi.alipaydev.com/gateway.do?' + order_string
        # 最终返回数据,将地址带回去
        # 在代码中可以将用户引导到该地址pay_url
        return JsonResponse({'res': 3, 'pay_url': pay_url})


# 业务处理:使用Python sdk调用支付宝的支付接口
# 使用我们安装的包
# 导入包
from alipay import AliPay

# 进行初始化
# 关于path,借助settings.py以及os
from django.conf import settings
import os 

# app_private_key_string = os.path.join(settings.BASE_DIR, 'apps/order/app_private_key.pem')
# alipay_public_key_string = os.path.join(settings.BASE_DIR, 'apps/order/alipay_public_key.pem')

alipay = AliPay(
    appid="2016092700608687",  # 应用id《---沙箱中的id
    app_notify_url=None,  # 默认回调url《---这里是127.0.0.1,所以即使传了支付宝也访问不了,这里不能写空 '',必须写None,代表不传
    app_private_key_string=app_private_key_string,
    # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    alipay_public_key_string=alipay_public_key_string,
    sign_type="RSA2",  # RSA 或者 RSA2《---官网推荐RSA2
    debug=True  # 默认False, 此处沙箱模拟True
)

# 整个过程:初始化之后,开始调用接口,最终要把支付页面地址返回
对应的地址是/order/pay,进行URL配置
path('pay', OrderPayView.as_view(), name='pay') # 订单支付

# 至此后端结束
# 分析前端user_center_order.html

在页面上点击去付款,会访问支付宝支付页面地址,这里也有可能显示:已完成、待评价,这样就不应该访问该地址,所以需要进行相应的判断。《--根据支付状态

这里应该绑定【去付款】出class="oper_btn"的点击事件
{% load static %} 《---导入js

{% block bottomfiles %}

<script src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script>
$('.oper_btn').click(function () {
        // 点击之后,有可能不需要支付
            // 获取status,进行判断,在对应的元素下面自定义属性status 
            status = $(this).attr('status')
            // 获取订单id,在对应的元素下面自定义属性order_id
            order_id = $(this).attr('order_id')
            if (status == 1){
                // 进行支付
                csrf = $('input[name="csrfmiddlewaretoken"]').val()
                // 组织参数
                params = {'order_id': order_id, 'csrfmiddlewaretoken': csrf}
                // 发起ajax post请求, 访问/order/pay, 传递参数:order_id
                $.post('{% url 'order:pay' %}', params, function (data) {
                    if (data.res == 3){
                        // 引导用户到支付页面 如果使用location订单页面就找不到了,重新打开页面使用 window.open
                        window.open(data.pay_url)
                        // 浏览器访问order/check,获取支付交易结果
                        // ajax post  传递参数:order_id
                        $.post('/order/check', params, function () {
                            if (data.res == 3){
                                alert('支付成功')
                                //重新加载
                                location.reload()
                            }
                            else{
                                alert(data.errmsg)
                            }
                        })
                    }
                    else{
                        alert(data.errmsg)
                    }
                })
            }
            else if (status == 4){
                //其他情况
                // 跳转到评价页面
                location.href = "/order/comment/"+order_id
            }
        })
    </script>

{% endblock bottomfiles %}


P93 订单支付——获取支付结果


点击去支付,会跳转到支付宝的支付页面,要登录账号
进入沙箱找到支付的账号
在支付页面输入用户密码并进行付款,付款成功之后在用户的页面上会出现付款成功,但是目前我们的网站还不知道用户已经付款。这个是支付宝给用户看的,我们的网站并不知道结果,因为我们没有提供地址return_url、notify_url,支付宝无法告知我们是支付结果,但是网站又想获取支付结果,所以需要自己去调用支付宝的交易查询接口alipay.trade.query,获取用户支付结果,最终告诉用户是否支付成功,给用户显示支付结果。
如果用户的浏览器不访问我们的页面,我们就无法访问它,不能告诉用户支付结果。

用户如果不访问我们,便无法告诉用户支付结果。这里需要再添加一部分,需要让用户的浏览器去访问我们的网站,查询支付结果到底有没有成功。

【用户浏览器,访问/order/check获取交易支付结果】《----用户只要进行支付操作,就将交易结果显示给用户。
只要把用户引导到支付页面,写一段js代码,让他往服务器发一段请求,发请求的目的就是为了获取支付结果

// 浏览器访问/order/check,获取支付交易的结果《---考虑这里需不需要传参数?
// ajax post 传递参数:order_id
在回调函数对应data,data中应该告知我们有没有支付成功,根据data的返回结果告知用户该订单支付有没有成功

编写对应的视图函数,在初始化之前都与查询支付结果一致,最后调用的支付宝接口不同

# 请求方式: ajax post
# 前端请求的参数: 订单id(order_id)
# order/pay
class OrderPayView(View):
    """订单支付"""
    def post(self, request):

        # 用户是否登录
        user = request.user
        if not user.is_authenticated:
            return JsonResponse({'res': 0, 'errmsg': '用户未登录'})

        # 接收参数
        order_id = request.POST.get('order_id')

        # 校验参数
        if not order_id:
            return JsonResponse({'res': 1, 'errmsg': '无效的订单'})

        try:
            order = OrderInfo.objects.get(order_id=order_id,
                                          user=user,
                                          pay_method=3,
                                          order_status=1)
        except OrderInfo.DoesNotExist:
            return JsonResponse({'res': 2, 'errmsg': '订单错误'})

        # 业务处理:使用python sdk调用支付宝的查询接口
        # alipay初始化
        app_private_key_string = open("apps/order/app_private_key.pem").read()
        alipay_public_key_string = open("apps/order/alipay_public_key.pem").read()

        # app_private_key_string == """
        #     -----BEGIN RSA PRIVATE KEY-----
        #     base64 encoded content
        #     -----END RSA PRIVATE KEY-----
        # """
        #
        # alipay_public_key_string == """
        #     -----BEGIN PUBLIC KEY-----
        #     base64 encoded content
        #     -----END PUBLIC KEY-----
        # """

        # app_private_key_string = os.path.join(settings.BASE_DIR, 'apps/order/app_private_key.pem')
        # alipay_public_key_string = os.path.join(settings.BASE_DIR, 'apps/order/alipay_public_key.pem')

        alipay = AliPay(
            appid="2016092700608687",  # 应用id
            app_notify_url=None,  # 默认回调url
            app_private_key_string=app_private_key_string,
            # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            alipay_public_key_string=alipay_public_key_string,
            sign_type="RSA2",  # RSA 或者 RSA2
            debug=True  # 默认False, 此处沙箱模拟True
        )
            # 调用支付宝的交易查询接口
        while True:
            response = alipay.api_alipay_trade_query(order_id)

            # response = {
            # "trade_no": "2017032121001004070200176844",  # 支付宝交易号
            # "code": "10000",  # 接口调用是否成功
            # "invoice_amount": "20.00",
            # "open_id": "20880072506750308812798160715407",
            # "fund_bill_list": [
            #     {
            #         "amount": "20.00",
            #         "fund_channel": "ALIPAYACCOUNT"
            #     }
            # ],
            # "buyer_logon_id": "csq***@sandbox.com",
            # "send_pay_date": "2017-03-21 13:29:17",
            # "receipt_amount": "20.00",
            # "out_trade_no": "out_trade_no15",
            # "buyer_pay_amount": "20.00",
            # "buyer_user_id": "2088102169481075",
            # "msg": "Success",
            # "point_amount": "0.00",
            # "trade_status": "TRADE_SUCCESS",  # 支付结果
            # "total_amount": "20.00"
            # }
        # 查询接口调用是否成功
            code = response.get('code')
        # 接口调用是否成功 && 支付是否成功
            if code == '10000' and response.get('trade_status') == 'TRADE_SUCCESS':
                # 支付成功
                # 获取支付宝交易号
                trade_no = response.get('trade_no')
                # 更新订单状态
                order.trade_no = trade_no
                order.order_status = 4  # 待评价
                order.save()
                # 返回应答
                return JsonResponse({'res': 3, 'message': '支付成功'})
            elif code == '40004' or (code == '10000' and response.get('trade_status') == 'WAIT_BUYER_PAY'):
                # 等待买家付款
                import time
                time.sleep(5)
                continue
            else:
                # 支付出错
                return JsonResponse({'res': 4, 'errmsg': '支付失败'})

注:当code返回值为'40004' 时,需要进行处理

 


处理js
$.post('/order/check', params, function () {
    if (data.res == 3){
        alert('支付成功')
        //重新加载
        location.reload()
    }
    else{
        alert(data.errmsg)
    }
})


配置order/chexk的URL,进行测试

P98 订单评论代码走读


目前页面已经可以查询支付结果,页面上已经是待评价,但是后面依旧显示【去付款】,此时应该显示【待评论】,点击之后会跳转到相应的评论页面。查看源代码可以看到,此时td中的自定义属性status=4,这部分可以通过在页面中添加js,根据状态status的不同,改成不同的内容。

评论的代码:遍历每个获取到的a标签,遍历的时候要获取到自定义属性status,
如果status=1,则设置为去支付,
如果status=4,则设置为去评价,
如果status=5,则设置为已完成。    

订单评论URL的设置:
path('comment/(?P<order_id>.+)', CommentView.as_view(), name='comment') # 订单评价

下面如果用户去点击它,不同的状态对应不同的处理
{% block bottomfiles %}
    <script src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
    <script>
        $('.oper_btn').each(function () {
            // 获取status
            status = $(this).attr('status')
            if (status == 1){
                $(this).text('去支付')
            }
            else if (status == 4){
                $(this).text('去评价')
            }
            else if (status == 5){
                $(this).text('已完成')
            }
        })


        $('.oper_btn').click(function () {
            // 获取status
            status = $(this).attr('status')
            // 获取订单id
            order_id = $(this).attr('order_id')
            if (status == 1){
                // 进行支付
                csrf = $('input[name="csrfmiddlewaretoken"]').val()
                // 组织参数
                params = {'order_id': order_id, 'csrfmiddlewaretoken': csrf}
                // 发起ajax post请求, 访问/order/pay, 传递参数:order_id
                $.post('{% url 'order:pay' %}', params, function (data) {
                    if (data.res == 3){
                        // 引导用户到支付页面
                        window.open(data.pay_url)
                        // 浏览器访问order/check,获取支付交易结果
                        // ajax post  传递参数:order_id
                        $.post('/order/check', params, function () {
                            if (data.res == 3){
                                alert('支付成功')
                                //重新加载
                                location.reload()
                            }
                            else{
                                alert(data.errmsg)
                            }
                        })
                    }
                    else{
                        alert(data.errmsg)
                    }
                })
            }
            else if (status == 4){
                //其他情况
                // 跳转到评价页面
                location.href = "/order/comment/"+order_id
            }
        })
    </script>
{% endblock bottomfiles %}


测试js代码无响应时:检查元素--》network

评价页面的地址:/order/comment/order_id
点击去评价---》跳转到对应的页面 /order/comment/order_id,去路由系统中查找对应的views函数。
分析评价页面显示内容:
基本信息和订单页面显示基本一致,只需 在下方添加一个评论的输入框

view中的类视图:【get基与订单页面显示时的处理过程一致,无需详述】
class OrderCommentView(LoginRequiredMixin, View):
    def get(self, request, order_id):
        """展示评论页"""
    ...


在页面order_comment.html中,前面商品显示和订单页面一致,在下方每个商品对应一个评论的输入框,评论框中定义了一个元素input隐藏域
 name="sku_{{ forloop.counter }}"

该元素对应的值:评论的时候需要接收是哪一个商品的评论,隐藏域对应的值是跟这个商品关联的商品id。商品对应的评论内容对应在输入的文本框中,文本框中对应的内容是 name="content_{{ forloop.counter }}"

将其通通放置在表单中,在表单中还包含了两个隐藏域
其一是订单id,你需要对那个订单中的商品信息进行评论,在其中写一个隐藏域为 name="order_id",其值为订单id---》value="{{ order.order_id }}"
 <input type="hidden" name="order_id" value="{{ order.order_id }}">

以及订单中有几个商品total_count,其值为订单中跟订单关联的商品的长度,即订单中商品的个数
<input type="hidden" name="total_count" value="{{ order.order_skus|length }}">

用户输入完后点击提交,订单中的商品都会被提交。

点击提交之后,代表向当前地址发起一个post请求,在对应的视图中会有一个对应的post函数,处理评论内容
在该部分,首先要在地址中获取到订单的id,获取到之后进行数据校验,订单中有几个商品就应该对应多少评论,将total_count值进行接收,接收之后就可以获取到评论一共有多少条。

# 循环获取订单中商品的评论内容《---注:每个商品对应的名字是不一样的,通过forloop.counter1234往下数,遍历几次就应该有几个,在处理的时候也应该通过遍历拿到对应的值,每找到一个就应该获取商品的id,以及对应的评论内容,将其赋值给order_goods的comment,并做一个更新。
如果获取到了所有的订单商品的评论且进行了更新,则将该订单的状态设置为已完成,进行更新,最终将页面跳转到订单页面
拿到数字之后需要循环获取商品的评论,从1到total_count+1,获取到的 第一次就是1,在循环中获取被评论的商品id,名字对应的值是商品的id,获取到值之后即可获取到这是哪一条商品的评论。获取到评论内容sku_1对应下面的content_1,其余同理。没有内容则返回空。

根据order和sku_id查询到order_goods中,并将评论内容写到对应的order_goods.comment中,如果出错,则continue,继续看下一个。如果查到了,则需要将order_goods中的内容修改为接收到的content,并进行保存。

进行测试:下单,评论

此时商品详情页的评论部分应该会显示评论内容(此时在商品介绍下面显示的,进行修改),在detail.html页面写一段js代码

此时对应的元素中添加id="tag_detail",id="tag_comment"

<ul class="detail_tab clearfix">
    <li class="active" id="tag_detail">商品介绍</li>
    <li id="tag_comment">评论</li>
</ul>

<div class="tab_content" id="tab_detail">
    <dl>
        <dt>商品详情:</dt>
        <dd>{{ sku.goods.detail|safe }}</dd>
    </dl>
</div>

# style="display: none;默认不显示
<div class="tab_content" id="tab_comment" style="display: none;">
    <dl>
        {% for sku in sku_orders %}
        <dt>评论时间: {{ sku.update_time }} 用户名:{{ sku.order.user.username }}</dt>
        <dd>评论内容: {{ sku.comment }}</dd>
        {% empty %}
        暂时没有评论信息
        {% endfor %}
    </dl>
</div>

商品介绍和评论只显示一个,且点击哪一个,哪一个应该处于激活状态
class='active'
// 商品介绍与评论tag的切换
$('#tag_detail').click(function () {
  $('#tag_comment').removeClass('active')
  $(this).addClass('active')
  $('#tab_detail').show()
  $('#tab_comment').hide()
})

$('#tag_comment').click(function () {
  $('#tag_detail').removeClass('active')
  $(this).addClass('active')
  $('#tab_comment').show()
  $('#tab_detail').hide()
})
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值