xykj-plus/xykj-web-plus/backend-optimization-invoice-list.md
2026-06-13 13:48:51 +08:00

254 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 发票列表查询优化建议
## 问题描述
当前发票列表查询没有关联项目表和结算表,导致无法显示项目名称、结算年份、结算季度等信息。
## 需求
发票列表需要显示以下字段:
- 项目编号 (projectCode)
- 项目名称 (projectName)
- 结算年份 (settlementYear)
- 结算季度 (settlementQuarter)
- 税率 (taxRate)
## 数据库表关系
```
biz_invoice (发票表)
|
|-- settlementId (结算ID) --> biz_settlement (结算表)
| |
| |-- projectId --> biz_project (项目表)
| |-- contractId --> biz_contract (合同表)
|
|-- contractId (合同ID) --> biz_contract (合同表)
```
## SQL 查询优化建议
### 当前查询(可能的问题)
```sql
SELECT
bi.*,
ba.attachmentCount
FROM biz_invoice bi
LEFT JOIN (
SELECT businessId, COUNT(*) as attachmentCount
FROM biz_attachment
WHERE businessType = 'invoice'
GROUP BY businessId
) ba ON bi.business_id = ba.businessId
-- 问题:缺少关联 biz_settlement 和 biz_project 表
```
### 优化后的查询
```sql
SELECT
bi.invoice_id,
bi.invoice_no,
bi.invoice_title_a,
bi.invoice_title_b,
bi.invoice_amount,
bi.settlement_id,
bi.contract_id,
bi.tax_rate,
bi.status,
bi.payment_date,
bi.payment_remark,
bi.remark,
bi.create_time,
-- 结算表字段
bs.settlement_year,
bs.settlement_quarter,
bs.settlement_quarter_name,
-- 项目表字段
bp.project_id,
bp.project_code,
bp.project_name,
-- 合同表字段
bc.contract_code,
bc.contract_name,
bc.contract_amount,
-- 附件数量
COALESCE(ba.attachment_count, 0) as attachment_count
FROM biz_invoice bi
LEFT JOIN biz_settlement bs ON bi.settlement_id = bs.settlement_id
LEFT JOIN biz_project bp ON bs.project_id = bp.project_id
LEFT JOIN biz_contract bc ON bi.contract_id = bc.contract_id
LEFT JOIN (
SELECT business_id, COUNT(*) as attachment_count
FROM biz_attachment
WHERE business_type = 'invoice'
GROUP BY business_id
) ba ON bi.invoice_id = ba.business_id
WHERE 1=1
ORDER BY bi.create_time DESC
```
## MyBatis-Plus 配置建议
如果使用 MyBatis-Plus可以使用以下方式
### 方案1使用 @TableField (注解方式)
```java
public class BizInvoice {
@TableId
private Long invoiceId;
@TableField("settlement_id")
private Long settlementId;
// 以下字段不对应数据库表字段,仅用于返回前端
@TableField(exist = false)
private String projectCode;
@TableField(exist = false)
private String projectName;
@TableField(exist = false)
private String settlementYear;
@TableField(exist = false)
private String settlementQuarter;
@TableField(exist = false)
private String taxRate;
// ... 其他字段
}
```
### 方案2使用 VO 类
创建一个 `BizInvoiceVO` 类:
```java
@Data
public class BizInvoiceVO extends BizInvoice {
private String projectCode;
private String projectName;
private String settlementYear;
private String settlementQuarter;
private String settlementQuarterName;
private String taxRate;
private String taxRateName;
private String contractCode;
private String contractName;
private BigDecimal contractAmount;
private Integer attachmentCount;
}
```
### 方案3在 Mapper 中使用关联查询
```java
@Mapper
public interface BizInvoiceMapper extends BaseMapper<BizInvoice> {
@Select("""
SELECT
bi.*,
bs.settlement_year,
bs.settlement_quarter,
bs.settlement_quarter_name,
bp.project_code,
bp.project_name,
bc.contract_code,
bc.contract_name,
bc.contract_amount,
COALESCE(ba.attachment_count, 0) as attachment_count
FROM biz_invoice bi
LEFT JOIN biz_settlement bs ON bi.settlement_id = bs.settlement_id
LEFT JOIN biz_project bp ON bs.project_id = bp.project_id
LEFT JOIN biz_contract bc ON bi.contract_id = bc.contract_id
LEFT JOIN (
SELECT business_id, COUNT(*) as attachment_count
FROM biz_attachment
WHERE business_type = 'invoice'
GROUP BY business_id
) ba ON bi.invoice_id = ba.business_id
${ew.customSqlSegment}
""")
Page<BizInvoiceVO> selectInvoicePage(
@Param("page") Page<BizInvoiceVO> page,
@Param("ew") Wrapper<BizInvoice> queryWrapper
);
}
```
## 查询条件处理
查询时支持按以下条件筛选:
- 发票抬头(甲方) - invoice_title_a
- 发票抬头(乙方) - invoice_title_b
- 结算信息ID - settlement_id
- 发票状态 - status
```java
// 示例查询条件
QueryWrapper<BizInvoice> wrapper = new QueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(params.getInvoiceTitleA()),
"invoice_title_a", params.getInvoiceTitleA())
.like(StringUtils.isNotBlank(params.getInvoiceTitleB()),
"invoice_title_b", params.getInvoiceTitleB())
.eq(params.getSettlementId() != null,
"settlement_id", params.getSettlementId())
.eq(StringUtils.isNotBlank(params.getStatus()),
"status", params.getStatus());
```
## 数据返回格式
```json
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"invoiceId": 1,
"invoiceNo": "INV2024001",
"invoiceTitleA": "甲方公司",
"invoiceTitleB": "乙方公司",
"invoiceAmount": 10000.00,
"settlementId": 10,
"projectId": 5,
"projectCode": "PRJ001",
"projectName": "某某项目",
"settlementYear": "2024",
"settlementQuarter": "1",
"taxRate": "6",
"taxRateName": "6%",
"contractId": 3,
"contractCode": "CONTRACT001",
"contractName": "服务合同",
"contractAmount": 50000.00,
"status": "0",
"paymentDate": null,
"remark": null,
"createTime": "2024-01-01 10:00:00",
"attachmentCount": 2
}
],
"total": 100
}
```
## 注意事项
1. **LEFT JOIN 使用**:确保使用 LEFT JOIN避免因为没有关联数据而导致发票记录不显示
2. **字段映射**确保数据库字段与Java实体类字段正确映射使用下划线转驼峰
3. **字典转换**:后端需要将字典代码转换为字典名称
- `settlementQuarter` (1,2,3,4) -> `settlementQuarterName` (第一季度,第二季度...)
- `taxRate` (6,13) -> `taxRateName` (6%,13%)
4. **性能优化**:为关联字段添加索引
- `biz_invoice.settlement_id`
- `biz_invoice.contract_id`
- `biz_settlement.project_id`
## 验证方法
1. 查看发票列表API返回的数据是否包含项目名称、结算年份、结算季度等字段
2. 确认这些字段有正确的值不是null或空
3. 前端列表能正确显示这些信息