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
相关产品推荐
相关产品推荐

