feat: 完善手动创建租户功能,添加账单明细和支付记录

1. 手动创建租户时自动生成账单明细(LineItemsJson)
2. 账单状态为已支付时自动创建支付记录
3. 租户列表接口返回联系人信息和认证状态
4. 账单详情接口返回支付记录和解析后的账单明细
5. 管理员账号自动复用租户联系人信息

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
MSuMshk
2026-02-02 17:32:05 +08:00
parent b4a597fe08
commit 59bc3005af
15 changed files with 1513 additions and 6 deletions

View File

@@ -86,6 +86,11 @@ public sealed record BillingDetailDto
/// </summary>
public string? LineItemsJson { get; init; }
/// <summary>
/// 账单明细列表(从 LineItemsJson 解析)。
/// </summary>
public IReadOnlyList<BillingLineItemDto>? LineItems { get; init; }
/// <summary>
/// 备注。
/// </summary>
@@ -195,3 +200,34 @@ public sealed record PaymentRecordDto
/// </summary>
public DateTime CreatedAt { get; init; }
}
/// <summary>
/// 账单明细项 DTO。
/// </summary>
public sealed record BillingLineItemDto
{
/// <summary>
/// 项目名称。
/// </summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// 单价。
/// </summary>
public decimal UnitPrice { get; init; }
/// <summary>
/// 数量。
/// </summary>
public int Quantity { get; init; }
/// <summary>
/// 金额。
/// </summary>
public decimal Amount { get; init; }
/// <summary>
/// 备注。
/// </summary>
public string? Remark { get; init; }
}

View File

@@ -1,3 +1,4 @@
using System.Text.Json;
using MediatR;
using TakeoutSaaS.Application.App.Billings.Contracts;
using TakeoutSaaS.Application.App.Billings.Queries;
@@ -25,7 +26,43 @@ public sealed class GetBillingDetailQueryHandler(IBillingRepository billingRepos
return null;
}
// 3. 映射为 DTO 并返回(支付记录暂时返回空列表)
// 3. 查询支付记录
var payments = await billingRepository.GetPaymentsByBillingIdAsync(request.BillingId, cancellationToken);
// 4. 映射支付记录为 DTO
var paymentDtos = payments.Select(p => new PaymentRecordDto
{
Id = p.Id,
BillingStatementId = p.BillingStatementId,
Amount = p.Amount,
Method = (int)p.Method,
Status = (int)p.Status,
TransactionNo = p.TransactionNo,
ProofUrl = p.ProofUrl,
PaidAt = p.PaidAt,
Notes = p.Notes,
VerifiedBy = p.VerifiedBy,
VerifiedAt = p.VerifiedAt,
RefundReason = p.RefundReason,
RefundedAt = p.RefundedAt,
CreatedAt = p.CreatedAt
}).ToList();
// 5. 解析账单明细 JSON
List<BillingLineItemDto>? lineItems = null;
if (!string.IsNullOrEmpty(detail.LineItemsJson))
{
try
{
lineItems = JsonSerializer.Deserialize<List<BillingLineItemDto>>(detail.LineItemsJson);
}
catch
{
// 解析失败时忽略
}
}
// 6. 映射为 DTO 并返回
return new BillingDetailDto
{
Id = detail.Id,
@@ -43,12 +80,13 @@ public sealed class GetBillingDetailQueryHandler(IBillingRepository billingRepos
Status = detail.Status,
DueDate = detail.DueDate,
LineItemsJson = detail.LineItemsJson,
LineItems = lineItems,
Notes = detail.Notes,
OverdueNotifiedAt = detail.OverdueNotifiedAt,
ReminderSentAt = detail.ReminderSentAt,
CreatedAt = detail.CreatedAt,
UpdatedAt = detail.UpdatedAt,
Payments = []
Payments = paymentDtos
};
}
}