You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

KnexJs如何在销售查询中关联商品明细并解决返回空对象问题

问题原因
  • 你在第一个then中通过map返回的是包含多个未执行完成的Promise数组,没有等待所有子查询 resolve 就直接返回给下游,接口响应时序列化Promise对象就会得到空值。
  • 子查询返回的values是商品明细数组,你使用对象展开运算符{...values}把数组转成了以索引为key的对象,不符合明细列表的预期结构。
  • 补充:查询类接口建议使用200状态码,201通常用于资源创建成功的场景。
方案1:基于原有写法调整(仅修复逻辑,未优化性能)

只需要用Promise.all包裹所有子查询任务,等待全部执行完成后再返回,同时修正明细字段的赋值逻辑:

const getSales = (req, res) => {
    db('tbl_venda')
        .select('tbl_venda.id_venda', 'tbl_venda.pedido', 'tbl_venda.data_venda',
            'tbl_venda.data_entrega', 'tbl_cliente.nome AS cliente', 'tbl_vendedor.nome AS vendedor',
            'tbl_venda.obs', 'tbl_venda.total_pagar', 'tbl_venda.total_bruto', 'tbl_venda.id_loja',
            'tbl_venda.horadavenda')
        .join('tbl_cliente', 'tbl_cliente.id_cliente', 'tbl_venda.id_cliente')
        .join('tbl_vendedor', 'tbl_vendedor.id_vendedor', 'tbl_venda.id_vendedor')
        .where('data_venda', '>=', req.body.dateInitial)
        .where('data_venda', '<=', req.body.dateFinal)
        .orderBy('id_venda')
        .then(rows => {
            // 用Promise.all等待所有子查询完成
            return Promise.all(rows.map(row => {
                return db('tbl_venda_itens')
                    .select('tbl_venda_itens.id_venda_itens', 'tbl_venda_itens.id_venda',
                        'tbl_fornecedor.nome_fantasia AS fabricante', 'tbl_venda_itens.quantidade',
                        'tbl_estoque.descricao AS produto', 'tbl_venda_itens.valor_unitario',
                        'tbl_venda_itens.valor_total', 'tbl_venda_itens.cor', 'tbl_venda_itens.desconto',
                        'tbl_venda_itens.preco_final')
                    .join('tbl_fornecedor', 'tbl_venda_itens.id_fornecedor', 'tbl_fornecedor.id_fornecedor')
                    .join('tbl_estoque', 'tbl_venda_itens.id_estoque', 'tbl_estoque.id_estoque')
                    .where('id_venda', '=', row.id_venda)
                    .then(values => {
                        // 直接把明细数组赋值给items字段,不需要展开
                        return { ...row, items: values }
                    })
            }))
        })
        .then(values => res.status(200).send(values))
        .catch(error => showError(error, res))
}
方案2:更推荐的高性能实现(避免N+1查询)

原有写法每一条销售记录都会发起一次数据库查询,数据量稍大就会有严重性能问题,建议使用一次关联查询+结果分组的方式实现:

const getSales = async (req, res) => {
    try {
        const rawData = await db('tbl_venda')
            .select(
                'tbl_venda.id_venda', 'tbl_venda.pedido', 'tbl_venda.data_venda',
                'tbl_venda.data_entrega', 'tbl_cliente.nome AS cliente', 'tbl_vendedor.nome AS vendedor',
                'tbl_venda.obs', 'tbl_venda.total_pagar', 'tbl_venda.total_bruto', 'tbl_venda.id_loja',
                'tbl_venda.horadavenda',
                // 追加明细字段
                'tbl_venda_itens.id_venda_itens', 'tbl_fornecedor.nome_fantasia AS fabricante', 
                'tbl_venda_itens.quantidade', 'tbl_estoque.descricao AS produto', 
                'tbl_venda_itens.valor_unitario', 'tbl_venda_itens.valor_total', 
                'tbl_venda_itens.cor', 'tbl_venda_itens.desconto', 'tbl_venda_itens.preco_final'
            )
            .join('tbl_cliente', 'tbl_cliente.id_cliente', 'tbl_venda.id_cliente')
            .join('tbl_vendedor', 'tbl_vendedor.id_vendedor', 'tbl_venda.id_vendedor')
            .leftJoin('tbl_venda_itens', 'tbl_venda_itens.id_venda', 'tbl_venda.id_venda')
            .leftJoin('tbl_fornecedor', 'tbl_venda_itens.id_fornecedor', 'tbl_fornecedor.id_fornecedor')
            .leftJoin('tbl_estoque', 'tbl_venda_itens.id_estoque', 'tbl_estoque.id_estoque')
            .where('data_venda', '>=', req.body.dateInitial)
            .where('data_venda', '<=', req.body.dateFinal)
            .orderBy('tbl_venda.id_venda')

        // 按销售单ID分组组装数据
        const result = Object.values(rawData.reduce((acc, cur) => {
            if (!acc[cur.id_venda]) {
                acc[cur.id_venda] = {
                    id_venda: cur.id_venda,
                    pedido: cur.pedido,
                    data_venda: cur.data_venda,
                    data_entrega: cur.data_entrega,
                    cliente: cur.cliente,
                    vendedor: cur.vendedor,
                    obs: cur.obs,
                    total_pagar: cur.total_pagar,
                    total_bruto: cur.total_bruto,
                    id_loja: cur.id_loja,
                    horadavenda: cur.horadavenda,
                    items: []
                }
            }
            // 存在明细数据时追加
            if (cur.id_venda_itens) {
                acc[cur.id_venda].items.push({
                    id_venda_itens: cur.id_venda_itens,
                    fabricante: cur.fabricante,
                    quantidade: cur.quantidade,
                    produto: cur.produto,
                    valor_unitario: cur.valor_unitario,
                    valor_total: cur.valor_total,
                    cor: cur.cor,
                    desconto: cur.desconto,
                    preco_final: cur.preco_final
                })
            }
            return acc
        }, {}))

        res.status(200).send(result)
    } catch (error) {
        showError(error, res)
    }
}

内容的提问来源于stack exchange,提问作者Bruno Nobre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 00:06:09