fix:修复注释错误
This commit is contained in:
@@ -26,25 +26,25 @@ public sealed class TenantBillingStatementConfiguration : IEntityTypeConfigurati
|
||||
builder.Property(x => x.Currency).HasMaxLength(8).HasDefaultValue("CNY");
|
||||
builder.Property(x => x.Status).HasConversion<int>();
|
||||
|
||||
// 2. (空行后) JSON 字段(当前以 text 存储 JSON 字符串,便于兼容历史迁移)
|
||||
// 2. JSON 字段(当前以 text 存储 JSON 字符串,便于兼容历史迁移)
|
||||
builder.Property(x => x.LineItemsJson).HasColumnType("text");
|
||||
|
||||
// 3. (空行后) 备注字段
|
||||
// 3. 备注字段
|
||||
builder.Property(x => x.Notes).HasMaxLength(512);
|
||||
|
||||
// 4. (空行后) 唯一约束与索引
|
||||
// 4. 唯一约束与索引
|
||||
builder.HasIndex(x => new { x.TenantId, x.StatementNo }).IsUnique();
|
||||
|
||||
// 5. (空行后) 性能索引(高频查询:租户+状态+到期日)
|
||||
// 5. 性能索引(高频查询:租户+状态+到期日)
|
||||
builder.HasIndex(x => new { x.TenantId, x.Status, x.DueDate })
|
||||
.HasDatabaseName("idx_billing_tenant_status_duedate");
|
||||
|
||||
// 6. (空行后) 逾期扫描索引(仅索引 Pending/Overdue)
|
||||
// 6. 逾期扫描索引(仅索引 Pending/Overdue)
|
||||
builder.HasIndex(x => new { x.Status, x.DueDate })
|
||||
.HasDatabaseName("idx_billing_status_duedate")
|
||||
.HasFilter($"\"Status\" IN ({(int)TenantBillingStatus.Pending}, {(int)TenantBillingStatus.Overdue})");
|
||||
|
||||
// 7. (空行后) 创建时间索引(支持列表倒序)
|
||||
// 7. 创建时间索引(支持列表倒序)
|
||||
builder.HasIndex(x => x.CreatedAt)
|
||||
.HasDatabaseName("idx_billing_created_at");
|
||||
}
|
||||
|
||||
@@ -25,14 +25,14 @@ public sealed class TenantPaymentConfiguration : IEntityTypeConfiguration<Tenant
|
||||
builder.Property(x => x.RefundReason).HasMaxLength(512);
|
||||
builder.Property(x => x.Notes).HasMaxLength(512);
|
||||
|
||||
// 2. (空行后) 复合索引:租户+账单
|
||||
// 2. 复合索引:租户+账单
|
||||
builder.HasIndex(x => new { x.TenantId, x.BillingStatementId });
|
||||
|
||||
// 3. (空行后) 支付记录时间排序索引
|
||||
// 3. 支付记录时间排序索引
|
||||
builder.HasIndex(x => new { x.BillingStatementId, x.PaidAt })
|
||||
.HasDatabaseName("idx_payment_billing_paidat");
|
||||
|
||||
// 4. (空行后) 交易号索引(部分索引:仅非空)
|
||||
// 4. 交易号索引(部分索引:仅非空)
|
||||
builder.HasIndex(x => x.TransactionNo)
|
||||
.HasDatabaseName("idx_payment_transaction_no")
|
||||
.HasFilter("\"TransactionNo\" IS NOT NULL");
|
||||
|
||||
@@ -25,13 +25,13 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null && x.TenantId == tenantId);
|
||||
|
||||
// 2. (空行后) 按状态过滤
|
||||
// 2. 按状态过滤
|
||||
if (status.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.Status == status.Value);
|
||||
}
|
||||
|
||||
// 3. (空行后) 按日期范围过滤(账单周期)
|
||||
// 3. 按日期范围过滤(账单周期)
|
||||
if (from.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.PeriodStart >= from.Value);
|
||||
@@ -42,7 +42,7 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
query = query.Where(x => x.PeriodEnd <= to.Value);
|
||||
}
|
||||
|
||||
// 4. (空行后) 排序返回
|
||||
// 4. 排序返回
|
||||
return await query
|
||||
.OrderByDescending(x => x.PeriodEnd)
|
||||
.ToListAsync(cancellationToken);
|
||||
@@ -102,7 +102,7 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
// 1. 以当前 UTC 时间作为逾期判断基准
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// 2. (空行后) 查询逾期且仍处于待支付的账单(仅 Pending 才允许自动切换为 Overdue)
|
||||
// 2. 查询逾期且仍处于待支付的账单(仅 Pending 才允许自动切换为 Overdue)
|
||||
return await context.TenantBillingStatements
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
@@ -120,7 +120,7 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
var now = DateTime.UtcNow;
|
||||
var dueTo = now.AddDays(daysAhead);
|
||||
|
||||
// 2. (空行后) 仅查询待支付账单
|
||||
// 2. 仅查询待支付账单
|
||||
return await context.TenantBillingStatements
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
@@ -198,19 +198,19 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null);
|
||||
|
||||
// 2. (空行后) 按租户过滤(可选)
|
||||
// 2. 按租户过滤(可选)
|
||||
if (tenantId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.TenantId == tenantId.Value);
|
||||
}
|
||||
|
||||
// 3. (空行后) 按状态过滤(可选)
|
||||
// 3. 按状态过滤(可选)
|
||||
if (status.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.Status == status.Value);
|
||||
}
|
||||
|
||||
// 4. (空行后) 按日期范围过滤(账单周期)
|
||||
// 4. 按日期范围过滤(账单周期)
|
||||
if (from.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.PeriodStart >= from.Value);
|
||||
@@ -221,7 +221,7 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
query = query.Where(x => x.PeriodEnd <= to.Value);
|
||||
}
|
||||
|
||||
// 5. (空行后) 按金额范围过滤(应付金额,包含边界)
|
||||
// 5. 按金额范围过滤(应付金额,包含边界)
|
||||
if (minAmount.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.AmountDue >= minAmount.Value);
|
||||
@@ -232,7 +232,7 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
query = query.Where(x => x.AmountDue <= maxAmount.Value);
|
||||
}
|
||||
|
||||
// 6. (空行后) 关键字过滤(账单号或租户名)
|
||||
// 6. 关键字过滤(账单号或租户名)
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
var normalized = keyword.Trim();
|
||||
@@ -249,10 +249,10 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
select b;
|
||||
}
|
||||
|
||||
// 7. (空行后) 统计总数
|
||||
// 7. 统计总数
|
||||
var total = await query.CountAsync(cancellationToken);
|
||||
|
||||
// 8. (空行后) 分页查询
|
||||
// 8. 分页查询
|
||||
var items = await query
|
||||
.OrderByDescending(x => x.PeriodEnd)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
@@ -279,7 +279,7 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
&& x.PeriodStart >= startDate
|
||||
&& x.PeriodEnd <= endDate);
|
||||
|
||||
// 2. (空行后) 聚合统计(金额统一使用:应付 - 折扣 + 税费)
|
||||
// 2. 聚合统计(金额统一使用:应付 - 折扣 + 税费)
|
||||
var now = DateTime.UtcNow;
|
||||
var totalAmount = await query.SumAsync(x => x.AmountDue - x.DiscountAmount + x.TaxAmount, cancellationToken);
|
||||
var paidAmount = await query.Where(x => x.Status == TenantBillingStatus.Paid).SumAsync(x => x.AmountPaid, cancellationToken);
|
||||
@@ -288,13 +288,13 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
.Where(x => (x.Status == TenantBillingStatus.Pending || x.Status == TenantBillingStatus.Overdue) && x.DueDate < now)
|
||||
.SumAsync(x => (x.AmountDue - x.DiscountAmount + x.TaxAmount) - x.AmountPaid, cancellationToken);
|
||||
|
||||
// 3. (空行后) 数量统计
|
||||
// 3. 数量统计
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
var paidCount = await query.CountAsync(x => x.Status == TenantBillingStatus.Paid, cancellationToken);
|
||||
var unpaidCount = await query.CountAsync(x => x.Status == TenantBillingStatus.Pending || x.Status == TenantBillingStatus.Overdue, cancellationToken);
|
||||
var overdueCount = await query.CountAsync(x => (x.Status == TenantBillingStatus.Pending || x.Status == TenantBillingStatus.Overdue) && x.DueDate < now, cancellationToken);
|
||||
|
||||
// 4. (空行后) 趋势统计
|
||||
// 4. 趋势统计
|
||||
var normalizedGroupBy = NormalizeGroupBy(groupBy);
|
||||
var trendRaw = await query
|
||||
.Select(x => new
|
||||
@@ -307,7 +307,7 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// 4.1 (空行后) 在内存中按 Day/Week/Month 聚合(避免依赖特定数据库函数扩展)
|
||||
// 4.1 在内存中按 Day/Week/Month 聚合(避免依赖特定数据库函数扩展)
|
||||
var trend = trendRaw
|
||||
.GroupBy(x => GetTrendBucket(x.PeriodStart, normalizedGroupBy))
|
||||
.Select(g => new TenantBillingTrendDataPoint
|
||||
@@ -371,7 +371,7 @@ public sealed class TenantBillingRepository(TakeoutAppDbContext context) : ITena
|
||||
var dayOfWeek = (int)date.DayOfWeek; // Sunday=0, Monday=1, ...
|
||||
var daysSinceMonday = (dayOfWeek + 6) % 7;
|
||||
|
||||
// 2. (空行后) 回退到周一 00:00:00(UTC)
|
||||
// 2. 回退到周一 00:00:00(UTC)
|
||||
var monday = date.AddDays(-daysSinceMonday);
|
||||
return new DateTime(monday.Year, monday.Month, monday.Day, 0, 0, 0, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
@@ -698,8 +698,8 @@ public sealed class TakeoutAppDbContext(
|
||||
.HasComment("发布状态:0=草稿,1=已发布。");
|
||||
|
||||
// 1. 解决 EF Core 默认值哨兵问题:当我们希望插入 false/0 时,若数据库配置了 default 且 EF 认为该值是“未设置”,会导致 insert 省略列,最终落库为默认值。
|
||||
// 2. (空行后) 发布状态使用 -1 作为哨兵,避免 Draft=0 被误判为“未设置”而触发数据库默认值(发布/草稿切换必须可控)。
|
||||
// 3. (空行后) 将布尔开关哨兵值设置为数据库默认值:true 作为哨兵,false 才会被显式写入,从而保证“可见性/可售开关”在新增时可正确落库。
|
||||
// 2. 发布状态使用 -1 作为哨兵,避免 Draft=0 被误判为“未设置”而触发数据库默认值(发布/草稿切换必须可控)。
|
||||
// 3. 将布尔开关哨兵值设置为数据库默认值:true 作为哨兵,false 才会被显式写入,从而保证“可见性/可售开关”在新增时可正确落库。
|
||||
builder.Property(x => x.IsPublicVisible)
|
||||
.HasDefaultValue(true)
|
||||
.HasSentinel(true)
|
||||
@@ -709,7 +709,7 @@ public sealed class TakeoutAppDbContext(
|
||||
.HasSentinel(true)
|
||||
.HasComment("是否允许新租户购买/选择(仅影响新购)。");
|
||||
|
||||
// 4. (空行后) 展示配置:推荐标识与标签(用于套餐展示页/对比页)
|
||||
// 4. 展示配置:推荐标识与标签(用于套餐展示页/对比页)
|
||||
builder.Property(x => x.IsRecommended)
|
||||
.HasDefaultValue(false)
|
||||
.HasComment("是否推荐展示(运营推荐标识)。");
|
||||
|
||||
@@ -178,7 +178,7 @@ public sealed class EfMerchantRepository(TakeoutAppDbContext context, TakeoutLog
|
||||
// 1. 保存业务库变更
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 保存日志库变更
|
||||
// 2. 保存日志库变更
|
||||
await logsContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -406,7 +406,7 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext, Take
|
||||
// 1. 保存业务库变更
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 保存日志库变更
|
||||
// 2. 保存日志库变更
|
||||
await logsContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,21 +105,21 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context, TakeoutLogsD
|
||||
query = query.Where(x => EF.Functions.ILike(x.Name, $"%{normalizedName}%"));
|
||||
}
|
||||
|
||||
// 4. (空行后) 按联系人过滤(模糊匹配)
|
||||
// 4. 按联系人过滤(模糊匹配)
|
||||
if (!string.IsNullOrWhiteSpace(contactName))
|
||||
{
|
||||
var normalizedContactName = contactName.Trim();
|
||||
query = query.Where(x => EF.Functions.ILike(x.ContactName ?? string.Empty, $"%{normalizedContactName}%"));
|
||||
}
|
||||
|
||||
// 5. (空行后) 按联系电话过滤(模糊匹配)
|
||||
// 5. 按联系电话过滤(模糊匹配)
|
||||
if (!string.IsNullOrWhiteSpace(contactPhone))
|
||||
{
|
||||
var normalizedContactPhone = contactPhone.Trim();
|
||||
query = query.Where(x => EF.Functions.ILike(x.ContactPhone ?? string.Empty, $"%{normalizedContactPhone}%"));
|
||||
}
|
||||
|
||||
// 6. (空行后) 兼容关键字查询:名称/编码/联系人/电话
|
||||
// 6. 兼容关键字查询:名称/编码/联系人/电话
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
var normalizedKeyword = keyword.Trim();
|
||||
@@ -130,10 +130,10 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context, TakeoutLogsD
|
||||
EF.Functions.ILike(x.ContactPhone ?? string.Empty, $"%{normalizedKeyword}%"));
|
||||
}
|
||||
|
||||
// 7. (空行后) 先统计总数,再按创建时间倒序分页
|
||||
// 7. 先统计总数,再按创建时间倒序分页
|
||||
var total = await query.CountAsync(cancellationToken);
|
||||
|
||||
// 8. (空行后) 查询当前页数据
|
||||
// 8. 查询当前页数据
|
||||
var items = await query
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.Skip((page - 1) * pageSize)
|
||||
@@ -169,18 +169,18 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context, TakeoutLogsD
|
||||
// 1. 标准化名称
|
||||
var normalized = name.Trim();
|
||||
|
||||
// 2. (空行后) 构建查询(名称使用 ILike 做不区分大小写的等值匹配)
|
||||
// 2. 构建查询(名称使用 ILike 做不区分大小写的等值匹配)
|
||||
var query = context.Tenants
|
||||
.AsNoTracking()
|
||||
.Where(x => EF.Functions.ILike(x.Name, normalized));
|
||||
|
||||
// 3. (空行后) 更新场景排除自身
|
||||
// 3. 更新场景排除自身
|
||||
if (excludeTenantId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.Id != excludeTenantId.Value);
|
||||
}
|
||||
|
||||
// 4. (空行后) 判断是否存在
|
||||
// 4. 判断是否存在
|
||||
return query.AnyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context, TakeoutLogsD
|
||||
await context.TenantReviewClaims.AddAsync(claim, cancellationToken);
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 写入审计日志
|
||||
// 2. 写入审计日志
|
||||
await logsContext.TenantAuditLogs.AddAsync(auditLog, cancellationToken);
|
||||
await logsContext.SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
@@ -292,7 +292,7 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context, TakeoutLogsD
|
||||
// 1. 释放实体跟踪避免重复写入
|
||||
context.Entry(claim).State = EntityState.Detached;
|
||||
|
||||
// 2. (空行后) 返回抢占失败
|
||||
// 2. 返回抢占失败
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -398,7 +398,7 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context, TakeoutLogsD
|
||||
// 1. 保存业务库变更
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 保存日志库变更
|
||||
// 2. 保存日志库变更
|
||||
await logsContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,14 +33,14 @@ public sealed class BillingDomainService(
|
||||
throw new InvalidOperationException("该订阅周期的账单已存在。");
|
||||
}
|
||||
|
||||
// 2. (空行后) 查询套餐价格信息
|
||||
// 2. 查询套餐价格信息
|
||||
var package = await tenantPackageRepository.FindByIdAsync(subscription.TenantPackageId, cancellationToken);
|
||||
if (package is null)
|
||||
{
|
||||
throw new InvalidOperationException("订阅未关联有效套餐,无法生成账单。");
|
||||
}
|
||||
|
||||
// 3. (空行后) 选择价格(简化规则:优先按年/按月)
|
||||
// 3. 选择价格(简化规则:优先按年/按月)
|
||||
var days = (subscription.EffectiveTo - subscription.EffectiveFrom).TotalDays;
|
||||
var amountDue = days >= 300 ? package.YearlyPrice : package.MonthlyPrice;
|
||||
if (!amountDue.HasValue)
|
||||
@@ -48,7 +48,7 @@ public sealed class BillingDomainService(
|
||||
throw new InvalidOperationException("套餐价格未配置,无法生成账单。");
|
||||
}
|
||||
|
||||
// 4. (空行后) 生成账单明细
|
||||
// 4. 生成账单明细
|
||||
var lineItems = new List<BillingLineItem>
|
||||
{
|
||||
BillingLineItem.Create(
|
||||
@@ -58,7 +58,7 @@ public sealed class BillingDomainService(
|
||||
unitPrice: amountDue.Value)
|
||||
};
|
||||
|
||||
// 5. (空行后) 构建账单实体
|
||||
// 5. 构建账单实体
|
||||
var now = DateTime.UtcNow;
|
||||
return new TenantBillingStatement
|
||||
{
|
||||
@@ -98,7 +98,7 @@ public sealed class BillingDomainService(
|
||||
// 1. 计算金额
|
||||
var amountDue = quotaPackage.Price * quantity;
|
||||
|
||||
// 2. (空行后) 生成账单明细
|
||||
// 2. 生成账单明细
|
||||
var lineItems = new List<BillingLineItem>
|
||||
{
|
||||
BillingLineItem.Create(
|
||||
@@ -108,7 +108,7 @@ public sealed class BillingDomainService(
|
||||
unitPrice: quotaPackage.Price)
|
||||
};
|
||||
|
||||
// 3. (空行后) 构建账单实体
|
||||
// 3. 构建账单实体
|
||||
var now = DateTime.UtcNow;
|
||||
var billing = new TenantBillingStatement
|
||||
{
|
||||
@@ -139,7 +139,7 @@ public sealed class BillingDomainService(
|
||||
// 1. 账单号格式:BILL-{yyyyMMdd}-{序号}
|
||||
var date = DateTime.UtcNow.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
|
||||
|
||||
// 2. (空行后) 使用雪花 ID 作为全局递增序号,确保分布式唯一
|
||||
// 2. 使用雪花 ID 作为全局递增序号,确保分布式唯一
|
||||
var sequence = idGenerator.NextId();
|
||||
return $"BILL-{date}-{sequence}";
|
||||
}
|
||||
@@ -154,7 +154,7 @@ public sealed class BillingDomainService(
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2. (空行后) 批量标记逾期(防御性:再次判断 Pending)
|
||||
// 2. 批量标记逾期(防御性:再次判断 Pending)
|
||||
var processedAt = DateTime.UtcNow;
|
||||
var updated = 0;
|
||||
foreach (var billing in overdueBillings)
|
||||
@@ -172,7 +172,7 @@ public sealed class BillingDomainService(
|
||||
updated++;
|
||||
}
|
||||
|
||||
// 3. (空行后) 持久化
|
||||
// 3. 持久化
|
||||
if (updated > 0)
|
||||
{
|
||||
await billingRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
@@ -33,7 +33,7 @@ public sealed class BillingExportService : IBillingExportService
|
||||
using var workbook = new XLWorkbook();
|
||||
var worksheet = workbook.Worksheets.Add("Billings");
|
||||
|
||||
// 2. (空行后) 写入表头
|
||||
// 2. 写入表头
|
||||
var headers = new[]
|
||||
{
|
||||
"Id", "TenantId", "StatementNo", "BillingType", "Status",
|
||||
@@ -46,7 +46,7 @@ public sealed class BillingExportService : IBillingExportService
|
||||
worksheet.Cell(1, i + 1).Value = headers[i];
|
||||
}
|
||||
|
||||
// 3. (空行后) 写入数据行
|
||||
// 3. 写入数据行
|
||||
for (var rowIndex = 0; rowIndex < billings.Count; rowIndex++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -73,7 +73,7 @@ public sealed class BillingExportService : IBillingExportService
|
||||
worksheet.Cell(r, 16).Value = billing.LineItemsJson ?? string.Empty;
|
||||
}
|
||||
|
||||
// 4. (空行后) 自动调整列宽并输出
|
||||
// 4. 自动调整列宽并输出
|
||||
worksheet.Columns().AdjustToContents();
|
||||
|
||||
using var stream = new MemoryStream();
|
||||
@@ -102,7 +102,7 @@ public sealed class BillingExportService : IBillingExportService
|
||||
// 2. 标题
|
||||
column.Item().Text("Billings Export").FontSize(16).SemiBold();
|
||||
|
||||
// 3. (空行后) 逐条输出
|
||||
// 3. 逐条输出
|
||||
for (var i = 0; i < billings.Count; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -129,7 +129,7 @@ public sealed class BillingExportService : IBillingExportService
|
||||
});
|
||||
});
|
||||
|
||||
// 4. (空行后) 输出字节
|
||||
// 4. 输出字节
|
||||
var bytes = document.GeneratePdf();
|
||||
return Task.FromResult(bytes);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public sealed class BillingExportService : IBillingExportService
|
||||
csv.WriteField("LineItemsJson");
|
||||
await csv.NextRecordAsync();
|
||||
|
||||
// 3. (空行后) 写入数据行
|
||||
// 3. 写入数据行
|
||||
foreach (var b in billings)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -196,7 +196,7 @@ public sealed class BillingExportService : IBillingExportService
|
||||
await csv.NextRecordAsync();
|
||||
}
|
||||
|
||||
// 4. (空行后) Flush 并返回字节
|
||||
// 4. Flush 并返回字节
|
||||
await writer.FlushAsync(cancellationToken);
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ public sealed class RedisAdminPasswordResetTokenStore(
|
||||
// 1. 生成 URL 安全的随机令牌
|
||||
var token = GenerateUrlSafeToken(48);
|
||||
|
||||
// 2. (空行后) 写入缓存(Value:userId)
|
||||
// 2. 写入缓存(Value:userId)
|
||||
await cache.SetStringAsync(BuildKey(token), userId.ToString(), new DistributedCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpiration = expiresAt
|
||||
}, cancellationToken);
|
||||
|
||||
// 3. (空行后) 返回令牌
|
||||
// 3. 返回令牌
|
||||
return token;
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@ public sealed class RedisAdminPasswordResetTokenStore(
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. (空行后) 删除缓存(一次性令牌)
|
||||
// 2. 删除缓存(一次性令牌)
|
||||
await cache.RemoveAsync(key, cancellationToken);
|
||||
|
||||
// 3. (空行后) 解析用户 ID
|
||||
// 3. 解析用户 ID
|
||||
return long.TryParse(value, out var userId) ? userId : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public sealed class IdentityUserOperationLogConsumer(TakeoutLogsDbContext logsCo
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. (空行后) 构建日志实体与去重记录
|
||||
// 2. 构建日志实体与去重记录
|
||||
var message = context.Message;
|
||||
var log = new OperationLog
|
||||
{
|
||||
@@ -50,7 +50,7 @@ public sealed class IdentityUserOperationLogConsumer(TakeoutLogsDbContext logsCo
|
||||
});
|
||||
logsContext.OperationLogs.Add(log);
|
||||
|
||||
// 3. (空行后) 保存并处理并发去重冲突
|
||||
// 3. 保存并处理并发去重冲突
|
||||
try
|
||||
{
|
||||
await logsContext.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
@@ -27,7 +27,7 @@ public static class OperationLogOutboxServiceCollectionExtensions
|
||||
throw new InvalidOperationException("缺少 RabbitMQ 配置。");
|
||||
}
|
||||
|
||||
// 2. (空行后) 注册 MassTransit 与 Outbox
|
||||
// 2. 注册 MassTransit 与 Outbox
|
||||
services.AddMassTransit(configurator =>
|
||||
{
|
||||
configurator.AddConsumer<IdentityUserOperationLogConsumer>();
|
||||
@@ -50,7 +50,7 @@ public static class OperationLogOutboxServiceCollectionExtensions
|
||||
cfg.ConfigureEndpoints(context);
|
||||
});
|
||||
});
|
||||
// 3. (空行后) 返回服务集合
|
||||
// 3. 返回服务集合
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user