flask 与 小程序 下单提交 订单列表展示

mina/pages/order/index.wxml

<view class="container">
     <view class="address-box">
        <view class="add-address" hidden="{{default_address}}">
            <view class="title" bindtap="addressSet">新增收货地址</view>
        </view>
        <view class="show-address" hidden="{{!default_address}}" bindtap="selectAddress">
            <view class="name-tel">{{default_address.name}}  {{default_address.mobile}}</view>
            <view class="addr-text">{{default_address.detail}}</view>
        </view>
     </view>
     <view class="goods-list">
        <view class="list-title">商品列表</view>
        <view class="a-goods" wx:for-items="{{goods_list}}" wx:key="{{index}}">
            <view class="img-box">
                <image src="{{item.pic_url}}" class="img" />
            </view>
            <view class="text-box">
                <view class="arow arow01">
                    <view class="goods-name">{{item.name}}</view>
                    <view class="goods-price">¥ {{item.price}}</view>
                </view>
                <view class="arow">
                    <view class="goods-label"></view>
                    <view class="goods-num">x {{item.number}}</view>
                </view>
            </view>
        </view>
     </view>
     <view class="peisong-way">
        <view class="row-box">
            <view class="row-label">配送方式</view>
            <view class="right-text" wx:if="{{yun_price > 0}}">快递</view>
            <view class="right-text" wx:if="{{yun_price == 0}}">包邮</view>
        </view>
        <view class="row-box">
            <view class="row-label">备注</view>
            <view class="right-text">
                <input name="remark" type="text" class="liuyan" placeholder="如需备注请输入" />
            </view>
        </view>
     </view>

     <view class="goods-info">
        <view class="row-box">
            <view class="row-label">商品金额</view>
            <view class="right-text">¥ {{pay_price}}</view>
        </view>
        <view class="row-box">
            <view class="row-label">运费</view>
            <view class="right-text">+ ¥ {{yun_price}}</view>
        </view>
     </view>

     <view class="jiesuan-box">
        <view class="left-price">
            <view class="total">合计:¥ {{total_price}}</view>
        </view>
        <button class="to-pay-btn" bindtap="createOrder">提交订单</button>
    </view>
</view>

mina/pages/order/index.js

//获取应用实例
var app = getApp();

Page({
    data: {
        goods_list: [],
        default_address: null,
        yun_price: "0.00",
        pay_price: "0.00",
        total_price: "0.00",
        params: null,
        express_address_id:0
    },
    onLoad: function (e) {
        var that = this;
        that.setData({
            params: JSON.parse(e.data)
        });
    },
    onShow: function () {
        var that = this;
         this.getOrderInfo();
    },
    createOrder: function (e) {
        wx.showLoading();
        var that = this;
        var data = {
            type: this.data.params.type,
            goods: JSON.stringify(this.data.params.goods),
            express_address_id: that.data.default_address.id
        };
        wx.request({
            url: app.buildUrl("/order/create"),
            header: app.getRequestHeader(),
            method: 'POST',
            data: data,
            success: function (res) {
                wx.hideLoading();
                var resp = res.data;
                if (resp.code != 200) {
                    app.alert({"content": resp.msg});
                    return;
                }
                wx.navigateTo({
                    url: "/pages/my/order_list"
                });
            }
        });

    },
    addressSet: function () {
        wx.navigateTo({
            url: "/pages/my/addressSet?id=0"
        });
    },
    selectAddress: function () {
        wx.navigateTo({
            url: "/pages/my/addressList"
        });
    },
    getOrderInfo: function () {
        var that = this;
        var data = {
            type: this.data.params.type,
            goods: JSON.stringify(this.data.params.goods)
        };
        wx.request({
            url: app.buildUrl("/order/info"),
            header: app.getRequestHeader(),
            method: 'POST',
            data: data,
            success: function (res) {
                var resp = res.data;
                if (resp.code != 200) {
                    app.alert({"content": resp.msg});
                    return;
                }

                that.setData({
                    goods_list: resp.data.food_list,
                    default_address: resp.data.default_address,
                    yun_price: resp.data.yun_price,
                    pay_price: resp.data.pay_price,
                    total_price: resp.data.total_price,
                });

                if( that.data.default_address ){
                    that.setData({
                         express_address_id: that.data.default_address.id
                    });
                }
            }
        });
    }

});

onLoad:function(e){}

params: JSON.parse(e.data)

JSON.parse()是JavaScript中的一个方法,用于将一个JSON字符串解析为对应的JavaScript对象。它接受一个JSON字符串作为参数,并返回一个JavaScript对象。

在你提供的引用中,JSON.parse()的用法如下:

var str = '{"name":"huangxiaojian","age":"23"}';
var obj = JSON.parse(str);
console.log(obj); // 输出:{ name: "huangxiaojian", age: "23" }

JSON.stringify()是JavaScript中的另一个方法,用于将一个JavaScript对象转换为对应的JSON字符串。它接受一个JavaScript对象作为参数,并返回一个JSON字符串。

在你提供的引用中,JSON.stringify()的用法如下:

var obj = { a: 1, b: 2 };
var str = JSON.stringify(obj);
console.log(str); // 输出:{"a":1,"b":2}

params: JSON.parse(e.data)的意思是将变量e.data中的JSON字符串解析为JavaScript对象。

createOrder:function(e){  }

wx.showLoading():

wx.showLoading(Object object) | 微信开放文档

显示 loading 提示框。需主动调用 wx.hideLoading 才能关闭提示框

wx.hideLoading():

web/controllers/api/Order.py

# -*- coding: utf-8 -*-
from web.controllers.api import route_api
from flask import request, jsonify,g
from application import app, db
import json, decimal
from common.models.food.Food import Food
from common.models.pay.PayOrder import PayOrder
from common.libs.UrlManager import UrlManager
from common.libs.Helper import getCurrentDate
from common.libs.pay.PayService import PayService
from common.libs.pay.WeChatService import WeChatService
from common.libs.member.CartService import CartService
from common.models.member.MemberAddress import MemberAddress
from common.models.member.OauthMemberBind import OauthMemberBind


@route_api.route("/order/info", methods=[ "POST" ])
def orderInfo():
	resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
	req = request.values
	params_goods = req['goods'] if 'goods' in req else None
	member_info = g.member_info
	params_goods_list = []
	if params_goods:
		params_goods_list = json.loads(params_goods)

	food_dic = {}
	for item in params_goods_list:
		food_dic[item['id']] = item['number']

	food_ids = food_dic.keys()
	food_list = Food.query.filter(Food.id.in_(food_ids)).all()
	data_food_list = []
	yun_price = pay_price = decimal.Decimal(0.00)
	if food_list:
		for item in food_list:
			tmp_data = {
				"id": item.id,
				"name": item.name,
				"price": str(item.price),
				'pic_url': UrlManager.buildImageUrl(item.main_image),
				'number': food_dic[item.id]
			}
			pay_price = pay_price + item.price * int( food_dic[item.id] )
			data_food_list.append(tmp_data)

	# 获取地址
	address_info = MemberAddress.query.filter_by( is_default = 1,member_id = member_info.id,status = 1 ).first()
	default_address = ''
	if address_info:
		default_address = {
			"id": address_info.id,
			"name": address_info.nickname,
			"mobile": address_info.mobile,
			"address":"%s%s%s%s"%( address_info.province_str,address_info.city_str,address_info.area_str,address_info.address )
		}

	resp['data']['food_list'] = data_food_list
	resp['data']['pay_price'] = str(pay_price)
	resp['data']['yun_price'] = str(yun_price)
	resp['data']['total_price'] = str(pay_price + yun_price)
	resp['data']['default_address'] = default_address
	return jsonify(resp)

@route_api.route("/order/create", methods=[ "POST"])
def orderCreate():
	resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
	req = request.values
	type = req['type'] if 'type' in req else ''
	note = req['note'] if 'note' in req else ''
	express_address_id = int( req['express_address_id'] ) if 'express_address_id' in req and req['express_address_id'] else 0
	params_goods = req['goods'] if 'goods' in req else None

	items = []
	if params_goods:
		items = json.loads(params_goods)

	if len( items ) < 1:
		resp['code'] = -1
		resp['msg'] = "下单失败:没有选择商品~~"
		return jsonify(resp)

	address_info = MemberAddress.query.filter_by( id = express_address_id ).first()
	if not address_info or not address_info.status:
		resp['code'] = -1
		resp['msg'] = "下单失败:快递地址不对~~"
		return jsonify(resp)

	member_info = g.member_info
	target = PayService()
	params = {
		"note":note,
		'express_address_id':address_info.id,
		'express_info':{
			'mobile':address_info.mobile,
			'nickname':address_info.nickname,
			"address":"%s%s%s%s"%( address_info.province_str,address_info.city_str,address_info.area_str,address_info.address )
		}
	}
	resp = target.createOrder( member_info.id ,items ,params)
	#如果是来源购物车的,下单成功将下单的商品去掉
	if resp['code'] == 200 and type == "cart":
		CartService.deleteItem( member_info.id,items )

	return jsonify( resp )

@route_api.route("/order/pay", methods=[ "POST"])
def orderPay():
	resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
	member_info = g.member_info
	req = request.values
	order_sn = req['order_sn'] if 'order_sn' in req else ''
	pay_order_info = PayOrder.query.filter_by( order_sn = order_sn,member_id = member_info.id ).first()
	if not pay_order_info:
		resp['code'] = -1
		resp['msg'] = "系统繁忙。请稍后再试~~"
		return jsonify(resp)

	oauth_bind_info = OauthMemberBind.query.filter_by( member_id =  member_info.id ).first()
	if not oauth_bind_info:
		resp['code'] = -1
		resp['msg'] = "系统繁忙。请稍后再试~~"
		return jsonify(resp)

	config_mina = app.config['MINA_APP']
	notify_url = app.config['APP']['domain'] + config_mina['callback_url']

	target_wechat = WeChatService( merchant_key=config_mina['paykey'] )

	data = {
		'appid': config_mina['appid'],
		'mch_id': config_mina['mch_id'],
		'nonce_str': target_wechat.get_nonce_str(),
		'body': '订餐',  # 商品描述
		'out_trade_no': pay_order_info.order_sn,  # 商户订单号
		'total_fee': int( pay_order_info.total_price * 100 ),
		'notify_url': notify_url,
		'trade_type': "JSAPI",
		'openid': oauth_bind_info.openid
	}

	pay_info = target_wechat.get_pay_info( pay_data=data)

	#保存prepay_id为了后面发模板消息
	pay_order_info.prepay_id = pay_info['prepay_id']
	db.session.add( pay_order_info )
	db.session.commit()

	resp['data']['pay_info'] = pay_info
	return jsonify(resp)

@route_api.route("/order/callback", methods=[ "POST"])
def orderCallback():
	result_data = {
		'return_code': 'SUCCESS',
		'return_msg': 'OK'
	}
	header = {'Content-Type': 'application/xml'}
	config_mina = app.config['MINA_APP']
	target_wechat = WeChatService(merchant_key=config_mina['paykey'])
	callback_data = target_wechat.xml_to_dict( request.data )
	app.logger.info( callback_data  )
	sign = callback_data['sign']
	callback_data.pop( 'sign' )
	gene_sign = target_wechat.create_sign( callback_data )
	app.logger.info(gene_sign)
	if sign != gene_sign:
		result_data['return_code'] = result_data['return_msg'] = 'FAIL'
		return target_wechat.dict_to_xml(result_data), header
	if callback_data['result_code'] != 'SUCCESS':
		result_data['return_code'] = result_data['return_msg'] = 'FAIL'
		return target_wechat.dict_to_xml(result_data), header

	order_sn = callback_data['out_trade_no']
	pay_order_info = PayOrder.query.filter_by(order_sn=order_sn).first()
	if not pay_order_info:
		result_data['return_code'] = result_data['return_msg'] = 'FAIL'
		return target_wechat.dict_to_xml(result_data), header

	if int( pay_order_info.total_price * 100  ) != int( callback_data['total_fee'] ):
		result_data['return_code'] = result_data['return_msg'] = 'FAIL'
		return target_wechat.dict_to_xml(result_data), header

	if pay_order_info.status == 1:
		return target_wechat.dict_to_xml(result_data), header

	target_pay = PayService()
	target_pay.orderSuccess( pay_order_id = pay_order_info.id,params = { "pay_sn":callback_data['transaction_id'] } )
	target_pay.addPayCallbackData( pay_order_id = pay_order_info.id, data = request.data)
	return target_wechat.dict_to_xml(result_data), header

@route_api.route("/order/ops", methods=[ "POST"])
def orderOps():
	resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
	req = request.values
	member_info = g.member_info
	order_sn = req['order_sn'] if 'order_sn' in req else ''
	act = req['act'] if 'act' in req else ''
	pay_order_info = PayOrder.query.filter_by(order_sn=order_sn, member_id=member_info.id).first()
	if not pay_order_info:
		resp['code'] = -1
		resp['msg'] = "系统繁忙。请稍后再试~~"
		return jsonify(resp)


	if act == "cancel":
		target_pay = PayService( )
		ret = target_pay.closeOrder( pay_order_id=pay_order_info.id )
		if not ret:
			resp['code'] = -1
			resp['msg'] = "系统繁忙。请稍后再试~~"
			return jsonify(resp)
	elif act == "confirm":
		pay_order_info.express_status = 1
		pay_order_info.updated_time = getCurrentDate()
		db.session.add( pay_order_info )
		db.session.commit()

	return jsonify(resp)




@route_api.route("/order/create", methods=[ "POST"])

type: cart
goods: [{"id":23, "price":"36.00", "number":1}, {"id":18, "price":"36.00", "number":1}]

json.loads()

是Python标准库json模块中的一个方法,用于将JSON字符串转换为Python数据类型。它的使用方法如下所示:

import json

json_str = '{"name": "John", "age": 30, "city": "New York"}'
data = json.loads(json_str)

print(data)  # 输出:{'name': 'John', 'age': 30, 'city': 'New York'}

在上面的例子中,我们首先导入了json模块。然后,我们定义了一个JSON字符串json_str,其中包含了一个名为"name"的键和对应的值"John",一个名为"age"的键和对应的值30,以及一个名为"city"的键和对应的值"New York"。接下来,我们使用json.loads()方法将JSON字符串转换为Python数据类型,并将结果赋值给变量data。最后,我们打印出data的值,即转换后的Python字典。

需要注意的是,json.loads()只适用于读取JSON字符串,如果想要从JSON文件中读取数据,请使用json.load()方法

jsonify()

jsonify()是Flask框架中的一个函数,用于将Python对象转换为JSON格式的响应。它会自动设置响应头部的"Content-Type"为"application/json",简化了返回JSON数据的操作。

以下是两种使用jsonify()函数返回JSON数据的例子:

使用字典作为参数:

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/json1', methods=['GET'])
def json_demo1():
    return jsonify({
        "username": 'yoyo',
        "email": "111@qq.com"
    })

if __name__ == '__main__':
    app.run()

使用键值对作为参数:

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/json2', methods=['GET'])
def json_demo2():
    return jsonify(
        username="yoyo",
        email="111@qq.com"
    )

if __name__ == '__main__':
    app.run()

这两个例子中,当访问对应的路由时,会返回一个JSON格式的响应,其中包含了指定的键值对或字典。

Food.query.filter(Food.id.in_(food_ids)).all()中in的作用

Food.query.filter(Food.id.in_(food_ids)).all()中,in_是一个SQLAlchemy的查询操作符,用于判断一个字段的值是否在给定的列表中。它可以用于过滤查询结果,只返回满足条件的记录。

具体来说,Food.id.in_(food_ids)表示筛选出Food表中id字段的值在food_ids列表中的记录。这样,filter()方法就会返回满足这个条件的所有记录。

举个例子,假设food_ids是一个包含[1, 2, 3]的列表,那么Food.query.filter(Food.id.in_(food_ids)).all()将返回Food表中id为1、2、3的所有记录

@route_api.route("/order/callback", methods=[ "POST"])

.pop()

.pop()函数用于从列表中删除指定位置的元素,并返回被删除的元素。如果没有指定位置,默认删除列表中的最后一个元素。

以下是.pop()函数的两个例子:

1  弹出列表中的第一个元素:

names = ['Lee', 'Summer', 'Andy']
list_pop = names.pop(0)
print("删除的项:", list_pop)  # 输出:删除的项: Lee
print("列表中的值:", names)  # 输出:列表中的值: ['Summer', 'Andy']

2  弹出列表中的最后三个元素:

nums = [2022, 9, 5, 437]
nums.pop()  # 弹出最后一个元素,返回437
nums.pop()  # 弹出倒数第二个元素,返回5
nums.pop()  # 弹出倒数第三个元素,返回9
print(nums)  # 输出:[2022]

注意:.pop()函数一次只能删除一个元素,如果指定了多个位置,会报错TypeError: pop expected at most 1 argument。

app.logger.info()

app.logger.info()是Flask框架中用于记录信息级别日志的方法。它可以用来记录一些重要的信息,以便在程序运行时进行调试和监控。

以下是两种使用app.logger.info()的例子:

1 在Flask路由中使用app.logger.info()记录信息:

@app.route('/')
def main():
    app.logger.info("This is an info message")
    return "logger levels!"

2 使用Python的logging模块记录信息:

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

logger.info("This is an info message")

request.data request.values 区别是什么

request.data和request.values是Flask框架中用于获取请求数据的两种方式,它们的区别如下:

  1. request.data:

    • request.data返回的是原始的请求数据,通常用于获取POST请求中的原始数据。
    • 它返回的是一个字节字符串,需要进行解码才能得到可读的数据。
    • 适用于处理二进制数据或非表单数据,例如JSON数据。
  2. request.values:

    • request.values返回的是一个字典,包含了请求中的所有参数和值。
    • 它会自动根据请求的Content-Type进行解析,支持多种数据格式,包括表单数据、URL查询字符串和JSON数据。
    • 适用于处理表单数据或URL查询字符串。

下面是一个示例演示:

from flask import Flask, request

app = Flask(__name__)

@app.route('/example', methods=['POST'])
def example():
    data = request.data.decode('utf-8')
    values = request.values

    return f"request.data: {data}\nrequest.values: {values}"

if __name__ == '__main__':
    app.run()

通过发送POST请求到http://localhost:5000/example,可以看到返回结果中包含了request.data和request.values的值。

sign 和gene_sign请详细述说一下这两个有什么不同

sign和gene_sign是两个变量,它们的作用和含义如下:

  1. sign:sign是从微信返回的数据中获取的签名。在这段代码中,callback_data是从微信返回的xml信息中解析出来的数据,其中包含了一个sign字段,表示微信返回数据的签名。这个签名是用于验证数据的完整性和真实性的。

  2. gene_sign:gene_sign是通过调用WeChatService类的create_sign方法生成的签名。在这段代码中,callback_data是从微信返回的xml信息中解析出来的数据,通过调用target_wechat.create_sign方法,传入callback_data作为参数,生成一个签名。这个签名是用于与微信返回的签名进行比对,确认数据的完整性和真实性。

这两个签名的不同之处在于生成的方式和用途不同。sign是从微信返回的数据中获取的,用于验证微信返回数据的完整性和真实性。gene_sign是通过调用create_sign方法生成的,用于与微信返回的签名进行比对,确认数据的完整性和真实性。

数据库common/models/member/MemberAddress

DROP TABLE IF EXISTS `member_address`;
CREATE TABLE `member_address` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `member_id` int(11) NOT NULL DEFAULT '0' COMMENT '会员id',
  `nickname` varchar(20) NOT NULL DEFAULT '' COMMENT '收货人姓名',
  `mobile` varchar(11) NOT NULL DEFAULT '' COMMENT '收货人手机号码',
  `province_id` int(11) NOT NULL DEFAULT '0' COMMENT '省id',
  `province_str` varchar(50) NOT NULL DEFAULT '' COMMENT '省名称',
  `city_id` int(11) NOT NULL DEFAULT '0' COMMENT '城市id',
  `city_str` varchar(50) NOT NULL DEFAULT '' COMMENT '市名称',
  `area_id` int(11) NOT NULL DEFAULT '0' COMMENT '区域id',
  `area_str` varchar(50) NOT NULL DEFAULT '' COMMENT '区域名称',
  `address` varchar(100) NOT NULL DEFAULT '' COMMENT '详细地址',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效 1:有效 0:无效',
  `is_default` TINYINT(1)  NOT NULL  DEFAULT '0'  COMMENT '默认地址',
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后一次更新时间',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入时间',
  PRIMARY KEY (`id`),
  KEY `idx_member_id_status` (`member_id`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员收货地址';
flask-sqlacodegen 'mysql://root:root@127.0.0.1/food_db' --tables member_address --outfile "common/models/member/MemberAddress.py"  --flask

注意: Order.py   Cart.py Member.py 这些文件要导入到__init__.py中

若不导入则在小程序页面中显示  页面不存在

web/controllers/api/__init__.py

# -*- coding: utf-8 -*-
from flask import Blueprint
route_api = Blueprint( 'api_page',__name__ )
from web.controllers.api.Member import *
from web.controllers.api.Food import *
from web.controllers.api.Order import *

from web.controllers.api.Cart import *


@route_api.route("/")
def index():
    return "Mina Api V1.0~~"

common/libs/pay/PayService.py

# -*- coding: utf-8 -*-
import hashlib,time,random,decimal,json
from application import  app,db
from common.models.food.Food import Food
from common.models.food.FoodSaleChangeLog import FoodSaleChangeLog
from common.models.pay.PayOrder import PayOrder
from common.models.pay.PayOrderItem import PayOrderItem
from common.models.pay.PayOrderCallbackData import PayOrderCallbackData
from common.libs.Helper import getCurrentDate
from common.libs.queue.QueueService import QueueService
from common.libs.food.FoodService import FoodService
class PayService():

    def __init__(self):
        pass

    def createOrder(self,member_id,items = None,params = None):
        resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
        pay_price  = decimal.Decimal( 0.00 )
        continue_cnt = 0
        food_ids = []
        for item in items:
            if decimal.Decimal( item['price'] ) < 0 :
                continue_cnt += 1
                continue

            pay_price = pay_price +  decimal.Decimal( item['price'] ) * int( item['number'] )
            food_ids.append( item['id'] )

        if continue_cnt >= len(items ) :
            resp['code'] = -1
            resp['msg'] = '商品items为空~~'
            return resp

        yun_price = params['yun_price'] if params and 'yun_price' in params else 0
        note = params['note'] if params and 'note' in params else ''
        express_address_id = params['express_address_id'] if params and 'express_address_id' in params else 0
        express_info = params['express_info'] if params and 'express_info' in params else {}
        yun_price = decimal.Decimal( yun_price )
        total_price = pay_price + yun_price
        try:
            #为了防止并发库存出问题了,我们坐下selectfor update, 这里可以给大家演示下
            tmp_food_list = db.session.query( Food ).filter( Food.id.in_( food_ids ) )\
                .with_for_update().all()

            tmp_food_stock_mapping = {}
            for tmp_item in tmp_food_list:
                tmp_food_stock_mapping[ tmp_item.id ] = tmp_item.stock

            model_pay_order = PayOrder()
            model_pay_order.order_sn = self.geneOrderSn()
            model_pay_order.member_id = member_id
            model_pay_order.total_price = total_price
            model_pay_order.yun_price = yun_price
            model_pay_order.pay_price = pay_price
            model_pay_order.note = note
            model_pay_order.status = -8
            model_pay_order.express_status = -8
            model_pay_order.express_address_id = express_address_id
            model_pay_order.express_info = json.dumps(  express_info )
            model_pay_order.updated_time = model_pay_order.created_time = getCurrentDate()
            db.session.add( model_pay_order )
            #db.session.flush()
            for item in items:
                tmp_left_stock =  tmp_food_stock_mapping[ item['id'] ]

                if decimal.Decimal(item['price']) < 0:
                    continue

                if int( item['number'] ) > int( tmp_left_stock ):
                    raise Exception( "您购买的这美食太火爆了,剩余:%s,你购买%s~~"%( tmp_left_stock,item['number'] ) )

                tmp_ret = Food.query.filter_by( id = item['id'] ).update({
                    "stock":int(tmp_left_stock) - int(item['number'])
                })
                if not tmp_ret:
                    raise Exception("下单失败请重新下单")

                tmp_pay_item = PayOrderItem()
                tmp_pay_item.pay_order_id = model_pay_order.id
                tmp_pay_item.member_id = member_id
                tmp_pay_item.quantity = item['number']
                tmp_pay_item.price = item['price']
                tmp_pay_item.food_id = item['id']
                tmp_pay_item.note = note
                tmp_pay_item.updated_time = tmp_pay_item.created_time = getCurrentDate()
                db.session.add( tmp_pay_item )
                #db.session.flush()

                FoodService.setStockChangeLog( item['id'],-item['number'],"在线购买" )
            db.session.commit()
            resp['data'] = {
                'id' : model_pay_order.id,
                'order_sn' : model_pay_order.order_sn,
                'total_price':str( total_price )
            }
        except Exception as e:
            db.session.rollback()
            print( e )
            resp['code'] = -1
            resp['msg'] = "下单失败请重新下单"
            resp['msg'] = str(e)
            return resp
        return resp

    def closeOrder(self,pay_order_id = 0):
        if pay_order_id < 1:
            return False
        pay_order_info = PayOrder.query.filter_by( id =  pay_order_id ,status = -8 ).first()
        if not pay_order_info:
            return False

        pay_order_items = PayOrderItem.query.filter_by( pay_order_id = pay_order_id ).all()
        if pay_order_items:
            #需要归还库存
            for item in pay_order_items:
                tmp_food_info = Food.query.filter_by( id = item.food_id ).first()
                if tmp_food_info:
                    tmp_food_info.stock = tmp_food_info.stock + item.quantity
                    tmp_food_info.updated_time = getCurrentDate()
                    db.session.add( tmp_food_info )
                    db.session.commit()
                    FoodService.setStockChangeLog( item.food_id, item.quantity, "订单取消")

        pay_order_info.status = 0
        pay_order_info.updated_time = getCurrentDate()
        db.session.add( pay_order_info )
        db.session.commit()
        return True

    def orderSuccess(self,pay_order_id = 0,params = None):
        try:
            pay_order_info = PayOrder.query.filter_by( id = pay_order_id ).first()
            if not pay_order_info or pay_order_info.status not in [ -8,-7 ]:
                return True

            pay_order_info.pay_sn = params['pay_sn'] if params and 'pay_sn' in params else ''
            pay_order_info.status = 1
            pay_order_info.express_status = -7
            pay_order_info.updated_time = getCurrentDate()
            db.session.add( pay_order_info  )


            pay_order_items = PayOrderItem.query.filter_by( pay_order_id = pay_order_id ).all()
            for order_item in pay_order_items:
                tmp_model_sale_log = FoodSaleChangeLog()
                tmp_model_sale_log.food_id = order_item.food_id
                tmp_model_sale_log.quantity = order_item.quantity
                tmp_model_sale_log.price = order_item.price
                tmp_model_sale_log.member_id = order_item.member_id
                tmp_model_sale_log.created_time = getCurrentDate()
                db.session.add( tmp_model_sale_log )

            db.session.commit()
        except Exception as e:
            db.session.rollback()
            print(e)
            return False

        #加入通知队列,做消息提醒和
        QueueService.addQueue( "pay",{
            "member_id": pay_order_info.member_id,
            "pay_order_id":pay_order_info.id
        })
        return True

    def addPayCallbackData(self,pay_order_id = 0,type = 'pay',data = ''):
        model_callback = PayOrderCallbackData()
        model_callback.pay_order_id = pay_order_id
        if type == "pay":
            model_callback.pay_data = data
            model_callback.refund_data = ''
        else:
            model_callback.refund_data = data
            model_callback.pay_data = ''

        model_callback.created_time = model_callback.updated_time = getCurrentDate()
        db.session.add( model_callback )
        db.session.commit()
        return True

    def geneOrderSn(self):
        m = hashlib.md5()
        sn = None
        while True:
            str = "%s-%s"%( int( round( time.time() * 1000) ),random.randint( 0,9999999 ) )
            m.update(str.encode("utf-8"))
            sn = m.hexdigest()
            if not PayOrder.query.filter_by( order_sn = sn  ).first():
                break
        return sn

createOrder()

.with_for_update()

.with_for_update()是SQLAlchemy中的一个方法,用于在数据库事务中锁定查询结果,以防止其他事务对查询结果进行修改。

这个方法通常与filter()filter_by()一起使用,用于指定查询条件。在查询结果上调用.with_for_update()方法后,查询结果将被锁定,其他事务将无法修改这些结果,直到当前事务提交或回滚。

以下是两个例子来演示.with_for_update()的使用:

在模型查询中使用.with_for_update()

# 假设有一个名为User的模型类
user = User.query.filter(User.id == 1).with_for_update().first()
# 对查询结果进行操作
user.name = 'New Name'
db.session.commit()

在数据库会话中使用.with_for_update()

# 假设有一个名为Address的模型类
addr = Address.query.filter_by(user_id=3).with_for_update().first()
if addr.status == 0:
    addr.status = 1
    db.session.commit()

这些例子展示了如何在查询结果上使用.with_for_update()方法,并在事务中对查询结果进行修改。

.with_for_update()的作用是在数据库事务中对查询结果进行加锁,以确保其他事务无法修改这些记录,直到当前事务结束。

在给定的代码中,.with_for_update()被用于查询语句中的db.session.query()方法后面。这意味着查询结果将被锁定,并且其他事务无法修改这些记录,直到当前事务结束。

这在并发环境中非常有用,可以防止其他事务在当前事务处理期间修改查询结果。这样可以确保查询结果的一致性和可靠性。

json.dumps()

Python JSON_w3cschool

函数描述
json.dumps将 Python 对象编码成 JSON 字符串
json.loads将已编码的 JSON 字符串解码为 Python 对象

orderSuccess()

支付成功后将

PayOrder里的状态 status 置为   1:支付完成,

快递状态 express_status  置为       -7 已付款待发货

代码中为什么用try: except Exception as e: 结构

在代码中使用try: except Exception as e:结构是为了捕获可能发生的异常并进行相应的处理。这样可以保证程序在遇到异常时不会崩溃,而是能够继续执行下去或者进行相应的错误处理。

try: except Exception as e:结构的作用是在try块中执行一段可能会引发异常的代码,如果在执行过程中发生了异常,就会跳转到对应的except块中进行异常处理。Exception是所有异常的基类,通过as e可以将异常对象赋值给变量e,以便在except块中进行进一步的处理或打印错误信息。

在给定的代码中,使用try: except Exception as e:结构是为了捕获可能发生的异常,并在异常发生时进行相应的处理。具体的处理方式可能包括记录日志、返回错误信息或执行其他操作,以保证程序的正常运行和错误处理。


订单列表展示功能

mina/pages/my/order_list.wxml

<view class="container">
    <view class="status-box">
        <view bindtap="statusTap" class="status-label {{index == currentType ? 'active' : ''}}" wx:for-items="{{statusType}}" wx:key="{{index}}" data-index="{{index}}">
            {{item}}
            <view class="{{tabClass[index]}}"></view>
        </view>
    </view>
    <view class="no-order" wx:if="{{!order_list.length}}">
        <image src="/images/no-order.png" class="no-order-img"></image>
        <view class="text">暂无订单</view>
    </view>
    <view class="order-list" wx:if="{{order_list.length}}">
        <view class="a-order" wx:for="{{order_list}}" wx:key="{{index}}" wx:for-item="item">
            <view class="order-date" data-id="{{item.order_sn}}" bindtap="orderDetail">
                <view class="date-box">下单时间:{{item.date}}</view>
                <view class="status {{(item.status==0 || item.status==1) ? '':'red'}}">{{item.status_desc}}</view>
            </view>
            <view class="goods-info"  data-id="{{item.order_sn}}" bindtap="orderDetail">
                <view class="goods-des">
                   <view>订单号:{{item.order_number}} </view>
                   <view wx:if="{{item.note && item.note != ''}}">备注: {{item.note}}</view>
                </view>
            </view>
            <view >
                <scroll-view class="goods-img-container" scroll-x="true">
                    <view class="img-box" wx:for="{{item.goods_list}}" wx:for-item="itemGood">
                        <image src="{{itemGood.pic_url}}" class="goods-img"></image>
                    </view>
                </scroll-view>
            </view>
            <view class="price-box">
                <view class="total-price">合计:¥ {{item.total_price}}</view>
                <view class="btn cancel-btn" bindtap="orderCancel" data-id="{{item.order_sn}}" wx:if="{{item.status==-8}}">取消订单</view>
                <view class="btn topay-btn" bindtap="toPay" data-id="{{item.order_sn}}" wx:if="{{item.status==-8}}">马上付款</view>

                <view class="btn topay-btn" bindtap="orderConfirm" data-id="{{item.order_sn}}" wx:if="{{item.status==-6}}">确认收货</view>
                <view class="btn topay-btn" bindtap="orderComment" data-id="{{item.order_sn}}" wx:if="{{item.status==-5}}">走,去评价</view>
            </view>
        </view>
    </view>
</view>

mina/pages/my/order_list.js

var app = getApp();
Page({
    data: {
        order_list:[],
        statusType: ["待付款", "待发货", "待确认", "待评价", "已完成","已关闭"],
        status:[ "-8","-7","-6","-5","1","0" ],
        currentType: 0,
        tabClass: ["", "", "", "", "", ""]
    },
    statusTap: function (e) {
        var curType = e.currentTarget.dataset.index;
        this.setData({
            currentType: curType
        });
        this.getPayOrder();
    },
    orderDetail: function (e) {
        wx.navigateTo({
            url: "/pages/my/order_info?order_sn=" + e.currentTarget.dataset.id
        })
    },
    onLoad: function (options) {
        // 生命周期函数--监听页面加载
    },
    onShow: function () {
        this.getPayOrder();
    },
    orderCancel:function( e ){
        this.orderOps( e.currentTarget.dataset.id,"cancel","确定取消订单?" );
    },
    getPayOrder:function(){
        var that = this;
        wx.request({
            url: app.buildUrl("/my/order"),
            header: app.getRequestHeader(),
            data: {
                status: that.data.status[ that.data.currentType ]
            },
            success: function (res) {
                var resp = res.data;
                if (resp.code != 200) {
                    app.alert({"content": resp.msg});
                    return;
                }

                that.setData({
                   order_list:resp.data.pay_order_list
                });
            }
        });
    },
    toPay:function( e ){
        var that = this;
        wx.request({
            url: app.buildUrl("/order/pay"),
            header: app.getRequestHeader(),
            method: 'POST',
            data: {
                order_sn: e.currentTarget.dataset.id
            },
            success: function (res) {
                var resp = res.data;
                if (resp.code != 200) {
                    app.alert({"content": resp.msg});
                    return;
                }
                var pay_info = resp.data.pay_info;
                wx.requestPayment({
                    'timeStamp': pay_info.timeStamp,
                    'nonceStr': pay_info.nonceStr,
                    'package': pay_info.package,
                    'signType': 'MD5',
                    'paySign': pay_info.paySign,
                    'success': function (res) {
                    },
                    'fail': function (res) {
                    }
                });
            }
        });
    },
    orderConfirm:function( e ){
        this.orderOps( e.currentTarget.dataset.id,"confirm","确定收到?" );
    },
    orderComment:function( e ){
        wx.navigateTo({
            url: "/pages/my/comment?order_sn=" + e.currentTarget.dataset.id
        });
    },
    orderOps:function(order_sn,act,msg){
        var that = this;
        var params = {
            "content":msg,
            "cb_confirm":function(){
                wx.request({
                    url: app.buildUrl("/order/ops"),
                    header: app.getRequestHeader(),
                    method: 'POST',
                    data: {
                        order_sn: order_sn,
                        act:act
                    },
                    success: function (res) {
                        var resp = res.data;
                        app.alert({"content": resp.msg});
                        if ( resp.code == 200) {
                            that.getPayOrder();
                        }
                    }
                });
            }
        };
        app.tip( params );
    }
});

web/controllers/api/My.py

# -*- coding: utf-8 -*-
from web.controllers.api import route_api
from flask import request, jsonify,g
from common.models.food.Food import Food
from application import app,db
from common.models.pay.PayOrder import PayOrder
from common.models.pay.PayOrderItem import PayOrderItem
from common.libs.UrlManager import UrlManager
from common.libs.Helper import selectFilterObj,getDictFilterField,getCurrentDate
from common.models.member.MemberComments import MemberComments
import json,datetime

@route_api.route("/my/order")
def myOrderList():
	resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
	member_info = g.member_info
	req = request.values
	status = int( req['status'] ) if 'status' in req else 0
	query = PayOrder.query.filter_by( member_id = member_info.id )
	if status == -8 :#等待付款
		query = query.filter( PayOrder.status == -8 )
	elif status == -7:#待发货
		query = query.filter( PayOrder.status == 1,PayOrder.express_status == -7,PayOrder.comment_status == 0 )
	elif status == -6:#待确认
		query = query.filter(PayOrder.status == 1, PayOrder.express_status == -6,PayOrder.comment_status == 0)
	elif status == -5:#待评价
		query = query.filter(PayOrder.status == 1, PayOrder.express_status == 1,PayOrder.comment_status == 0)
	elif status == 1:#已完成
		query = query.filter(PayOrder.status == 1, PayOrder.express_status == 1,PayOrder.comment_status == 1 )
	else:
		query = query.filter( PayOrder.status == 0 )

	pay_order_list = query.order_by( PayOrder.id.desc() ).all()
	data_pay_order_list = []
	if pay_order_list:
		pay_order_ids = selectFilterObj( pay_order_list,"id" )
		pay_order_item_list = PayOrderItem.query.filter( PayOrderItem.pay_order_id.in_( pay_order_ids ) ).all()
		food_ids = selectFilterObj( pay_order_item_list,"food_id" )
		food_map = getDictFilterField( Food,Food.id,"id",food_ids )
		pay_order_item_map = {}
		if pay_order_item_list:
			for item in pay_order_item_list:
				if item.pay_order_id not in pay_order_item_map:
					pay_order_item_map[ item.pay_order_id ] = []

				tmp_food_info = food_map[ item.food_id ]
				pay_order_item_map[item.pay_order_id].append({
					'id':item.id,
					'food_id':item.food_id,
					'quantity':item.quantity,
					'price':str( item.price ),
					'pic_url':UrlManager.buildImageUrl( tmp_food_info.main_image ),
					'name':tmp_food_info.name
				})


		for item in pay_order_list:
			tmp_data = {
				'status':item.pay_status,
				'status_desc':item.status_desc,
				'date':item.created_time.strftime("%Y-%m-%d %H:%M:%S"),
				'order_number':item.order_number,
				'order_sn':item.order_sn,
				'note':item.note,
				'total_price':str( item.total_price ),
				'goods_list':pay_order_item_map[ item.id ]
			}

			data_pay_order_list.append( tmp_data )
	resp['data']['pay_order_list'] = data_pay_order_list
	return jsonify(resp)

@route_api.route("/my/order/info")
def myOrderInfo():
	resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
	member_info = g.member_info
	req = request.values
	order_sn = req['order_sn'] if 'order_sn' in req else ''
	pay_order_info = PayOrder.query.filter_by( member_id=member_info.id ,order_sn = order_sn).first()
	if not pay_order_info:
		resp['code'] = -1
		resp['msg'] = "系统繁忙,请稍后再试~~"
		return jsonify(resp)

	express_info = {}
	if pay_order_info.express_info:
		express_info = json.loads( pay_order_info.express_info )

	tmp_deadline = pay_order_info.created_time + datetime.timedelta(minutes=30)
	info = {
		"order_sn":pay_order_info.order_sn,
		"status":pay_order_info.pay_status,
		"status_desc":pay_order_info.status_desc,
		"pay_price":str( pay_order_info.pay_price),
		"yun_price":str( pay_order_info.yun_price),
		"total_price":str( pay_order_info.total_price),
		"address":express_info,
		"goods": [],
		"deadline":tmp_deadline.strftime("%Y-%m-%d %H:%M")
	}

	pay_order_items = PayOrderItem.query.filter_by( pay_order_id = pay_order_info.id  ).all()
	if pay_order_items:
		food_ids = selectFilterObj( pay_order_items , "food_id")
		food_map = getDictFilterField(Food, Food.id, "id", food_ids)
		for item in pay_order_items:
			tmp_food_info = food_map[item.food_id]
			tmp_data = {
				"name": tmp_food_info.name,
				"price": str( item.price ),
				"unit": item.quantity,
				"pic_url": UrlManager.buildImageUrl( tmp_food_info.main_image ),
			}
			info['goods'].append( tmp_data )
	resp['data']['info'] = info
	return jsonify(resp)


@route_api.route("/my/comment/add",methods = [ "POST" ])
def myCommentAdd():
	resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
	member_info = g.member_info
	req = request.values
	order_sn = req['order_sn'] if 'order_sn' in req else ''
	score = req['score'] if 'score' in req else 10
	content = req['content'] if 'content' in req else ''

	pay_order_info = PayOrder.query.filter_by( member_id=member_info.id ,order_sn = order_sn).first()
	if not pay_order_info:
		resp['code'] = -1
		resp['msg'] = "系统繁忙,请稍后再试~~"
		return jsonify(resp)

	if pay_order_info.comment_status:
		resp['code'] = -1
		resp['msg'] = "已经评价过了~~"
		return jsonify(resp)

	pay_order_items = PayOrderItem.query.filter_by( pay_order_id = pay_order_info.id ).all()
	food_ids = selectFilterObj( pay_order_items,"food_id" )
	tmp_food_ids_str = '_'.join(str(s) for s in food_ids if s not in [None])
	model_comment = MemberComments()
	model_comment.food_ids = "_%s_"%tmp_food_ids_str
	model_comment.member_id = member_info.id
	model_comment.pay_order_id = pay_order_info.id
	model_comment.score = score
	model_comment.content = content
	db.session.add( model_comment )

	pay_order_info.comment_status = 1
	pay_order_info.updated_time = getCurrentDate()
	db.session.add( pay_order_info )

	db.session.commit()
	return jsonify(resp)

@route_api.route("/my/comment/list" )
def myCommentList():
	resp = {'code': 200, 'msg': '操作成功~', 'data': {}}
	member_info = g.member_info
	comment_list = MemberComments.query.filter_by( member_id=member_info.id )\
		.order_by(MemberComments.id.desc()).all()
	data_comment_list = []
	if comment_list:
		pay_order_ids = selectFilterObj( comment_list,"pay_order_id" )
		pay_order_map = getDictFilterField( PayOrder,PayOrder.id,"id",pay_order_ids )
		for item in comment_list:
			tmp_pay_order_info = pay_order_map[ item.pay_order_id ]
			tmp_data = {
				"date":item.created_time.strftime("%Y-%m-%d %H:%M:%S"),
				"content":item.content,
				"order_number":tmp_pay_order_info.order_number
			}
			data_comment_list.append( tmp_data )
	resp['data']['list'] = data_comment_list
	return jsonify(resp)



数据库common/models/pay/PayOrderItem.py

DROP TABLE IF EXISTS `pay_order_item`;

CREATE TABLE `pay_order_item` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `pay_order_id` int(11) NOT NULL DEFAULT '0' COMMENT '订单id',
  `member_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '会员id',
  `quantity` int(11) NOT NULL DEFAULT '1' COMMENT '购买数量 默认1份',
  `price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '商品总价格,售价 * 数量',
  `food_id` int(11) NOT NULL DEFAULT '0' COMMENT '美食表id',
  `note` text NOT NULL COMMENT '备注信息',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:1:成功 0 失败',
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最近一次更新时间',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入时间',
  PRIMARY KEY (`id`),
  KEY `id_order_id` (`pay_order_id`),
  KEY `idx_food_id` (`food_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单详情表';
flask-sqlacodegen 'mysql://root:root@127.0.0.1/food_db' --tables pay_order_item --outfile "common/models/pay/PayOrderItem.py"  --flask

数据库PayOrder.py

# coding: utf-8
from application import db, app


class PayOrder(db.Model):
    __tablename__ = 'pay_order'
    __table_args__ = (
        db.Index('idx_member_id_status', 'member_id', 'status'),
    )

    id = db.Column(db.Integer, primary_key=True)
    order_sn = db.Column(db.String(40), nullable=False, unique=True, server_default=db.FetchedValue(), info='随机订单号')
    member_id = db.Column(db.BigInteger, nullable=False, server_default=db.FetchedValue(), info='会员id')
    total_price = db.Column(db.Numeric(10, 2), nullable=False, server_default=db.FetchedValue(), info='订单应付金额')
    yun_price = db.Column(db.Numeric(10, 2), nullable=False, server_default=db.FetchedValue(), info='运费金额')
    pay_price = db.Column(db.Numeric(10, 2), nullable=False, server_default=db.FetchedValue(), info='订单实付金额')
    pay_sn = db.Column(db.String(128), nullable=False, server_default=db.FetchedValue(), info='第三方流水号')
    prepay_id = db.Column(db.String(128), nullable=False, server_default=db.FetchedValue(), info='第三方预付id')
    note = db.Column(db.Text, nullable=False, info='备注信息')
    status = db.Column(db.Integer, nullable=False, server_default=db.FetchedValue(), info='1:支付完成 0 无效 -1 申请退款 -2 退款中 -9 退款成功  -8 待支付  -7 完成支付待确认')
    express_status = db.Column(db.Integer, nullable=False, server_default=db.FetchedValue(), info='快递状态,-8 待支付 -7 已付款待发货 1:确认收货 0:失败')
    express_address_id = db.Column(db.Integer, nullable=False, server_default=db.FetchedValue(), info='快递地址id')
    express_info = db.Column(db.String(1000), nullable=False, server_default=db.FetchedValue(), info='快递信息')
    comment_status = db.Column(db.Integer, nullable=False, server_default=db.FetchedValue(), info='评论状态')
    pay_time = db.Column(db.DateTime, nullable=False, server_default=db.FetchedValue(), info='付款到账时间')
    updated_time = db.Column(db.DateTime, nullable=False, server_default=db.FetchedValue(), info='最近一次更新时间')
    created_time = db.Column(db.DateTime, nullable=False, server_default=db.FetchedValue(), info='插入时间')




    @property
    def pay_status(self):
        tmp_status = self.status
        if self.status == 1:
            tmp_status = self.express_status
            if self.express_status == 1 and self.comment_status == 0:
                tmp_status = -5
            if self.express_status == 1 and self.comment_status == 1:
                tmp_status = 1
        return tmp_status

    @property
    def status_desc(self):
        return app.config['PAY_STATUS_DISPLAY_MAPPING'][ str( self.pay_status )]

    @property
    def order_number(self):
        order_number = self.created_time.strftime("%Y%m%d%H%M%S")
        order_number = order_number + str(self.id).zfill(5)
        return order_number

根据提供的引用内容,以下是关于订单类的一些属性和方法的介绍:

Python装饰器:优雅而强大的函数修饰工具 | w3cschool笔记

什么是装饰器?

装饰器是一种特殊的函数,它接受一个函数作为参数,并返回一个新的函数。这个新函数通常包装或修改了原始函数的行为,但仍保留了原始函数的名称和调用方式。装饰器可以在不修改函数源代码的情况下,为函数添加额外的功能、行为或修饰。

装饰器的语法

在Python中,装饰器使用​@​符号放置在函数定义之前,作为函数的修饰符。

python 装饰器装饰类_mob649e815e9bc9的技术博客_51CTO博客

这三个属性分别是pay_status、status_desc和order_number。它们被定义为@property装饰器,用于将方法转换为属性,使其可以像访问属性一样访问。

  1. pay_status属性:

    • 这个属性根据不同的条件返回不同的值。
    • 首先,它将self.status赋值给tmp_status变量。
    • 如果self.status等于1,则将self.express_status赋值给tmp_status。
    • 如果self.express_status等于1且self.comment_status等于0,则将-5赋值给tmp_status。
    • 如果self.express_status等于1且self.comment_status等于1,则将1赋值给tmp_status。
    • 最后,返回tmp_status的值作为pay_status属性的值。
  2. status_desc属性:

    • 这个属性返回一个与self.pay_status对应的描述。
    • 首先,将self.pay_status转换为字符串,并作为键在app.config[‘PAY_STATUS_DISPLAY_MAPPING’]字典中查找对应的值。
    • 返回找到的值作为status_desc属性的值。
  3. order_number属性:

    • 这个属性返回一个订单号。
    • 首先,将self.created_time按照"%Y%m%d%H%M%S"的格式转换为字符串,并赋值给order_number变量。
    • 然后,将self.id转换为字符串,并使用zfill(5)方法在左侧填充0,使其总长度为5,并将其追加到order_number后面。
    • 最后,返回order_number作为order_number属性的值。

这三个属性的作用是为了方便获取和使用相关的数据。pay_status属性根据不同的条件返回不同的支付状态,status_desc属性返回与支付状态对应的描述,order_number属性返回一个唯一的订单号。

  1. pay_status属性:该属性根据订单的状态返回一个支付状态码。如果订单状态为1,则返回快递状态码;如果快递状态为1且评论状态为0,则返回-5;如果快递状态为1且评论状态为1,则返回1。

  2. status_desc属性:该属性返回订单的支付状态描述。根据支付状态码在配置文件中查找对应的描述信息。

  3. order_number属性:该属性返回订单的订单号。订单号由订单创建时间和订单ID组成,格式为"年月日时分秒+订单ID(补零至5位)"。

该装饰器们 在My.py    59行 60 行 被使用

问题1: zfill()

zfill()是Python中的一个字符串方法,用于在字符串的左侧填充指定数量的零。它的语法如下:

string.zfill(width)

其中,string是要进行填充的字符串,width是填充后字符串的总长度。

以下是一个示例:

s = "123"
s = s.zfill(5)
print(s)  # 输出:00123

在这个例子中,原始字符串是"123",使用zfill()方法将字符串填充为总长度为5的字符串,左侧用零进行填充。

zfill()方法还可以接受一个可选的参数,用于指定填充字符。例如:

s = "123"
s = s.zfill(5, "*")
print(s)  # 输出:**123

在这个例子中,使用zfill()方法将字符串填充为总长度为5的字符串,左侧用"*"字符进行填充。

zfill()方法还可以用于给负数补零。例如:

n = "-123"
s = n.zfill(5)
print(s)  # 输出:-0123

在这个例子中,原始字符串是"-123",使用zfill()方法将字符串填充为总长度为5的字符串,左侧用零进行填充。

strftime()

strftime()函数是一个用于格式化时间的函数。它可以将时间按照指定的格式转换为字符串。下面是一个演示例子:

import datetime

# 获取当前时间
now = datetime.datetime.now()

# 格式化时间
formatted_time = now.strftime("%Y-%m-%d %H:%M:%S")

# 输出格式化后的时间
print(formatted_time)

输出结果为当前时间的格式化字符串,例如:“2021-01-01 12:00:00”。

支付下单和拉起支付功能

wx.requestPayment(Object object)

wx.requestPayment(Object object) | 微信开放文档

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3

统一下单   实现方法:

前往mina/pages/my/order_list.wxml给 马上付款  按钮添加事件bindtap="toPay",这里绑定的是order_sn随机订单号,防止别人在前端发现我们内部的number信息。

通过order_list.js中的toPay函数发送相关请求数据给Order.py中的@route_api.route("/order/pay", methods=[ "POST"]),获得相关数据后即可对应相关订单信息生成响应金额微信支付二维码,扫描后即可支付。

签名

第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。 注意:密钥的长度为32个字节。

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3

微信小程序支付中 签名 是干什么的?有什么作用?

微信小程序支付中的签名是用于验证  支付请求  的合法性和完整性的一种安全机制。它的作用是确保支付请求的数据在传输过程中没有被篡改,并且确保请求是由合法的发送方发起的。

签名是通过对 支付请求 中的参数进行 加密 生成的,包括了商户号、订单号、支付金额等信息。在支付请求发送到微信服务器之前,商户需要使用自己的私钥对这些参数进行加密生成签名,并将签名一同发送到微信服务器。微信服务器在接收到支付请求后,会使用商户在微信支付平台上设置的公钥对签名进行解密,并与请求中的参数进行比对,以验证请求的合法性和完整性。

通过签名机制,可以防止支付请求被篡改,确保支付过程的安全性。同时,签名也可以防止恶意攻击者伪造支付请求,保证支付请求的真实性。

数据库common/models/pay/OauthAccessToken.py

DROP TABLE IF EXISTS `oauth_access_token`;

CREATE TABLE `oauth_access_token` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `access_token` varchar(600) NOT NULL DEFAULT '',
  `expired_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '过期时间',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入时间',
  PRIMARY KEY (`id`),
  KEY `idx_expired_time` (`expired_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信的access_token 用户调用其他接口的';

flask-sqlacodegen 'mysql://root:root@127.0.0.1/food_db' --tables oauth_access_token --outfile "common/models/pay/OauthAccessToken.py"  --flask

common/libs/pay/WeChatService.py

# -*- coding: utf-8 -*-
import hashlib,requests,uuid,json,datetime
import xml.etree.ElementTree as ET
from application import app,db
from common.models.pay.OauthAccessToken import OauthAccessToken
from common.libs.Helper import getCurrentDate
class WeChatService():

    def __init__(self,merchant_key = None):
        self.merchant_key = merchant_key

    def create_sign(self, pay_data):
        '''
		生成签名
		:return:
		'''
        stringA = '&'.join(["{0}={1}".format(k, pay_data.get(k)) for k in sorted(pay_data)])
        stringSignTemp = '{0}&key={1}'.format(stringA, self.merchant_key)
        sign = hashlib.md5( stringSignTemp.encode("utf-8") ).hexdigest()
        return sign.upper()

    def get_pay_info(self,pay_data = None):
        '''
		获取支付信息
		:param xml_data:
		:return:
		'''

        sign = self.create_sign( pay_data )
        pay_data ['sign'] = sign
        xml_data = self.dict_to_xml( pay_data )
        headers = {'Content-Type': 'application/xml'}
        url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
        r =  requests.post( url= url, data=xml_data.encode('utf-8'),headers = headers)
        r.encoding = "utf-8"
        app.logger.info( r.text )
        if r.status_code == 200:
            prepay_id = self.xml_to_dict( r.text ).get('prepay_id')
            pay_sign_data = {
                'appId': pay_data.get('appid'),
                'timeStamp': pay_data.get('out_trade_no'),
                'nonceStr': pay_data.get('nonce_str'),
                'package': 'prepay_id={0}'.format(prepay_id),
                'signType': 'MD5'
            }
            pay_sign = self.create_sign( pay_sign_data )
            pay_sign_data.pop('appId')
            pay_sign_data['paySign'] = pay_sign
            pay_sign_data['prepay_id'] = prepay_id
            return pay_sign_data

        return False


    def dict_to_xml(self,dict_data):
        '''
        dict to xml
        :param dict_data:
        :return:
        '''
        xml = ["<xml>"]
        for k, v in dict_data.items():
            xml.append("<{0}>{1}</{0}>".format(k, v))
        xml.append("</xml>")
        return "".join(xml)

    def xml_to_dict(self,xml_data):
        '''
        xml to dict
        :param xml_data:
        :return:
        '''
        xml_dict = {}
        root = ET.fromstring(xml_data)
        for child in root:
            xml_dict[child.tag] = child.text
        return xml_dict

    def get_nonce_str(self):
        '''
        获取随机字符串
        :return:
        '''
        return str(uuid.uuid4()).replace('-', '')

    def getAccessToken(self):
        token = None
        token_info = OauthAccessToken.query.filter( OauthAccessToken.expired_time >= getCurrentDate() ).first()
        if token_info:
            token = token_info.access_token
            return token

        config_mina = app.config['MINA_APP']
        url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}"\
            .format( config_mina['appid'],config_mina['appkey'] )

        r = requests.get( url = url )
        if r.status_code != 200 or not r.text:
            return token

        data = json.loads( r.text )
        now = datetime.datetime.now()
        date = now + datetime.timedelta(seconds=data['expires_in'] - 200)
        model_token = OauthAccessToken()
        model_token.access_token = data['access_token']
        model_token.expired_time = date.strftime( "%Y-%m-%d %H:%M:%S" )
        model_token.created_time = getCurrentDate()
        db.session.add( model_token )
        db.session.commit()

        return data['access_token']


create_sign()

stringA = '&'.join(["{0}={1}".format(k, pay_data.get(k)) for k in sorted(pay_data)])

stringA = '&'.join(["{0}={1}".format(k, pay_data.get(k)) for k in sorted(pay_data)])是一个将字典pay_data中的键值对按照键的字母顺序排序,并以key=value的形式拼接成字符串的操作。

下面是一个示例:

pay_data = {'name': 'John', 'age': 25, 'gender': 'male'}
stringA = '&'.join(["{0}={1}".format(k, pay_data.get(k)) for k in sorted(pay_data)])
print(stringA)  # 输出:age=25&gender=male&name=John

这个操作首先使用sorted()函数对字典的键进行排序,然后使用列表推导式将排序后的键值对按照key=value的格式拼接成字符串,并使用'&'.join()方法将所有的键值对用&连接起来。
 

stringSignTemp = '{0}&key={1}'.format(stringA, self.merchant_key)

这段代码是使用Python中的字符串格式化方法format()来生成一个字符串。stringSignTemp是一个格式化字符串,它使用了两个占位符{0}{1}stringAself.merchant_key是两个变量,它们将分别替换掉占位符中的{0}{1}

具体来说,stringSignTemp的值将是stringAself.merchant_key按照顺序替换占位符后的结果。这个字符串的生成目的可能是用于支付宝支付接口中的签名计算,其中stringA是待签名的字符串,self.merchant_key是用于签名的密钥。

范例:

stringA = 'some_string'
self.merchant_key = 'secret_key'
stringSignTemp = '{0}&key={1}'.format(stringA, self.merchant_key)
print(stringSignTemp)  # 输出:'some_string&key=secret_key'

.text

.text是一个在Python中常用的字符串方法,它用于返回字符串对象的文本内容。下面是一个示例:

text = "Hello, world!"
print(text.text)  # 输出:Hello, world!

在这个示例中,我们定义了一个字符串对象text,并使用.text方法来获取它的文本内容。最后,我们使用print函数将文本内容打印出来。

xml_to_dict(self, sml_data)

这个函数使用xml.etree.ElementTree模块来解析XML数据。它首先将XML数据传递给ET.fromstring()函数,然后遍历根元素的所有子元素,并将每个子元素的标签作为键,文本内容作为值存储在字典中。最后,返回这个字典。

XML数据是什么

XML数据是一种用于保存和传输数据的标记语言。它使用自定义的标签来描述数据的结构和内容,以便在不同的平台和系统之间进行数据交换和共享。XML数据具有良好的可读性和可扩展性,可以适应不同的应用需求。通过使用XML,开发人员可以更好地控制数据的存储和传输,实现数据的结构化管理和有效的数据交互。

XML和python 数据的不同之处

XML和Python数据的不同之处在于它们的结构和用途不同。

XML是一种标记语言,用于传输和存储数据。它使用标签来定义数据的结构和层次关系。XML数据可以包含文本、属性和子元素。XML适用于跨平台和跨语言的数据交换,常见的应用场景包括配置文件、数据传输和Web服务。

Python数据是指在Python编程语言中使用的数据类型和结构。Python提供了多种内置数据类型,包括整数、浮点数、字符串、列表、元组、字典等。Python数据可以用于存储和处理各种类型的数据,包括数字、文本、集合和对象。

下面是一个案例,展示了XML数据和Python数据的不同之处:

XML数据:

<book>
  <title>Python Programming</title>
  <author>John Smith</author>
  <year>2021</year>
</book>

Python数据:

book = {
  "title": "Python Programming",
  "author": "John Smith",
  "year": 2021
}

在XML数据中,数据以标签的形式进行组织,每个标签表示一个数据项。而在Python数据中,数据以键值对的形式进行组织,每个键值对表示一个数据项。

支付回调功能

通过支付回调的参数找到对应的payorder信息,然后让订单置为已支付。

找到支付成功后存入数据库的信息进行分析,拿到该条数据进行代码处理就可以了。

数据库common/models/pay/PayOrderCallbackData.py


DROP TABLE IF EXISTS `pay_order_callback_data`;

CREATE TABLE `pay_order_callback_data` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pay_order_id` int(11) NOT NULL DEFAULT '0' COMMENT '支付订单id',
  `pay_data` text NOT NULL COMMENT '支付回调信息',
  `refund_data` text NOT NULL COMMENT '退款回调信息',
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后一次更新时间',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `pay_order_id` (`pay_order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
flask-sqlacodegen 'mysql://root:root@127.0.0.1/food_db' --tables pay_order_callback_data --outfile "common/models/pay/PayOrderCallbackData.py"  --flask

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xinzheng新政

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值