fix(billing): 修复账单详情查询的数据库并发错误
问题: - Npgsql.NpgsqlOperationInProgressException: A command is already in progress - 在同一个数据库连接上,billReader 未释放就执行 paymentReader 根因: - GetBillingDetailQueryHandler 中先查询账单并打开 billReader - 读取账单数据后未释放 reader - 直接在同一连接上执行支付记录查询,触发并发异常 解决方案: - 将账单字段先读取到本地变量 - 主动 DisposeAsync 释放 billReader - 再执行支付记录查询 - 最后用本地变量组装 BillingDetailDto
This commit is contained in:
@@ -46,14 +46,35 @@ public sealed class GetBillingDetailQueryHandler(
|
||||
throw new BusinessException(ErrorCodes.NotFound, "账单不存在");
|
||||
}
|
||||
|
||||
// 1.2 (空行后) 读取账单行数据到内存(释放 Reader,避免同连接并发执行命令)
|
||||
var billingId = billReader.GetInt64(0);
|
||||
var tenantId = billReader.GetInt64(1);
|
||||
var tenantName = billReader.IsDBNull(2) ? string.Empty : billReader.GetString(2);
|
||||
long? subscriptionId = billReader.IsDBNull(3) ? null : billReader.GetInt64(3);
|
||||
var statementNo = billReader.GetString(4);
|
||||
var billingType = (BillingType)billReader.GetInt32(5);
|
||||
var status = (TenantBillingStatus)billReader.GetInt32(6);
|
||||
var periodStart = billReader.GetDateTime(7);
|
||||
var periodEnd = billReader.GetDateTime(8);
|
||||
var amountDue = billReader.GetDecimal(9);
|
||||
var discountAmount = billReader.GetDecimal(10);
|
||||
var taxAmount = billReader.GetDecimal(11);
|
||||
var amountPaid = billReader.GetDecimal(12);
|
||||
var currency = billReader.IsDBNull(13) ? "CNY" : billReader.GetString(13);
|
||||
var dueDate = billReader.GetDateTime(14);
|
||||
DateTime? reminderSentAt = billReader.IsDBNull(15) ? null : billReader.GetDateTime(15);
|
||||
DateTime? overdueNotifiedAt = billReader.IsDBNull(16) ? null : billReader.GetDateTime(16);
|
||||
var notes = billReader.IsDBNull(17) ? null : billReader.GetString(17);
|
||||
var lineItemsJson = billReader.IsDBNull(18) ? null : billReader.GetString(18);
|
||||
var createdAt = billReader.GetDateTime(19);
|
||||
long? createdBy = billReader.IsDBNull(20) ? null : billReader.GetInt64(20);
|
||||
DateTime? updatedAt = billReader.IsDBNull(21) ? null : billReader.GetDateTime(21);
|
||||
long? updatedBy = billReader.IsDBNull(22) ? null : billReader.GetInt64(22);
|
||||
|
||||
// 1.2 (空行后) 反序列化账单明细
|
||||
// 1.3 (空行后) 主动释放账单 Reader,确保后续查询不会触发 Npgsql 并发命令异常
|
||||
await billReader.DisposeAsync();
|
||||
|
||||
// 1.4 (空行后) 反序列化账单明细
|
||||
var lineItems = new List<BillingLineItemDto>();
|
||||
if (!string.IsNullOrWhiteSpace(lineItemsJson))
|
||||
{
|
||||
@@ -67,7 +88,7 @@ public sealed class GetBillingDetailQueryHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// 1.3 (空行后) 查询支付记录
|
||||
// 1.5 (空行后) 查询支付记录
|
||||
var payments = new List<PaymentRecordDto>();
|
||||
await using var paymentCommand = CreateCommand(
|
||||
connection,
|
||||
@@ -100,39 +121,36 @@ public sealed class GetBillingDetailQueryHandler(
|
||||
});
|
||||
}
|
||||
|
||||
// 1.4 (空行后) 组装详情 DTO
|
||||
var amountDue = billReader.GetDecimal(9);
|
||||
var discountAmount = billReader.GetDecimal(10);
|
||||
var taxAmount = billReader.GetDecimal(11);
|
||||
// 1.6 (空行后) 组装详情 DTO
|
||||
var totalAmount = amountDue - discountAmount + taxAmount;
|
||||
|
||||
return new BillingDetailDto
|
||||
{
|
||||
Id = billReader.GetInt64(0),
|
||||
TenantId = billReader.GetInt64(1),
|
||||
TenantName = billReader.IsDBNull(2) ? string.Empty : billReader.GetString(2),
|
||||
SubscriptionId = billReader.IsDBNull(3) ? null : billReader.GetInt64(3),
|
||||
StatementNo = billReader.GetString(4),
|
||||
BillingType = (BillingType)billReader.GetInt32(5),
|
||||
Status = (TenantBillingStatus)billReader.GetInt32(6),
|
||||
PeriodStart = billReader.GetDateTime(7),
|
||||
PeriodEnd = billReader.GetDateTime(8),
|
||||
AmountDue = billReader.GetDecimal(9),
|
||||
DiscountAmount = billReader.GetDecimal(10),
|
||||
TaxAmount = billReader.GetDecimal(11),
|
||||
Id = billingId,
|
||||
TenantId = tenantId,
|
||||
TenantName = tenantName,
|
||||
SubscriptionId = subscriptionId,
|
||||
StatementNo = statementNo,
|
||||
BillingType = billingType,
|
||||
Status = status,
|
||||
PeriodStart = periodStart,
|
||||
PeriodEnd = periodEnd,
|
||||
AmountDue = amountDue,
|
||||
DiscountAmount = discountAmount,
|
||||
TaxAmount = taxAmount,
|
||||
TotalAmount = totalAmount,
|
||||
AmountPaid = billReader.GetDecimal(12),
|
||||
Currency = billReader.IsDBNull(13) ? "CNY" : billReader.GetString(13),
|
||||
DueDate = billReader.GetDateTime(14),
|
||||
AmountPaid = amountPaid,
|
||||
Currency = currency,
|
||||
DueDate = dueDate,
|
||||
ReminderSentAt = reminderSentAt,
|
||||
OverdueNotifiedAt = overdueNotifiedAt,
|
||||
LineItemsJson = lineItemsJson,
|
||||
LineItems = lineItems,
|
||||
Payments = payments,
|
||||
Notes = notes,
|
||||
CreatedAt = billReader.GetDateTime(19),
|
||||
CreatedAt = createdAt,
|
||||
CreatedBy = createdBy,
|
||||
UpdatedAt = billReader.IsDBNull(21) ? null : billReader.GetDateTime(21),
|
||||
UpdatedAt = updatedAt,
|
||||
UpdatedBy = updatedBy
|
||||
};
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user