✨ feat: 完成账单管理模块后端功能开发及API优化
核心功能:
- 账单CRUD操作(创建、查询、详情、更新状态、删除)
- 支付记录管理(创建支付、审核支付)
- 批量操作支持(批量更新账单状态)
- 统计分析功能(账单统计、逾期账单查询)
- 导出功能(Excel/PDF/CSV)
API端点 (16个):
- GET /api/admin/v1/billings - 账单列表(分页、筛选、排序)
- POST /api/admin/v1/billings - 创建账单
- GET /api/admin/v1/billings/{id} - 账单详情
- DELETE /api/admin/v1/billings/{id} - 删除账单
- PUT /api/admin/v1/billings/{id}/status - 更新状态
- POST /api/admin/v1/billings/batch/status - 批量更新
- GET /api/admin/v1/billings/{id}/payments - 支付记录
- POST /api/admin/v1/billings/{id}/payments - 创建支付
- PUT /api/admin/v1/billings/payments/{paymentId}/verify - 审核支付
- GET /api/admin/v1/billings/statistics - 统计数据
- GET /api/admin/v1/billings/overdue - 逾期账单
- POST /api/admin/v1/billings/export - 导出账单
架构优化:
- 采用CQRS模式分离读写(MediatR + Dapper + EF Core)
- 完整的领域模型设计(TenantBillingStatement, TenantPayment等)
- FluentValidation请求验证
- 状态机管理账单和支付状态流转
API设计优化 (三项改进):
1. 导出API响应Content-Type改为application/octet-stream
2. 支付审核API添加Approved和Notes可选参数,支持通过/拒绝
3. 移除TenantBillings API中重复的TenantId参数
数据库变更:
- 新增账单相关表及关系
- 支持Snowflake ID主键
- 完整的审计字段支持
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6221,13 +6221,17 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
b.Property<decimal>("AmountDue")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasComment("应付金额。");
|
||||
.HasComment("应付金额(原始金额)。");
|
||||
|
||||
b.Property<decimal>("AmountPaid")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasComment("实付金额。");
|
||||
|
||||
b.Property<int>("BillingType")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("账单类型(订阅账单/配额包账单/手动账单/续费账单)。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
@@ -6236,6 +6240,14 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<string>("Currency")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(8)
|
||||
.HasColumnType("character varying(8)")
|
||||
.HasDefaultValue("CNY")
|
||||
.HasComment("货币类型(默认 CNY)。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
@@ -6244,6 +6256,11 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<decimal>("DiscountAmount")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasComment("折扣金额。");
|
||||
|
||||
b.Property<DateTime>("DueDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("到期日。");
|
||||
@@ -6252,6 +6269,15 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("text")
|
||||
.HasComment("账单明细 JSON,记录各项费用。");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)")
|
||||
.HasComment("备注信息(如:人工备注、取消原因等)。");
|
||||
|
||||
b.Property<DateTime?>("OverdueNotifiedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("逾期通知时间。");
|
||||
|
||||
b.Property<DateTime>("PeriodEnd")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("账单周期结束时间。");
|
||||
@@ -6260,6 +6286,10 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("账单周期开始时间。");
|
||||
|
||||
b.Property<DateTime?>("ReminderSentAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("提醒发送时间(续费提醒、逾期提醒等)。");
|
||||
|
||||
b.Property<string>("StatementNo")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
@@ -6270,6 +6300,15 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("integer")
|
||||
.HasComment("当前付款状态。");
|
||||
|
||||
b.Property<long?>("SubscriptionId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("关联的订阅 ID(仅当 BillingType 为 Subscription 或 Renewal 时有值)。");
|
||||
|
||||
b.Property<decimal>("TaxAmount")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasComment("税费金额。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
@@ -6284,9 +6323,19 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt")
|
||||
.HasDatabaseName("idx_billing_created_at");
|
||||
|
||||
b.HasIndex("Status", "DueDate")
|
||||
.HasDatabaseName("idx_billing_status_duedate")
|
||||
.HasFilter("\"Status\" IN (0, 2)");
|
||||
|
||||
b.HasIndex("TenantId", "StatementNo")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("TenantId", "Status", "DueDate")
|
||||
.HasDatabaseName("idx_billing_tenant_status_duedate");
|
||||
|
||||
b.ToTable("tenant_billing_statements", null, t =>
|
||||
{
|
||||
t.HasComment("租户账单,用于呈现周期性收费。");
|
||||
@@ -6555,6 +6604,15 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("character varying(512)")
|
||||
.HasComment("支付凭证 URL。");
|
||||
|
||||
b.Property<string>("RefundReason")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)")
|
||||
.HasComment("退款原因。");
|
||||
|
||||
b.Property<DateTime?>("RefundedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("退款时间。");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("支付状态。");
|
||||
@@ -6576,8 +6634,23 @@ namespace TakeoutSaaS.Infrastructure.Migrations
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("VerifiedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("审核时间。");
|
||||
|
||||
b.Property<long?>("VerifiedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("审核人 ID(管理员)。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TransactionNo")
|
||||
.HasDatabaseName("idx_payment_transaction_no")
|
||||
.HasFilter("\"TransactionNo\" IS NOT NULL");
|
||||
|
||||
b.HasIndex("BillingStatementId", "PaidAt")
|
||||
.HasDatabaseName("idx_payment_billing_paidat");
|
||||
|
||||
b.HasIndex("TenantId", "BillingStatementId");
|
||||
|
||||
b.ToTable("tenant_payments", null, t =>
|
||||
|
||||
Reference in New Issue
Block a user