async.forEachOf 中的多个保存操作

Multiple save operations in async.forEachOf

我在 mongo 集合中循环处理一些付款。所有 payoutdate == today() 的付款都必须导出并写入 sepa 文件,以便我们可以通过银行处理付款。

付款在创建时没有发票编号,我们会在处理付款时生成一个发票编号(通过上述函数导出)。 问题是,当我们 运行 导出多笔付款的函数时,所有付款都获得相同的发票编号。所以看起来,在下一次付款处理之前,最后一次保存操作没有完成。

如何实现每次付款都在增加?

这是循环函数:

const fs = require('fs');
const async = require('async');
const DateDiff = require('date-diff');
const SEPA = require('sepa');
const shopService = require(path.join(__dirname, '..', 'services', 'shop.service'));

    async.forEachOf(payments, function(payment, key, paymentDone){            
        var diff = new DateDiff(new Date(payment.payoutDate), new Date());
        if(payment.payoutDate && payment.amount > 0 && payment.completed == false && payment.exported == false && diff.days() <= 0){
            //payment has amount, is not completed and is not exported, create an SEPA transfer, and set the payment to completed
            //but first create an invoicenumber

                orderService.updateOrderPayment(payment.orderId, {generateInvoiceNumber: true}, function(err, result){
                    if(err){
                        console.log("error updating payment", err);
                    }
                    //reget the payment to avoid duplicated invoice numbers

                    orderService.getPayment(result.orderId, function(err, payment){
                        if(err){
                            console.log("error getting payment", err);
                        }

                        Shop.findOne({_id: payment.shopId}).exec(function(err, shop){
                            if(shop && shop.bankAccountNumber && shop.accountHolder && shop.bicCode){
                               //create transaction and add this to the file
                            }else{
                                var result = {
                                    paymentID: payment._id,
                                    orderId: payment.orderId,
                                    status: payment.status,
                                    message: "shop does not have an iban, accountholder or biccode",
                                    shop: shop.nameSlug
                                }
                                resultArray.push(result);
                                console.log("shop does not have an iban, accountholder or biccode", shop.nameSlug);
                                paymentDone();
                            }

                            orderService.updateOrderPayment(payment.orderId, {status: 'completed'}, function(err, result){
                                orderService.updateOrderStatusById(payment.orderId, {status: 'Granted', date: new Date(), comment: null});
                                var result = {
                                    paymentID: payment._id,
                                    orderId: payment.orderId,
                                    status: payment.status,
                                    message: "payment exported",
                                }
                                resultArray.push(result);
                                counter++;
                                paymentDone();
                            })
                        })
                    })    
                })    
        }else{
            var result = {
                paymentID: payment._id,
                orderId: payment.orderId,
                status: payment.status,
                message: "order already processed"
            }
            resultArray.push(result);
            paymentDone();
        }
    }, function(){
        if(resultArray.length == payments.length){
            //console.log("Result", resultArray);
            if(counter == 0){
                res.status(200).json({"message":"No orders to export", resultArray});
            }else{
                res.set({"Content-Disposition":"attachment; filename=\"sepa.xml\""});
                res.send(doc.toString());
            }
        }
    })

订单服务包含以下功能(与本题相关)

function updateOrderPayment(orderId, paymentStatus, callback){
    console.log("updateOrderPayment");
    if(!paymentStatus){
        return callback("No payment details provided");
    }else{
        if(!paymentStatus.comment){
            paymentStatus.comment = null;
        }
    }
    getPayment(orderId, function(err, payment){
        if(err)
            return callback(err);

        handlePayment(payment, paymentStatus, function(result){
            result.save(function(err, result){
                if(err){
                    return callback(err);
                }
                console.log("payment saved");
                return callback(null, result);
            })
        })
    })
}

function handlePayment(payment, paymentStatus, callback){
    if(paymentStatus.status){
        var status = {
            status: paymentStatus.status,
            comment: paymentStatus.comment,
            date: Date.now()
        }
        payment.status.push(status);
    }

    if(paymentStatus.generateInvoiceNumber){
        console.log("generateInvoiceNumber");
        var invoiceNumber =0;

        Payment.findOne({invoiceNumber: {$exists:true}}).sort({_id: -1}).exec(function(err, latestPaymentsWithNumber){

            if(latestPaymentsWithNumber && latestPaymentsWithNumber.invoiceNumber){
                invoiceNumber = latestPaymentsWithNumber.invoiceNumber.split("-")[1];
            }

            var date = new Date();
            payment.invoiceNumber = date.getFullYear().toString() + date.getMonth().toString() + "-" + (parseInt(invoiceNumber)+1);
            console.log("number", payment.invoiceNumber);
            return callback(payment);
        })
    }

    if(paymentStatus.status == 'returned' || paymentStatus.status == 'cancelled'){
        payment.cancelled = true;
        payment.amount = 0;
        payment.payoutDate = null;
        return callback(payment);
    }

    if(paymentStatus.status == 'completed'){
        payment.completed = true;
        payment.exported = true;
        payment.payoutDate = null;  
        return callback(payment);
    }
}

function getPayment(orderId, callback){
    Payment.findOne({orderId: orderId}).exec(function(err, payment){
        if(err){
            return callback(err);
        }
        return callback(null, payment);
    })
}

你有2个选择:

1) 在范围内实现对保存操作的回调

x.forEach(function(_x) {
  _x.save(function(err) { });
});

2) 将您的函数分解为异步单元或使用异步库

function async(x, cb) {
  x.operations(cb)
}

function series(x) {
  if (x) {
    async(x, function() { series(xs.pop()); });
  } else // finished
}

series(xs.pop()); // xs is the array you're iterating

感谢两位的回复!组合是解决方案。

我已将查找最后一个 invoiceNumber 的查询更改为

Payment.find({invoiceNumber: {$ne:null}}).sort({date: -1}).limit(1).exec(function(err, latestPaymentsWithNumber){

我现在使用 async.eachSeries 迭代 paymentsasync.eachSeries(payments, function(payment, paymentDone){

然后我在第一个回调中执行 result.save 以假设我有正确的数据

result.save(function(err, payment){