核心功能:
- 账单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>
238 lines
8.3 KiB
C#
238 lines
8.3 KiB
C#
using System;
|
||
using Microsoft.EntityFrameworkCore.Migrations;
|
||
|
||
#nullable disable
|
||
|
||
namespace TakeoutSaaS.Infrastructure.Migrations
|
||
{
|
||
/// <inheritdoc />
|
||
public partial class UpdateTenantBillingSchema : Migration
|
||
{
|
||
/// <inheritdoc />
|
||
protected override void Up(MigrationBuilder migrationBuilder)
|
||
{
|
||
migrationBuilder.AddColumn<string>(
|
||
name: "RefundReason",
|
||
table: "tenant_payments",
|
||
type: "character varying(512)",
|
||
maxLength: 512,
|
||
nullable: true,
|
||
comment: "退款原因。");
|
||
|
||
migrationBuilder.AddColumn<DateTime>(
|
||
name: "RefundedAt",
|
||
table: "tenant_payments",
|
||
type: "timestamp with time zone",
|
||
nullable: true,
|
||
comment: "退款时间。");
|
||
|
||
migrationBuilder.AddColumn<DateTime>(
|
||
name: "VerifiedAt",
|
||
table: "tenant_payments",
|
||
type: "timestamp with time zone",
|
||
nullable: true,
|
||
comment: "审核时间。");
|
||
|
||
migrationBuilder.AddColumn<long>(
|
||
name: "VerifiedBy",
|
||
table: "tenant_payments",
|
||
type: "bigint",
|
||
nullable: true,
|
||
comment: "审核人 ID(管理员)。");
|
||
|
||
migrationBuilder.AlterColumn<decimal>(
|
||
name: "AmountDue",
|
||
table: "tenant_billing_statements",
|
||
type: "numeric(18,2)",
|
||
precision: 18,
|
||
scale: 2,
|
||
nullable: false,
|
||
comment: "应付金额(原始金额)。",
|
||
oldClrType: typeof(decimal),
|
||
oldType: "numeric(18,2)",
|
||
oldPrecision: 18,
|
||
oldScale: 2,
|
||
oldComment: "应付金额。");
|
||
|
||
migrationBuilder.AddColumn<int>(
|
||
name: "BillingType",
|
||
table: "tenant_billing_statements",
|
||
type: "integer",
|
||
nullable: false,
|
||
defaultValue: 0,
|
||
comment: "账单类型(订阅账单/配额包账单/手动账单/续费账单)。");
|
||
|
||
migrationBuilder.AddColumn<string>(
|
||
name: "Currency",
|
||
table: "tenant_billing_statements",
|
||
type: "character varying(8)",
|
||
maxLength: 8,
|
||
nullable: false,
|
||
defaultValue: "CNY",
|
||
comment: "货币类型(默认 CNY)。");
|
||
|
||
migrationBuilder.AddColumn<decimal>(
|
||
name: "DiscountAmount",
|
||
table: "tenant_billing_statements",
|
||
type: "numeric(18,2)",
|
||
precision: 18,
|
||
scale: 2,
|
||
nullable: false,
|
||
defaultValue: 0m,
|
||
comment: "折扣金额。");
|
||
|
||
migrationBuilder.AddColumn<string>(
|
||
name: "Notes",
|
||
table: "tenant_billing_statements",
|
||
type: "character varying(512)",
|
||
maxLength: 512,
|
||
nullable: true,
|
||
comment: "备注信息(如:人工备注、取消原因等)。");
|
||
|
||
migrationBuilder.AddColumn<DateTime>(
|
||
name: "OverdueNotifiedAt",
|
||
table: "tenant_billing_statements",
|
||
type: "timestamp with time zone",
|
||
nullable: true,
|
||
comment: "逾期通知时间。");
|
||
|
||
migrationBuilder.AddColumn<DateTime>(
|
||
name: "ReminderSentAt",
|
||
table: "tenant_billing_statements",
|
||
type: "timestamp with time zone",
|
||
nullable: true,
|
||
comment: "提醒发送时间(续费提醒、逾期提醒等)。");
|
||
|
||
migrationBuilder.AddColumn<long>(
|
||
name: "SubscriptionId",
|
||
table: "tenant_billing_statements",
|
||
type: "bigint",
|
||
nullable: true,
|
||
comment: "关联的订阅 ID(仅当 BillingType 为 Subscription 或 Renewal 时有值)。");
|
||
|
||
migrationBuilder.AddColumn<decimal>(
|
||
name: "TaxAmount",
|
||
table: "tenant_billing_statements",
|
||
type: "numeric(18,2)",
|
||
precision: 18,
|
||
scale: 2,
|
||
nullable: false,
|
||
defaultValue: 0m,
|
||
comment: "税费金额。");
|
||
|
||
migrationBuilder.CreateIndex(
|
||
name: "idx_payment_billing_paidat",
|
||
table: "tenant_payments",
|
||
columns: new[] { "BillingStatementId", "PaidAt" });
|
||
|
||
migrationBuilder.CreateIndex(
|
||
name: "idx_payment_transaction_no",
|
||
table: "tenant_payments",
|
||
column: "TransactionNo",
|
||
filter: "\"TransactionNo\" IS NOT NULL");
|
||
|
||
migrationBuilder.CreateIndex(
|
||
name: "idx_billing_created_at",
|
||
table: "tenant_billing_statements",
|
||
column: "CreatedAt");
|
||
|
||
migrationBuilder.CreateIndex(
|
||
name: "idx_billing_status_duedate",
|
||
table: "tenant_billing_statements",
|
||
columns: new[] { "Status", "DueDate" },
|
||
filter: "\"Status\" IN (0, 2)");
|
||
|
||
migrationBuilder.CreateIndex(
|
||
name: "idx_billing_tenant_status_duedate",
|
||
table: "tenant_billing_statements",
|
||
columns: new[] { "TenantId", "Status", "DueDate" });
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
protected override void Down(MigrationBuilder migrationBuilder)
|
||
{
|
||
migrationBuilder.DropIndex(
|
||
name: "idx_payment_billing_paidat",
|
||
table: "tenant_payments");
|
||
|
||
migrationBuilder.DropIndex(
|
||
name: "idx_payment_transaction_no",
|
||
table: "tenant_payments");
|
||
|
||
migrationBuilder.DropIndex(
|
||
name: "idx_billing_created_at",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropIndex(
|
||
name: "idx_billing_status_duedate",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropIndex(
|
||
name: "idx_billing_tenant_status_duedate",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "RefundReason",
|
||
table: "tenant_payments");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "RefundedAt",
|
||
table: "tenant_payments");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "VerifiedAt",
|
||
table: "tenant_payments");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "VerifiedBy",
|
||
table: "tenant_payments");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "BillingType",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "Currency",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "DiscountAmount",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "Notes",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "OverdueNotifiedAt",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "ReminderSentAt",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "SubscriptionId",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.DropColumn(
|
||
name: "TaxAmount",
|
||
table: "tenant_billing_statements");
|
||
|
||
migrationBuilder.AlterColumn<decimal>(
|
||
name: "AmountDue",
|
||
table: "tenant_billing_statements",
|
||
type: "numeric(18,2)",
|
||
precision: 18,
|
||
scale: 2,
|
||
nullable: false,
|
||
comment: "应付金额。",
|
||
oldClrType: typeof(decimal),
|
||
oldType: "numeric(18,2)",
|
||
oldPrecision: 18,
|
||
oldScale: 2,
|
||
oldComment: "应付金额(原始金额)。");
|
||
}
|
||
}
|
||
}
|