// 引入lodash库，用于处理各种数据结构，提供了很多便捷的工具函数，例如对象克隆、数组求和、遍历等操作，在后续多个函数中会用到。
var _ = require('lodash');
// 引入Node.js的path模块，用于处理文件路径相关操作，此处用于准确引入自定义的 `dao/DAO` 模块所在的路径。
var path = require("path");
// 引入 `orm` 库，可能用于对象关系映射相关操作，从代码中看，像是用于构建数据库查询条件等功能（例如使用 `orm.like` 方法），方便与数据库进行交互。
var orm = require("orm");
// 引入自定义的 `DAO` 模块，该模块应该封装了与数据库操作相关的通用方法，例如创建、查询、更新等操作，对应不同的数据模型（如 `OrderModel`、`OrderGoodModel` 等）进行数据库层面的操作。
var dao = require(path.join(process.cwd(), "dao/DAO"));

// 引入 `bluebird` 库，用于处理异步操作的 `Promise`，使得异步代码可以用更清晰的链式调用方式书写，提高代码的可读性和可维护性，在多个函数中用于包装异步操作并返回 `Promise` 对象。
var Promise = require("bluebird");
// 引入 `uniqid` 库，用于生成唯一的标识符，在创建订单相关逻辑中可能用于生成订单编号等唯一标识信息。
var uniqid = require('uniqid');

// 定义 `doCheckOrderParams` 函数，用于对创建或更新订单时传入的参数进行合法性检查和预处理，将处理后的参数信息以 `Promise` 的形式返回。
function doCheckOrderParams(params) {
    // 返回一个新的 `Promise` 对象，在其内部进行参数验证和信息整理逻辑，通过 `resolve` 和 `reject` 来控制 `Promise` 的状态（成功或失败）。
    return new Promise(function (resolve, reject) {
        // 创建一个空对象 `info`，用于存储经过整理和验证后的订单相关信息，后续会根据传入的参数情况，将符合要求的信息添加到这个对象中。
        var info = {};

        // 如果传入的参数中包含 `order_id`，则将其直接添加到 `info` 对象中，`order_id` 可能用于表示已存在的订单的唯一标识，后续操作可能会基于此判断是更新还是创建新订单等情况。
        if (params.order_id) info.order_id = params.order_id;

        // 如果传入的参数中不包含 `order_id`，则进行以下额外的参数验证和信息补充操作，意味着可能是要创建一个新订单，所以需要更多必要参数的验证。
        if (!params.order_id) {
            // 首先验证用户ID（`user_id`）是否存在，如果不存在则直接通过 `reject` 拒绝这个 `Promise`，并返回错误信息，表示用户ID不能为空，因为创建订单通常需要关联用户信息，用户ID是关键标识之一。
            if (!params.user_id) return reject("用户ID不能为空");
            // 进一步验证用户ID是否可以转换为数字类型，如果不能转换成功（即不是有效的数字），则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示用户ID必须是数字，以确保能准确在数据库中关联到对应的用户记录等操作。
            if (isNaN(parseInt(params.user_id))) return reject("用户ID必须是数字");
            // 如果用户ID验证通过，则将其添加到 `info` 对象中，作为新订单关联的用户标识信息。
            info.user_id = params.user_id;
        }

        // 如果没有传入 `order_id`，意味着创建新订单，此时生成一个唯一的订单编号（格式为 `"itcast-"` 加上通过 `uniqid` 库生成的唯一标识符），并添加到 `info` 对象中作为订单的编号信息。
        if (!params.order_id) info.order_number = "itcast-" + uniqid();

        // 验证订单价格（`order_price`）是否存在，如果不存在则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示订单价格不能为空，因为订单价格是订单的重要属性之一，不可或缺。
        if (!params.order_price) return reject("订单价格不能为空");
        // 进一步验证订单价格是否可以转换为数字类型（这里使用 `parseFloat` 是因为价格可能包含小数部分），如果不能转换成功则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示订单价格必须为数字，以保证数据的合法性和符合数据库存储要求。
        if (isNaN(parseFloat(params.order_price))) return reject("订单价格必须为数字");
        // 如果订单价格验证通过，则将其添加到 `info` 对象中，作为订单的价格信息。
        info.order_price = params.order_price;

        // 如果传入了 `order_pay` 参数，则将其值直接添加到 `info` 对象中，用于表示订单的支付情况等相关信息；如果没有传入，则默认设置为 `'0'`，表示未支付或初始支付状态，具体含义由业务逻辑决定。
        if (params.order_pay) {
            info.order_pay = params.order_pay;
        } else {
            info.order_pay = '0';
        }

        // 根据传入的 `is_send` 参数来设置订单的发货状态信息，如果 `is_send` 参数为 `1`，则将发货状态设置为 `'是'`；如果为其他值（包括未传入该参数的情况），则默认设置为 `'否'`，同样具体的状态表示和业务含义由业务逻辑决定。
        if (params.is_send) {
            if (params.is_send == 1) {
                info.is_send = '是';
            } else {
                info.is_send = '否';
            }
        } else {
            info.is_send = '否';
        }

        // 根据传入的 `trade_no` 参数来设置交易编号相关信息，如果传入了该参数则设置为 `'否'`（这里设置的值看起来有点奇怪，可能需要根据实际业务逻辑进一步确认是否正确），如果没有传入则设置为空字符串，可能后续会根据实际交易情况进行更新等操作。
        if (params.trade_no) {
            info.trade_no = '否';
        } else {
            info.trade_no = '';
        }

        // 验证发票抬头（`order_fapiao_title`）相关信息，如果传入了该参数，则进行进一步验证，判断其值是否为 `'个人'` 或 `'公司'`，如果不是则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示发票抬头必须是 `'个人'` 或 `'公司'`；
        // 如果验证通过或者没有传入该参数（则默认设置为 `'个人'`），则将其添加到 `info` 对象中，作为订单的发票抬头信息。
        if (params.order_fapiao_title) {
            if (params.order_fapiao_title!= '个人' && params.order_fapiao_title!= '公司')
                return reject("发票抬头必须是 个人 或 公司");
            info.order_fapiao_title = params.order_fapiao_title;

        } else {
            info.order_fapiao_title = "个人";
        }

        // 如果传入了 `order_fapiao_company` 参数，则将其值添加到 `info` 对象中，用于存储发票对应的公司信息（如果有）；如果没有传入则设置为空字符串，可能表示没有对应的公司信息或者留空待后续补充等情况。
        if (params.order_fapiao_company) {
            info.order_fapiao_company = params.order_fapiao_company;
        } else {
            info.order_fapiao_company = "";
        }

        // 如果传入了 `order_fapiao_content` 参数，则将其值添加到 `info` 对象中，用于存储发票内容相关信息；如果没有传入则设置为空字符串，可能表示没有特定的发票内容或者留空待填写等情况。
        if (params.order_fapiao_content) {
            info.order_fapiao_content = params.order_fapiao_content;
        } else {
            info.order_fapiao_content = "";
        }

        // 如果传入了 `consignee_addr` 参数，则将其值添加到 `info` 对象中，用于存储收件人地址信息；如果没有传入则设置为空字符串，可能表示没有填写收件人地址或者留空待补充等情况。
        if (params.consignee_addr) {
            info.consignee_addr = params.consignee_addr;
        } else {
            info.consignee_addr = "";
        }

        // 如果传入了 `goods` 参数（可能是订单商品相关信息，例如商品列表等内容），则将其直接添加到 `info` 对象中，后续在创建订单等操作中可能会进一步处理这些商品信息。
        if (params.goods) {
            info.goods = params.goods;
        }

        // 设置订单的支付状态初始值为 `'0'`，表示未支付或默认支付状态，具体含义由业务逻辑决定，后续可能会根据实际支付情况进行更新。
        info.pay_status = '0';

        // 如果传入了 `order_id`，意味着可能是更新订单操作，此时设置订单的创建时间为当前时间（通过获取当前日期时间并转换为时间戳，再除以 `1000`，可能是为了符合数据库存储的时间格式要求等情况），并添加到 `info` 对象中。
        if (params.order_id) info.create_time = (Date.parse(new Date()) / 1000);

        // 设置订单的更新时间为当前时间（同样进行时间戳转换操作），添加到 `info` 对象中，用于记录订单信息最后更新的时间点，方便后续查询、统计等业务操作使用。
        info.update_time = (Date.parse(new Date()) / 1000);

        // 如果所有参数验证和信息整理都顺利完成，则通过 `resolve` 方法将整理好的 `info` 对象传递出去，使得 `Promise` 状态变为已完成（成功），后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行后续操作。
        resolve(info);
    });
}

// 定义 `doCreateOrder` 函数，用于在数据库中创建新的订单记录，接收经过验证和整理后的订单信息（`info`），以 `Promise` 的形式返回创建订单操作的结果（成功则包含创建后的订单相关信息，失败则返回错误信息）。
function doCreateOrder(info) {
    // 返回一个新的 `Promise` 对象，在其内部执行创建订单的数据库操作，并通过 `resolve` 和 `reject` 控制 `Promise` 的状态。
    return new Promise(function (resolve, reject) {
        // 调用 `dao` 模块的 `create` 方法来执行创建订单的数据库操作，第一个参数 `"OrderModel"` 表示要操作的数据模型（对应数据库中的订单数据表），
        // 第二个参数使用 `_.clone(info)` 通过 `lodash` 库的克隆函数对传入的 `info` 对象进行克隆，避免在数据库操作过程中对原始数据造成意外修改，然后将克隆后的数据作为要插入数据库的订单信息，
        // 第三个参数是一个回调函数，用于处理数据库插入操作完成后的结果情况，根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。
        dao.create("OrderModel", _.clone(info), function (err, newOrder) {
            // 如果在创建订单的数据库操作过程中出现错误（`err` 不为 `null`），比如数据库插入语句执行失败、违反数据约束等原因，则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示创建订单失败，让调用者知晓操作未成功及原因。
            if (err) return reject("创建订单失败");
            // 如果订单创建成功，将创建后的订单对象（`newOrder`）添加到传入的 `info` 对象的 `order` 属性中（这样可以将订单相关的更多信息整合在一起方便后续操作），然后通过 `resolve` 方法将包含订单信息的 `info` 对象传递出去，
            // 使得 `Promise` 状态变为已完成（成功），后续可以通过 `.then` 方法获取并使用这个包含订单详细信息的 `info` 对象进行进一步操作，比如添加订单商品等操作。
            info.order = newOrder;
            resolve(info);
        });
    });
}

// 定义 `doCreateOrderGood` 函数，用于在数据库中创建订单商品记录，接收订单商品相关信息（`orderGood`），以 `Promise` 的形式返回创建订单商品操作的结果（成功则返回创建后的订单商品对象，失败则返回错误信息）。
function doCreateOrderGood(orderGood) {
    // 返回一个新的 `Promise` 对象，在其内部执行创建订单商品的数据库操作，并通过 `resolve` 和 `reject` 控制 `Promise` 的状态。
    return new Promise(function (resolve, reject) {
        // 调用 `dao` 模块的 `create` 方法来执行创建订单商品的数据库操作，第一个参数 `"OrderGoodModel"` 表示要操作的数据模型（对应数据库中的订单商品数据表），
        // 第二个参数直接传入 `orderGood` 对象，它包含了要插入数据库的订单商品相关信息，比如商品名称、价格等信息，第三个参数是一个回调函数，用于处理数据库插入操作完成后的结果情况，根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。
        dao.create("OrderGoodModel", orderGood, function (err, newOrderGood) {
            // 如果在创建订单商品的数据库操作过程中出现错误（`err` 不为 `null`），比如数据库插入语句执行失败、违反数据约束等原因，则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示创建订单商品失败，让调用者知晓操作未成功及原因。
            if (err) return reject("创建订单商品失败");
            // 如果订单商品创建成功，则通过 `resolve` 方法将创建后的订单商品对象（`newOrderGood`）传递出去，使得 `Promise` 状态变为已完成（成功），后续可以通过 `.then` 方法获取并使用这个对象进行进一步操作，比如关联到对应的订单等操作。
            resolve(newOrderGood);
        });
    });
}

// 定义 `doAddOrderGoods` 函数，用于将多个订单商品信息添加到对应的订单中，接收包含订单和商品信息的对象（`info`），以 `Promise` 的形式返回添加商品操作完成后的结果（成功则返回包含完整订单及商品信息的对象，失败则返回错误信息）。
function doAddOrderGoods(info) {
    // 返回一个新的 `Promise` 对象，在其内部执行添加订单商品到订单的相关逻辑，并通过 `resolve` 和 `reject` 控制 `Promise` 的状态。
    return new Promise(function (resolve, reject) {
        // 首先验证传入的 `info` 对象中是否包含已创建的订单对象（`order` 属性），如果不存在则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示订单对象未创建，因为后续操作需要基于已存在的订单来添加商品信息，所以订单对象必须存在。
        if (!info.order) return reject("订单对象未创建");

        // 获取传入的 `info` 对象中的商品信息（`goods` 属性），它可能是一个包含多个订单商品对象的数组，每个对象包含商品的具体信息，比如商品价格、数量等，后续会遍历这个数组来逐个创建订单商品记录并关联到订单上。
        var orderGoods = info.goods;

        // 如果存在订单商品信息（即 `orderGoods` 不为空且长度大于 `0`），则进行以下添加订单商品的操作。
        if (orderGoods && orderGoods.length > 0) {
            // 创建一个空数组 `fns`，用于存储多个创建订单商品的异步操作函数（每个函数返回一个 `Promise`），后续会使用 `Promise.all` 来并行执行这些异步操作，提高效率。
            var fns = [];
            // 使用 `lodash` 的 `sum` 函数和 `map` 函数计算所有订单商品的总价格，`_.map` 函数先从每个订单商品对象中提取 `goods_price` 字段的值形成一个新的价格数组，然后 `_.sum` 函数对这个价格数组进行求和操作，得到商品总价格，方便后续记录和使用。
            var goods_total_price = _.sum(_.map(orderGoods, "goods_price"));

            // 使用 `lodash` 的 `forEach` 函数遍历订单商品数组 `orderGoods`，对于每个订单商品对象（`orderGood`）进行以下操作，将商品关联到对应的订单上并准备创建商品记录。
            _(orderGoods).forEach(function (orderGood) {
                // 将当前订单商品对象的 `order_id` 属性设置为对应的订单的 `order_id`（从 `info.order.order_id` 获取），这样建立起订单商品与订单的关联关系，表明该商品属于哪个订单。
                orderGood.order_id = info.order.order_id;
                // 将当前订单商品对象的 `goods_total_price` 属性设置为前面计算好的商品总价格，可能用于记录该订单下所有商品的总价等相关业务用途，具体含义由业务逻辑决定。
                orderGood.goods_total_price = goods_total_price;
                // 将创建当前订单商品记录的异步操作函数（通过调用 `doCreateOrderGood` 函数返回的 `Promise`）添加到 `fns` 数组中，准备后续并行执行这些创建操作。
                fns.push(doCreateOrderGood(orderGood));
            });

            // 使用 `Promise.all` 函数并行执行 `fns` 数组中所有的创建订单商品的异步操作
// 以下是 `doAddOrderGoods` 函数剩余部分的注释

// `Promise.all` 会并行执行 `fns` 数组中的所有异步操作（即创建各个订单商品记录的 `Promise`），当所有异步操作都成功完成后，
// 会执行 `.then` 回调函数中的逻辑，将创建订单商品操作返回的结果数组（`results`，每个元素对应一个创建成功的订单商品对象）
// 设置为 `info.order` 对象的 `goods` 属性值，这样就将创建好的订单商品与对应的订单关联起来了，然后通过 `resolve` 方法将包含完整订单及商品信息的 `info` 对象传递出去，
// 使得 `Promise` 状态变为已完成（成功），表示添加订单商品到订单的操作成功完成，后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行进一步操作。
Promise.all(fns)
.then(function (results) {
    info.order.goods = results;
    resolve(info);
})
// 如果在并行执行创建订单商品的异步操作过程中，有任何一个操作出现错误（例如某个商品记录创建失败），则会进入 `.catch` 回调函数，
// 这里直接将错误信息通过 `reject` 拒绝 `Promise`，并将错误信息返回给调用者，告知调用者添加订单商品操作出现问题及具体的错误原因。
.catch(function (error) {
    if (error) return reject(error);
});

// 如果没有订单商品信息（即 `orderGoods` 为空或长度为0），说明不需要添加订单商品，直接通过 `resolve` 方法将传入的 `info` 对象传递出去，
// 表示添加订单商品操作顺利完成（因为本身就没有商品需要添加），`Promise` 状态变为已完成（成功），后续可以继续进行其他相关操作。
} else {
    resolve(info);
}
});
}

// 定义 `doGetAllOrderGoods` 函数，用于获取指定订单的所有商品信息，接收包含订单相关信息的对象（`info`），以 `Promise` 的形式返回获取操作的结果（成功则返回包含订单及商品列表信息的对象，失败则返回错误信息）。
function doGetAllOrderGoods(info) {
    return new Promise(function (resolve, reject) {
        // 首先验证传入的 `info` 对象中是否包含已创建的订单对象（`order` 属性），如果不存在则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示订单对象未创建，
        // 因为获取订单商品信息需要基于已存在的订单来查询对应的商品记录，所以订单对象必须存在才能进行后续操作。
        if (!info.order) return reject("订单对象未创建");

        // 调用 `dao` 模块的 `list` 方法来获取指定订单的商品信息，第一个参数 `"OrderGoodModel"` 表示要操作的数据模型（对应数据库中的订单商品数据表），
        // 第二个参数是一个对象，用于指定查询条件，这里通过 `{"columns":{"order_id":info.order.order_id}}` 设置只查询 `order_id` 与传入的订单对象的 `order_id` 匹配的商品记录，
        // 即获取指定订单下的所有商品信息，第三个参数是一个回调函数，用于处理查询操作完成后的结果情况，根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。
        dao.list("OrderGoodModel", { "columns": { "order_id": info.order.order_id } }, function (err, orderGoods) {
            // 如果在获取订单商品列表的数据库操作过程中出现错误（`err` 不为 `null`），比如数据库查询语句执行失败、连接问题等原因，则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示获取订单商品列表失败，让调用者知晓操作未成功及原因。
            if (err) return reject("获取订单商品列表失败");

            // 如果查询成功，将获取到的订单商品列表（`orderGoods`，是一个包含多个订单商品对象的数组）设置为 `info.order` 对象的 `goods` 属性值，这样就将获取到的商品信息关联到对应的订单上了，
            // 然后通过 `resolve` 方法将包含完整订单及商品信息的 `info` 对象传递出去，使得 `Promise` 状态变为已完成（成功），后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行进一步操作，比如展示订单详情等操作。
            info.order.goods = orderGoods;
            resolve(info);
        })
    });
}

// 定义 `doGetOrder` 函数，用于获取指定订单的详细信息，接收包含订单ID相关信息的对象（`info`），以 `Promise` 的形式返回获取操作的结果（成功则返回包含订单详细信息的对象，失败则返回错误信息）。
function doGetOrder(info) {
    return new Promise(function (resolve, reject) {
        // 调用 `dao` 模块的 `show` 方法来获取指定订单的详细信息，第一个参数 `"OrderModel"` 表示要操作的数据模型（对应数据库中的订单数据表），
        // 第二个参数传入 `info.order_id`，作为查询条件，用于查找数据库中对应 `order_id` 的订单记录，获取其详细信息，第三个参数是一个回调函数，用于处理查询操作完成后的结果情况，根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。
        dao.show("OrderModel", info.order_id, function (err, newOrder) {
            // 如果在获取订单详情的数据库操作过程中出现错误（`err` 不为 `null`），比如数据库查询语句执行失败、连接问题等原因，则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示获取订单详情失败，让调用者知晓操作未成功及原因。
            if (err) return reject("获取订单详情失败");
            // 如果查询结果中没有获取到对应的订单（`newOrder` 为 `null`），则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示订单ID不存在，因为没有找到对应订单记录说明传入的订单ID可能有误或者该订单不存在，告知调用者操作出现问题及原因。
            if (!newOrder) return reject("订单ID不能存在");
            // 如果成功获取到订单详情信息，将获取到的订单对象（`newOrder`）设置为 `info.order` 属性值，这样就将获取到的详细订单信息整合到传入的 `info` 对象中了，
            // 然后通过 `resolve` 方法将包含订单详细信息的 `info` 对象传递出去，使得 `Promise` 状态变为已完成（成功），后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行进一步操作，比如添加商品、更新订单等操作。
            info.order = newOrder;
            resolve(info);
        })
    });
}

// 定义 `doUpdateOrder` 函数，用于更新指定订单的信息，接收包含订单相关信息的对象（`info`），以 `Promise` 的形式返回更新操作的结果（成功则返回包含更新后订单详细信息的对象，失败则返回错误信息）。
function doUpdateOrder(info) {
    return new Promise(function (resolve, reject) {
        // 调用 `dao` 模块的 `update` 方法来执行更新订单信息的数据库操作，第一个参数 `"OrderModel"` 表示要操作的数据模型（对应数据库中的订单数据表），
        // 第二个参数传入 `info.order_id`，用于准确找到数据库中要更新的订单记录，第三个参数使用 `_.clone(info)` 通过 `lodash` 库的克隆函数对传入的 `info` 对象进行克隆，避免在数据库操作过程中对原始数据造成意外修改，然后将克隆后的数据作为要更新的订单信息，
        // 第四个参数是一个回调函数，用于处理更新操作完成后的结果情况，根据操作是否成功返回相应的信息给 `Promise` 的 `resolve` 或 `reject`。
        dao.update("OrderModel", info.order_id, _.clone(info), function (err, newOrder) {
            // 如果在更新订单信息的数据库操作过程中出现错误（`err` 不为 `null`），比如数据库更新语句执行失败、违反数据约束等原因，则通过 `reject` 拒绝 `Promise`，并返回错误信息，表示更新失败，让调用者知晓操作未成功及原因。
            if (err) return reject("更新失败");
            // 如果订单信息更新成功，将更新后的订单对象（`newOrder`）设置为 `info.order` 属性值，这样就将更新后的订单详细信息整合到传入的 `info` 对象中了，
            // 然后通过 `resolve` 方法将包含更新后订单详细信息的 `info` 对象传递出去，使得 `Promise` 状态变为已完成（成功），后续可以通过 `.then` 方法获取并使用这个 `info` 对象进行进一步操作，比如获取更新后的订单商品信息等操作。
            info.order = newOrder;
            resolve(info);
        });

    });
}

// 对外暴露 `createOrder` 函数，用于创建新的订单，接收订单相关参数（`params`）和一个回调函数（`cb`），在内部通过一系列异步操作（参数验证、创建订单、添加订单商品等）来完成订单创建流程，
// 最后通过回调函数将创建成功后的订单信息返回给调用者，若在创建过程中出现错误则通过回调函数返回相应的错误信息。
module.exports.createOrder = function (params, cb) {
    // 首先调用 `doCheckOrderParams` 函数对传入的订单参数进行合法性检查和预处理，该函数返回一个 `Promise`，如果参数验证通过则会传递处理好的订单信息，否则会返回错误信息拒绝 `Promise`。
    doCheckOrderParams(params)
    // 使用 `.then` 方法链式调用 `doCreateOrder` 函数，当 `doCheckOrderParams` 成功完成
	// 链式调用 `doCreateOrder` 函数，在 `doCheckOrderParams` 函数对订单参数验证和预处理成功后（即 `Promise` 被成功 `resolve`），
// 会将处理好的订单信息作为参数传递给 `doCreateOrder` 函数，用于执行创建订单的数据库操作。
.then(doCreateOrder)
// 继续链式调用 `doAddOrderGoods` 函数，当 `doCreateOrder` 函数成功创建订单（其返回的 `Promise` 被成功 `resolve`）后，
// 会将包含创建好的订单信息的对象传递给 `doAddOrderGoods` 函数，以便执行添加订单商品的相关操作，将商品与订单进行关联并创建商品记录等操作。
.then(doAddOrderGoods)
// 当 `doAddOrderGoods` 函数执行完成且操作成功（返回的 `Promise` 被成功 `resolve`）后，会进入这个 `.then` 回调函数，
// 此时参数 `info` 包含了完整的订单及商品相关信息，通过回调函数 `cb` 将最终的订单对象（`info.order`）返回给调用者，
// 表示订单创建流程全部成功完成，调用者可以获取订单详细信息进行后续的业务操作，比如展示订单详情、进行订单状态更新等操作。
.then(function (info) {
    cb(null, info.order);
})
// 如果在整个 `doCheckOrderParams` -> `doCreateOrder` -> `doAddOrderGoods` 的链式异步操作过程中，任何一个环节出现错误（即 `Promise` 被 `reject`），
// 都会进入这个 `.catch` 回调函数，直接将错误信息通过回调函数 `cb` 返回给调用者，告知调用者订单创建过程出现问题以及具体的错误原因，方便调用者进行相应的错误处理和提示给用户等操作。
.catch(function (err) {
    cb(err);
});
}

// 对外暴露 `getAllOrders` 函数，用于获取满足特定条件的所有订单信息，接收查询相关参数（`params`）和一个回调函数（`cb`），
// 通过构建查询条件、分页处理等操作从数据库中获取订单数据，并将结果和相关统计信息通过回调函数返回给调用者，若操作过程中出现错误则返回相应错误信息。
module.exports.getAllOrders = function (params, cb) {
    // 创建一个空对象 `conditions`，用于存储后续构建的数据库查询条件，这些条件将用于筛选出符合要求的订单记录。
    var conditions = {};

    // 验证传入的 `params.pagenum` 参数是否存在且大于 `0`，`pagenum` 通常表示当前页码，用于分页查询操作。
    // 如果不存在或者小于等于 `0`，则不符合分页查询的基本要求，直接通过回调函数 `cb` 返回错误信息，表示 `pagenum` 参数错误，告知调用者参数不符合要求。
    if (!params.pagenum || params.pagenum <= 0) return cb("pagenum 参数错误");

    // 同样验证传入的 `params.pagesize` 参数是否存在且大于 `0`，`pagesize` 通常表示每页显示的记录数，也是分页查询的关键参数之一。
    // 如果不存在或者小于等于 `0`，则不符合分页查询的要求，通过回调函数 `cb` 返回错误信息，表示 `pagesize` 参数错误，告知调用者参数不符合要求。
    if (!params.pagesize || params.pagesize <= 0) return cb("pagesize 参数错误");

    // 在 `conditions` 对象中初始化 `columns` 属性为一个空对象，`columns` 属性用于存放具体的查询条件字段和对应的值，
    // 这些条件将构成数据库查询语句中的 `WHERE` 子句部分，用于精确筛选出需要的订单记录。
    conditions["columns"] = {};

    // 如果传入的参数中包含 `user_id`，表示调用者希望根据用户ID来筛选订单，将 `user_id` 及其对应的值添加到 `conditions["columns"]` 对象中，
    // 这样在数据库查询时就只会获取该用户ID对应的订单记录，方便按用户维度查询订单信息，具体业务含义取决于系统的用户与订单关联设计。
    if (params.user_id) {
        conditions["columns"]["user_id"] = params.user_id;
    }

    // 如果传入的参数中包含 `pay_status`，表示调用者希望根据订单的支付状态来筛选订单，将 `pay_status` 及其对应的值添加到 `conditions["columns"]` 对象中，
    // 使得数据库查询只返回支付状态符合该条件的订单记录，例如可以查询已支付、未支付等不同支付状态的订单，具体支付状态的表示和业务逻辑由系统定义。
    if (params.pay_status) {
        conditions["columns"]["pay_status"] = params.pay_status;
    }

    // 根据传入的 `is_send` 参数来设置查询条件中的发货状态相关信息，用于筛选出发货状态符合要求的订单记录。
    // 如果 `is_send` 参数为 `1`，则将 `conditions["columns"]["is_send"]` 设置为 `'是'`，表示查询已发货的订单；
    // 如果为其他值（包括未传入该参数的情况），则默认设置为 `'否'`，表示查询未发货的订单，具体发货状态的表示和业务含义由业务逻辑决定。
    if (params.is_send) {
        if (params.is_send == 1) {
            conditions["columns"]["is_send"] = '是';
        } else {
            conditions["columns"]["is_send"] = '否';
        }
    }

    // 根据传入的 `order_fapiao_title` 参数来设置查询条件中的发票抬头相关信息，用于筛选出发票抬头符合要求的订单记录。
    // 如果 `order_fapiao_title` 参数为 `1`，则将 `conditions["columns"]["order_fapiao_title"]` 设置为 `'个人'`；
    // 如果为其他值（通常应该是对应 `'公司'` 的标识，具体由业务逻辑确定），则设置为 `'公司'`，以此来区分不同发票抬头类型的订单，满足按发票抬头查询订单的业务需求。
    if (params.order_fapiao_title) {
        if (params.order_fapiao_title == 1) {
            conditions["columns"]["order_fapiao_title"] = '个人';
        } else {
            conditions["columns"]["order_fapiao_title"] = '公司';
        }
    }

    // 如果传入的参数中包含 `order_fapiao_company`，则使用 `orm` 库提供的 `like` 方法构建模糊查询条件，
    // 将 `order_fapiao_company` 及其对应的值（通过 `%` 包裹传入值，表示匹配包含该值的任意字符串）添加到 `conditions["columns"]` 对象中，
    // 这样可以查询发票对应的公司名称包含指定内容的订单记录，方便在不完全知道公司全称等情况下进行模糊筛选，满足根据发票公司相关信息查找订单的业务场景。
    if (params.order_fapiao_company) {
        conditions["columns"]["order_fapiao_company"] = orm.like("%" + params.order_fapiao_company + "%");
    }

    // 如果传入的参数中包含 `order_fapiao_content`，同样使用 `orm` 库的 `like` 方法构建模糊查询条件，
    // 将 `order_fapiao_content` 及其对应的值（用 `%` 包裹传入值构建模糊匹配模式）添加到 `conditions["columns"]` 对象中，
    // 以便查询发票内容包含指定内容的订单记录，满足按发票内容部分信息来查找订单的业务需求，例如查找包含特定服务或商品描述的发票对应的订单。
    if (params.order_fapiao_content) {
        conditions["columns"]["order_fapiao_content"] = orm.like("%" + params.order_fapiao_content + "%");
    }

    // 如果传入的参数中包含 `consignee_addr`，使用 `orm` 库的 `like` 方法构建模糊查询条件，
    // 将 `consignee_addr` 及其对应的值（用 `%` 包裹传入值形成模糊匹配模式）添加到 `conditions["columns"]` 对象中，
    // 这样就能查询收件人地址包含指定内容的订单记录，方便在只记得部分地址信息等情况下筛选订单，满足按收件人地址部分内容查找订单的业务场景。
    if (params.consignee_addr) {
        conditions["columns"]["consignee_addr"] = orm.like("%" + params.consignee_addr + "%");
    }

    // 调用 `dao` 模块的 `countByConditions` 方法来统计满足前面构建的查询条件的订单记录总数，
    // 第一个参数 `"OrderModel"` 表示要操作的数据模型，即对应数据库中的订单数据表，明确从哪个表中统计记录数量；
    // 第二个参数传入 `conditions` 对象作为查询条件，告知数据库按照这些条件去筛选并统计符合要求的订单记录数量；
    // 第三个参数是一个回调函数，用于处理统计操作完成后的结果情况，根据操作是否成功返回相应的信息给回调函数 `cb`，若成功则返回统计的记录总数，用于后续的分页计算等操作。
    dao.countByConditions("OrderModel", conditions, function (err, count) {
        // 如果在统计订单记录总数的过程中出现错误（`err` 不为 `null`），比如数据库查询语句执行失败、连接问题等原因，
        // 则直接通过回调函数 `cb` 返回错误信息给调用者，表示操作出现问题及具体的错误原因，告知调用者无法获取订单总数进行分页操作。
        if (err) return cb(err);

        // 获取传入的每页显示记录数参数 `params.pagesize`，赋值给变量 `pagesize`，方便后续在分页计算等操作中使用该值，保持代码的可读性。
        pagesize = params.pagesize;
        // 获取传入的当前页码参数 `params.pagenum`，赋值给变量 `pagenum`，同样用于后续的分页相关计算和操作，使代码逻辑更清晰。
        pagenum = params.pagenum;

        // 计算总页数，通过将满足条件的订单记录总数（`count`）除以每页显示记录数（`pagesize`），然后向上取整（使用 `Math.ceil` 函数）得到总页数，
        // 这个总页数用于判断分页情况以及限制页码范围等操作，确保分页查询的合理性和完整性。
        pageCount = Math.ceil(count / pagesize);

        // 计算分页查询的偏移量（`offset`），通过公式 `(当前页码 - 1) * 每页显示记录数` 来确定从哪条记录开始查询，
        // 例如当前页码为 `1` 时，偏移量为 `0`，表示从第一条记录开始查询；页码为 `2` 时，偏移量为 `pagesize`，表示从第 `pagesize + 1` 条记录开始查询，以此类推。
        offset = (pagenum - 1) * pagesize;

        // 如果计算出的偏移量大于等于记录总数（`count`），说明传入的页码可能超出了合理范围（比如总共有 `100` 条记录，每页显示 `10` 条，传入页码 `11` 就会出现这种情况），
        // 此时将偏移量设置为记录总数，避免查询出现错误，相当于查询最后一页（可能不满一页的情况）的数据，保证分页查询的健壮性。
        if (offset >= count) {
            offset = count;
        }

        // 获取每页显示记录数 `pagesize`，赋值给变量 `limit`，在后续构建数据库查询条件时用于指定每页获取的记录数量，确保分页查询按设定的每页数量返回数据。
        limit = pagesize;

        // 在 `conditions` 对象中添加 `offset` 属性，并将前面计算好的偏移量赋值给它，用于告知数据库查询从哪条记录开始获取，构建分页查询的偏移量条件，实现分页功能。
        conditions["offset"] = offset;

        // 在 `conditions` 对象中添加 `limit` 属性，并将每页显示记录数（`limit`）赋值给它，用于告知数据库每次查询获取的记录数量上限，与 `offset` 配合实现分页查询，获取指定页的数据。
        conditions["limit"] = limit;

        // 这里原本可能是要设置 `conditions["only"]` 的相关内容（可能用于指定只查询某些特定字段等操作，但代码中未完整实现），暂时保留注释状态，可能后续需要根据业务需求补充完善。
        // conditions["only"] = 

        // 在 `conditions` 对象中添加 `order` 属性，并设置值为 `"-create_time"`，用于指定数据库查询结果的排序方式，
        // 这里表示按照订单创建时间倒序排列（`-` 号表示倒序，`create_time` 为订单创建时间字段），使得最新创建的订单排在前面，方便展示最新的数据等业务场景使用。
        conditions["order"] = "-create_time";

        // 调用 `dao` 模块的 `list` 方法来执行实际的数据库查询操作，获取满足前面构建的所有条件（包括筛选条件、分页条件、排序条件等）的订单记录列表，
        // 第一个参数 `"OrderModel"` 明确要操作的数据模型，即从订单数据表中查询数据；
        // 第二个参数传入 `conditions` 对象，包含了完整的查询条件信息，告知数据库如何筛选、分页以及排序数据；
        // 第三个参数是一个回调函数，用于处理查询操作完成后的结果情况，根据操作是否成功返回相应的信息给回调函数 `cb`，若成功则返回查询到的订单记录列表以及相关统计信息，供调用者进行后续的业务处理。
        dao.list("OrderModel", conditions, function (err, orders) {
            // 如果在查询订单记录列表的过程中出现错误（`err` 不为 `null`），比如数据库查询语句执行失败、连接问题等原因，
            // 则直接通过回调函数 `cb` 返回错误信息给调用者，表示操作出现问题及具体的错误原因，告知调用者无法获取订单列表数据。
            if (err) return cb(err);

            // 创建一个空对象 `resultDta`，用于存储最终要返回给调用者的结果信息，包含订单记录总数、当前页码以及订单记录列表等内容，方便调用者统一获取和处理查询结果。
            var resultDta = {};

            // 将前面统计得到的满足条件的订单记录总数（`count`）赋值给 `resultDta` 对象的 `total` 属性，方便调用者获取总的订单数量信息，例如用于展示分页导航中的总记录数等功能。
            resultDta["total"] = count;

            // 将传入的当前页码参数（`params.pagenum`）赋值给 `resultDta` 对象的 `pagenum` 属性，方便调用者知晓当前查询的是第几页数据，用于分页相关的展示和逻辑处理。
            resultDta["pagenum"] = pagenum;

            // 使用 `lodash` 库的 `map` 函数遍历查询到的订单记录列表（`orders`），对每个订单记录（`order`）进行处理，
            // 这里暂时只是直接返回每个订单记录（后续可能可以根据业务需求进一步处理订单记录，比如省略某些敏感字段等，代码中 `_.omit(order,)` 可能就是预留的这种处理方式但未完整实现），
            // 将处理后的订单记录数组赋值给 `resultDta` 对象的 `goods` 属性（这里 `goods` 命名可能不太准确，更合适的可能是 `orders` 之类的表示订单列表的名称，具体要根据业务逻辑确定），方便调用者获取具体的订单记录数据进行展示、分析等操作。
            resultDta["goods"] = _.map(orders, function (order) {
                return order; // _.omit(order,);
            });

            // 将包含订单记录总数、当前页码以及订单记录列表等完整信息的 `resultDta` 对象通过回调函数 `cb` 返回给调用者，
            // 表示查询操作成功完成，调用者可以根据这些信息进行后续的业务处理，比如在前端页面展示订单列表、进行分页导航等操作。
            cb(err, resultDta);
        })
    });
}

// 对外暴露 `getOrder` 函数，用于获取指定订单的详细信息，接收订单ID（`orderId`）和一个回调函数（`cb`），
// 通过一系列异步操作（先获取订单基本信息，再获取对应的订单商品信息）来整合完整的订单详情，最后通过回调函数返回给调用者，若出现错误则返回相应错误信息。
module.exports.getOrder = function (orderId, cb) {
    // 首先验证传入的订单ID（`orderId`）是否存在，如果不存在则通过回调函数 `cb` 返回错误信息，表示用户ID不能为空，
    // 这里可能是表述上的小问题，更准确应该是订单ID不能为空，因为没有订单ID无法确定要获取哪个订单的详细信息，告知调用者参数不符合要求。
    if (!orderId) return cb("用户ID不能为空");

    // 进一步验证订单ID是否可以转换为数字类型，如果不能转换成功（即不是有效的数字），则通过回调函数 `cb` 返回错误信息，表示用户ID必须是数字，
    // 因为通常在数据库中订单ID是以数字形式存储和使用的，需要传入合法的数字类型的订单ID才能准确查询到对应的订单记录，告知调用者参数不符合要求。
    if (isNaN(parseInt(orderId))) return cb("用户ID必须是数字");

    // 调用 `doGetOrder` 函数，传入包含订单ID的对象（`{"order_id":orderId}`），用于获取指定订单的基本信息，`doGetOrder` 函数返回一个 `Promise
	// 将传入的 `orderId` 参数赋值给 `params` 对象中的 `order_id` 属性，这样 `params` 对象就包含了要更新的订单的标识信息，
// 后续在进行订单参数验证等操作时，可以基于这个完整的 `params` 对象来处理，确保更新操作针对正确的订单进行。
params["order_id"] = orderId;

// 调用 `doCheckOrderParams` 函数对包含了订单ID以及其他可能的更新参数（在 `params` 对象中）进行合法性检查和预处理，
// 该函数返回一个 `Promise`，若参数验证通过会传递处理好的订单信息，若验证不通过则会返回相应的错误信息拒绝 `Promise`。
doCheckOrderParams(params)

// 使用 `.then` 方法链式调用 `doUpdateOrder` 函数，当 `doCheckOrderParams` 函数成功完成（即参数验证通过并整理好信息）后，
// 会将处理后的包含订单相关信息的对象传递给 `doUpdateOrder` 函数，由它执行更新订单信息的数据库操作，
// `doUpdateOrder` 函数同样返回一个 `Promise`，成功则包含更新后的订单详细信息，失败则返回错误信息。
.then(doUpdateOrder)

// 继续链式调用 `doGetAllOrderGoods` 函数，当 `doUpdateOrder` 成功更新订单信息后，会将包含更新后订单信息的对象传递给 `doGetAllOrderGoods` 函数，
// 由它执行获取该订单所有商品信息的操作，`doGetAllOrderGoods` 函数返回的 `Promise` 成功则返回包含完整订单及商品信息的对象，失败则返回错误信息。
.then(doGetAllOrderGoods)

// 当 `doGetAllOrderGoods` 函数执行完成且操作成功（返回的 `Promise` 被成功 `resolve`）后，会进入这个 `.then` 回调函数，
// 此时参数 `info` 包含了完整的更新后的订单及商品相关信息，通过回调函数 `cb` 将最终的订单对象（`info.order`）返回给调用者，
// 表示订单更新流程全部成功完成，调用者可以获取更新后的订单详细信息进行后续的业务操作，比如展示最新的订单详情、根据新的订单状态进行相应处理等操作。
.then(function (info) {
    cb(null, info.order);
})

// 如果在整个 `doCheckOrderParams` -> `doUpdateOrder` -> `doGetAllOrderGoods` 的链式异步操作过程中，任何一个环节出现错误（即 `Promise` 被 `reject`），
// 都会进入这个 `.catch` 回调函数，直接将错误信息通过回调函数 `cb` 返回给调用者，告知调用者订单更新过程出现问题以及具体的错误原因，方便调用者进行相应的错误处理和提示给用户等操作。
.catch(function (err) {
    cb(err);
});

}
//这段代码整体实现了更新订单信息的功能逻辑，通过对传入参数的验证、订单信息更新以及获取更新后订
//单商品信息等一系列步骤，保证了订单更新操作的完整性和准确性，并且通过回调函数将最终结果或错误
//信息返回给调用者，方便在外部进行相应的业务处理和错误提示。