# 合同列表查询优化建议 ## 问题描述 合同列表需要显示发票相关的金额信息,需要关联发票表进行计算: - **合同金额**:合同本身的金额 - **已开票金额**:该合同下所有发票的开票金额总和 - **已付款金额**:已付款的发票金额总和 - **未确认开票金额**:合同金额 - 已开票金额(即尚未开票的金额) ## 数据库表关系 ``` biz_contract (合同表) | |-- contract_id (合同ID) | |-- biz_invoice (发票表) | |-- contract_id (关联合同ID) |-- invoice_amount (开票金额) |-- status (发票状态: 0=已开票未付款, 1=已开票已付款) ``` ## SQL 查询优化建议 ### 当前查询(可能缺少发票关联) ```sql SELECT bc.*, COUNT(ba.attachment_id) as attachment_count FROM biz_contract bc LEFT JOIN biz_attachment ba ON bc.contract_id = ba.business_id AND ba.business_type = 'contract' WHERE 1=1 -- 问题:缺少发票表的关联 ``` ### 优化后的查询 ```sql SELECT bc.contract_id, bc.contract_code, bc.contract_name, bc.contract_amount, bc.contract_type, -- 已开票金额(该合同下所有发票的总金额) COALESCE(invoiced.total_invoiced_amount, 0) as invoiced_amount, -- 已付款金额(已付款的发票总金额) COALESCE(paid.total_paid_amount, 0) as paid_amount, -- 未确认开票金额(合同金额 - 已开票金额) bc.contract_amount - COALESCE(invoiced.total_invoiced_amount, 0) as unpaid_amount, -- 附件数量 COALESCE(attachment_count, 0) as attachment_count FROM biz_contract bc -- 关联已开票金额统计 LEFT JOIN ( SELECT contract_id, SUM(invoice_amount) as total_invoiced_amount FROM biz_invoice WHERE status IN ('0', '1') -- 已开票状态 GROUP BY contract_id ) invoiced ON bc.contract_id = invoiced.contract_id -- 关联已付款金额统计 LEFT JOIN ( SELECT contract_id, SUM(invoice_amount) as total_paid_amount FROM biz_invoice WHERE status = '1' -- 已付款状态 GROUP BY contract_id ) paid ON bc.contract_id = paid.contract_id -- 附件数量统计 LEFT JOIN ( SELECT business_id, COUNT(*) as attachment_count FROM biz_attachment WHERE business_type = 'contract' GROUP BY business_id ) attachments ON bc.contract_id = attachments.business_id WHERE 1=1 ORDER BY bc.create_time DESC ``` ## MyBatis-Plus 配置建议 ### 方案1:在 VO 类中添加统计字段 创建 `BizContractVO` 类: ```java @Data public class BizContractVO extends BizContract { /** 已开票金额 */ private BigDecimal invoicedAmount; /** 已付款金额 */ private BigDecimal paidAmount; /** 未确认开票金额 */ private BigDecimal unpaidAmount; /** 附件数量 */ private Integer attachmentCount; } ``` ### 方案2:在 Mapper 中使用关联查询 ```java @Mapper public interface BizContractMapper extends BaseMapper { @Select(""" SELECT bc.contract_id, bc.contract_code, bc.contract_name, bc.contract_amount, bc.contract_type, bc.paid_amount, bc.unpaid_amount, COALESCE(invoiced.total_invoiced_amount, 0) as invoiced_amount, bc.contract_amount - COALESCE(invoiced.total_invoiced_amount, 0) as unpaid_amount, COALESCE(paid.total_paid_amount, 0) as paid_amount, COALESCE(attachments.attachment_count, 0) as attachment_count FROM biz_contract bc LEFT JOIN ( SELECT contract_id, SUM(invoice_amount) as total_invoiced_amount FROM biz_invoice WHERE status IN ('0', '1') GROUP BY contract_id ) invoiced ON bc.contract_id = invoiced.contract_id LEFT JOIN ( SELECT contract_id, SUM(invoice_amount) as total_paid_amount FROM biz_invoice WHERE status = '1' GROUP BY contract_id ) paid ON bc.contract_id = paid.contract_id LEFT JOIN ( SELECT business_id, COUNT(*) as attachment_count FROM biz_attachment WHERE business_type = 'contract' GROUP BY business_id ) attachments ON bc.contract_id = attachments.business_id ${ew.customSqlSegment} """) Page selectContractPage( @Param("page") Page page, @Param("ew") Wrapper queryWrapper ); } ``` ## 字段计算逻辑说明 ### 1. 合同金额 (contract_amount) - 直接使用合同表中的 `contract_amount` 字段 - 金额由合同创建时确定 ### 2. 已开票金额 (invoiced_amount) ```sql SUM(invoice_amount) WHERE contract_id = ? AND status IN ('0', '1') -- 已开票状态(已开票未付款 + 已开票已付款) ``` - 计算该合同下所有已开票的发票金额总和 - 不论是否付款,只要开票就算 ### 3. 已付款金额 (paid_amount) ```sql SUM(invoice_amount) WHERE contract_id = ? AND status = '1' -- 已付款状态 ``` - 计算该合同下已付款的发票金额总和 - 只有状态为"已开票已付款"的发票才算 ### 4. 未确认开票金额 (unpaid_amount) ```sql contract_amount - invoiced_amount ``` - 合同金额减去已开票金额 - 表示尚未开票的金额 ## 数据返回格式示例 ```json { "code": 200, "msg": "查询成功", "rows": [ { "contractId": 1, "contractCode": "CONTRACT001", "contractName": "技术服务合同", "contractAmount": 100000.00, "contractTypeName": "人月合同", "invoicedAmount": 60000.00, "paidAmount": 40000.00, "unpaidAmount": 40000.00, "attachmentCount": 2, "createTime": "2024-01-01 10:00:00" } ], "total": 100 } ``` ## 前端显示 | 列名 | 说明 | 示例 | |------|------|------| | 合同编号 | 合同的唯一编号 | CONTRACT001 | | 合同名称 | 合同的名称 | 技术服务合同 | | 合同金额(元) | 合同总金额 | 100,000.00 | | 已开票金额(元) | 该合同已开票金额总和 | 60,000.00 | | 已付款金额(元) | 该合同已付款金额总和 | 40,000.00 | | 未付款金额(元) | 合同金额-已开票金额 | 40,000.00 | ## 注意事项 1. **LEFT JOIN 使用**:确保使用 LEFT JOIN,避免因为没有发票数据的合同导致记录不显示 2. **金额计算精度**:使用 `DECIMAL` 或 `BIGINT` 类型存储金额,避免精度丢失 3. **NULL 值处理**:使用 `COALESCE(value, 0)` 将 NULL 转换为 0 4. **性能优化**:为关联字段添加索引 - `biz_invoice.contract_id` - `biz_invoice.status` 5. **数据一致性**:确保发票状态正确更新(从"已开票未付款"到"已开票已付款") ## 验证方法 1. 查看合同列表API返回的数据是否包含 `invoicedAmount` 字段 2. 确认金额计算正确: - `invoicedAmount` = 所有发票金额总和 - `paidAmount` = 已付款发票金额总和 - `unpaidAmount` = `contractAmount` - `invoicedAmount` 3. 前端列表能正确格式化显示金额(千分位分隔,保留两位小数)