using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Infrastructure.Migrations.AppDb
{
///
public partial class AddStoreSchema : Migration
{
///
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn(
name: "DonationAmount",
table: "WithdrawalRequest",
type: "int",
nullable: false,
defaultValue: 0,
comment: "후원 수익(Donation) 잔액에서 차감되는 금액");
migrationBuilder.AddColumn(
name: "StoreRevenueAmount",
table: "WithdrawalRequest",
type: "int",
nullable: false,
defaultValue: 0,
comment: "상품 판매 수익(StoreRevenue) 잔액에서 차감되는 금액");
migrationBuilder.CreateTable(
name: "Game",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false, comment: "게임 이름"),
Publisher = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false, comment: "게임사/퍼블리셔"),
Thumbnail = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true),
Description = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
CommissionRate = table.Column(type: "decimal(5,2)", precision: 5, scale: 2, nullable: false, comment: "게임사 수익률(%)"),
ChannelCommissionRate = table.Column(type: "decimal(5,2)", precision: 5, scale: 2, nullable: false, comment: "후원 채널 보상률(%)"),
IsActive = table.Column(type: "bit", nullable: false),
Order = table.Column(type: "int", nullable: false),
CreatedAt = table.Column(type: "datetime2", nullable: false),
UpdatedAt = table.Column(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Game", x => x.ID);
},
comment: "상점 제휴 게임");
migrationBuilder.CreateTable(
name: "MemberAddress",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
MemberID = table.Column(type: "int", nullable: false),
IsDefault = table.Column(type: "bit", nullable: false),
RecipientName_Encrypted = table.Column(type: "nvarchar(max)", nullable: false, comment: "수령인명 (암호화)"),
RecipientName_Hash = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false, comment: "수령인명 SHA-256 hex (검색용)"),
Phone_Encrypted = table.Column(type: "nvarchar(max)", nullable: false, comment: "연락처 (암호화)"),
ZipCode = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: false),
Address1_Encrypted = table.Column(type: "nvarchar(max)", nullable: false, comment: "기본 주소 (암호화)"),
Address2_Encrypted = table.Column(type: "nvarchar(max)", nullable: false, comment: "상세 주소 (암호화)"),
KeyVersion = table.Column(type: "int", nullable: false, comment: "암호화 키 버전"),
CreatedAt = table.Column(type: "datetime2", nullable: false),
UpdatedAt = table.Column(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_MemberAddress", x => x.ID);
table.ForeignKey(
name: "FK_MemberAddress_Member_MemberID",
column: x => x.MemberID,
principalTable: "Member",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
},
comment: "회원 배송 주소 (AES-GCM 암호화)");
migrationBuilder.CreateTable(
name: "Order",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
OrderNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: false, comment: "주문 번호 (UNIQUE)"),
MemberID = table.Column(type: "int", nullable: false),
ChannelID = table.Column(type: "int", nullable: true, comment: "후원 채널 (선택)"),
TotalAmount = table.Column(type: "int", nullable: false, comment: "총 결제 금액"),
PlatformFeeAmount = table.Column(type: "int", nullable: false, comment: "dpot 몫"),
GameRevenueAmount = table.Column(type: "int", nullable: false, comment: "게임사 정산 큐 적재 금액"),
ChannelRewardAmount = table.Column(type: "int", nullable: false, comment: "채널주 StoreRevenue 입금 금액"),
Status = table.Column(type: "int", nullable: false, comment: "주문 상태"),
CancelReason = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true),
RefundReason = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true),
CreatedAt = table.Column(type: "datetime2", nullable: false),
PaidAt = table.Column(type: "datetime2", nullable: true),
RowVersion = table.Column(type: "rowversion", rowVersion: true, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Order", x => x.ID);
table.ForeignKey(
name: "FK_Order_Channel_ChannelID",
column: x => x.ChannelID,
principalTable: "Channel",
principalColumn: "ID");
table.ForeignKey(
name: "FK_Order_Member_MemberID",
column: x => x.MemberID,
principalTable: "Member",
principalColumn: "ID",
onDelete: ReferentialAction.Restrict);
},
comment: "상점 주문 헤더");
migrationBuilder.CreateTable(
name: "GameSettlement",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
GameID = table.Column(type: "int", nullable: false),
Year = table.Column(type: "int", nullable: false),
Month = table.Column(type: "int", nullable: false),
TotalRevenueAmount = table.Column(type: "int", nullable: false),
OrderCount = table.Column(type: "int", nullable: false),
Status = table.Column(type: "int", nullable: false, comment: "정산 상태 (1=Pending, 2=Settled)"),
CreatedAt = table.Column(type: "datetime2", nullable: false),
SettledAt = table.Column(type: "datetime2", nullable: true),
SettledByAdminID = table.Column(type: "int", nullable: true),
AdminMemo = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GameSettlement", x => x.ID);
table.ForeignKey(
name: "FK_GameSettlement_Game_GameID",
column: x => x.GameID,
principalTable: "Game",
principalColumn: "ID",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_GameSettlement_Member_SettledByAdminID",
column: x => x.SettledByAdminID,
principalTable: "Member",
principalColumn: "ID");
},
comment: "게임사 월별 정산 큐");
migrationBuilder.CreateTable(
name: "Product",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
GameID = table.Column(type: "int", nullable: false, comment: "게임 ID (필수)"),
Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false, comment: "상품명"),
Description = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
Thumbnail = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true),
Type = table.Column(type: "int", nullable: false, comment: "상품 유형 (1=Physical, 2=Digital)"),
Price = table.Column(type: "int", nullable: false, comment: "판매가 (KRW)"),
Stock = table.Column(type: "int", nullable: false, comment: "재고 (-1=무제한)"),
IsActive = table.Column(type: "bit", nullable: false),
Order = table.Column(type: "int", nullable: false),
SaleStartAt = table.Column(type: "datetime2", nullable: true),
SaleEndAt = table.Column(type: "datetime2", nullable: true),
CreatedAt = table.Column(type: "datetime2", nullable: false),
UpdatedAt = table.Column(type: "datetime2", nullable: true),
RowVersion = table.Column(type: "rowversion", rowVersion: true, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Product", x => x.ID);
table.ForeignKey(
name: "FK_Product_Game_GameID",
column: x => x.GameID,
principalTable: "Game",
principalColumn: "ID",
onDelete: ReferentialAction.Restrict);
},
comment: "상점 상품");
migrationBuilder.CreateTable(
name: "OrderRefund",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
OrderID = table.Column(type: "int", nullable: false),
Type = table.Column(type: "int", nullable: false, comment: "유형 (1=Cancel, 2=Return, 3=Exchange, 4=Refund)"),
Status = table.Column(type: "int", nullable: false, comment: "상태 (1=Requested, 2=Approved, 3=Rejected, 4=Completed)"),
Amount = table.Column(type: "int", nullable: false),
Reason = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false),
AdminMemo = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true),
AdminID = table.Column(type: "int", nullable: true),
RequestedAt = table.Column(type: "datetime2", nullable: false),
ResolvedAt = table.Column(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OrderRefund", x => x.ID);
table.ForeignKey(
name: "FK_OrderRefund_Member_AdminID",
column: x => x.AdminID,
principalTable: "Member",
principalColumn: "ID");
table.ForeignKey(
name: "FK_OrderRefund_Order_OrderID",
column: x => x.OrderID,
principalTable: "Order",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
},
comment: "통합 환불 이력 (취소/반품/교환/환불)");
migrationBuilder.CreateTable(
name: "Shipment",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
OrderID = table.Column(type: "int", nullable: false),
RecipientName_Encrypted = table.Column(type: "nvarchar(max)", nullable: false),
Phone_Encrypted = table.Column(type: "nvarchar(max)", nullable: false),
ZipCode = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: false),
Address1_Encrypted = table.Column(type: "nvarchar(max)", nullable: false),
Address2_Encrypted = table.Column(type: "nvarchar(max)", nullable: false),
KeyVersion = table.Column(type: "int", nullable: false),
Status = table.Column(type: "int", nullable: false),
Carrier = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true),
TrackingNumber = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true),
CreatedAt = table.Column(type: "datetime2", nullable: false),
ShippedAt = table.Column(type: "datetime2", nullable: true),
DeliveredAt = table.Column(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Shipment", x => x.ID);
table.ForeignKey(
name: "FK_Shipment_Order_OrderID",
column: x => x.OrderID,
principalTable: "Order",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
},
comment: "배송 (주문 시점 주소 스냅샷, 암호화)");
migrationBuilder.CreateTable(
name: "Coupon",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ProductID = table.Column(type: "int", nullable: false),
GameID = table.Column(type: "int", nullable: false, comment: "게임 ID (필수)"),
Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false),
ExpiresAt = table.Column(type: "datetime2", nullable: true),
UsagePolicy = table.Column(type: "int", nullable: false),
GeneratedCount = table.Column(type: "int", nullable: false),
UsedCount = table.Column(type: "int", nullable: false),
LowStockThreshold = table.Column(type: "int", nullable: false, defaultValue: 10),
CreatedAt = table.Column(type: "datetime2", nullable: false),
UpdatedAt = table.Column(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Coupon", x => x.ID);
table.ForeignKey(
name: "FK_Coupon_Game_GameID",
column: x => x.GameID,
principalTable: "Game",
principalColumn: "ID",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Coupon_Product_ProductID",
column: x => x.ProductID,
principalTable: "Product",
principalColumn: "ID",
onDelete: ReferentialAction.Restrict);
},
comment: "쿠폰 메타 (코드 풀의 상위)");
migrationBuilder.CreateTable(
name: "CouponCode",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
CouponID = table.Column(type: "int", nullable: false),
Code = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false, comment: "코드 평문 (사용자 표시용)"),
CodeHash = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false, comment: "SHA-256 hex (UNIQUE, 조회용)"),
Status = table.Column(type: "int", nullable: false, comment: "상태 (1=Available, 2=Reserved, 3=Issued, 4=Used)"),
IssuedToMemberID = table.Column(type: "int", nullable: true),
IssuedAt = table.Column(type: "datetime2", nullable: true),
UsedAt = table.Column(type: "datetime2", nullable: true),
CreatedAt = table.Column(type: "datetime2", nullable: false),
RowVersion = table.Column(type: "rowversion", rowVersion: true, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CouponCode", x => x.ID);
table.ForeignKey(
name: "FK_CouponCode_Coupon_CouponID",
column: x => x.CouponID,
principalTable: "Coupon",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_CouponCode_Member_IssuedToMemberID",
column: x => x.IssuedToMemberID,
principalTable: "Member",
principalColumn: "ID");
},
comment: "쿠폰 코드 풀 (Code 평문 + CodeHash SHA-256 hex)");
migrationBuilder.CreateTable(
name: "OrderItem",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
OrderID = table.Column(type: "int", nullable: false),
ProductID = table.Column(type: "int", nullable: false),
Quantity = table.Column(type: "int", nullable: false),
UnitPrice = table.Column(type: "int", nullable: false, comment: "주문 시점 상품 단가 스냅샷"),
IssuedCouponCodeID = table.Column(type: "int", nullable: true, comment: "Digital 상품 발급 쿠폰 코드"),
CreatedAt = table.Column(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OrderItem", x => x.ID);
table.ForeignKey(
name: "FK_OrderItem_CouponCode_IssuedCouponCodeID",
column: x => x.IssuedCouponCodeID,
principalTable: "CouponCode",
principalColumn: "ID");
table.ForeignKey(
name: "FK_OrderItem_Order_OrderID",
column: x => x.OrderID,
principalTable: "Order",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_OrderItem_Product_ProductID",
column: x => x.ProductID,
principalTable: "Product",
principalColumn: "ID",
onDelete: ReferentialAction.Restrict);
},
comment: "주문 라인");
migrationBuilder.CreateTable(
name: "MemberInventory",
columns: table => new
{
ID = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
MemberID = table.Column(type: "int", nullable: false),
OrderItemID = table.Column(type: "int", nullable: false),
CouponCodeID = table.Column(type: "int", nullable: false),
AcquiredAt = table.Column(type: "datetime2", nullable: false),
UsedAt = table.Column(type: "datetime2", nullable: true),
ExpiresAt = table.Column(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_MemberInventory", x => x.ID);
table.ForeignKey(
name: "FK_MemberInventory_CouponCode_CouponCodeID",
column: x => x.CouponCodeID,
principalTable: "CouponCode",
principalColumn: "ID",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_MemberInventory_Member_MemberID",
column: x => x.MemberID,
principalTable: "Member",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_MemberInventory_OrderItem_OrderItemID",
column: x => x.OrderItemID,
principalTable: "OrderItem",
principalColumn: "ID",
onDelete: ReferentialAction.Restrict);
},
comment: "회원 보관함 (Digital 상품)");
migrationBuilder.CreateIndex(
name: "IX_Coupon_GameID",
table: "Coupon",
column: "GameID");
migrationBuilder.CreateIndex(
name: "IX_Coupon_ProductID",
table: "Coupon",
column: "ProductID");
migrationBuilder.CreateIndex(
name: "IX_CouponCode_CodeHash",
table: "CouponCode",
column: "CodeHash",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_CouponCode_CouponID_Status",
table: "CouponCode",
columns: new[] { "CouponID", "Status" });
migrationBuilder.CreateIndex(
name: "IX_CouponCode_IssuedToMemberID",
table: "CouponCode",
column: "IssuedToMemberID");
migrationBuilder.CreateIndex(
name: "IX_Game_IsActive_Order",
table: "Game",
columns: new[] { "IsActive", "Order" });
migrationBuilder.CreateIndex(
name: "IX_Game_Name",
table: "Game",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_GameSettlement_GameID_Year_Month",
table: "GameSettlement",
columns: new[] { "GameID", "Year", "Month" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_GameSettlement_SettledByAdminID",
table: "GameSettlement",
column: "SettledByAdminID");
migrationBuilder.CreateIndex(
name: "IX_GameSettlement_Year_Month_Status",
table: "GameSettlement",
columns: new[] { "Year", "Month", "Status" });
migrationBuilder.CreateIndex(
name: "IX_MemberAddress_MemberID_IsDefault",
table: "MemberAddress",
columns: new[] { "MemberID", "IsDefault" });
migrationBuilder.CreateIndex(
name: "IX_MemberAddress_RecipientName_Hash",
table: "MemberAddress",
column: "RecipientName_Hash");
migrationBuilder.CreateIndex(
name: "IX_MemberInventory_CouponCodeID",
table: "MemberInventory",
column: "CouponCodeID",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_MemberInventory_MemberID_UsedAt",
table: "MemberInventory",
columns: new[] { "MemberID", "UsedAt" });
migrationBuilder.CreateIndex(
name: "IX_MemberInventory_OrderItemID",
table: "MemberInventory",
column: "OrderItemID");
migrationBuilder.CreateIndex(
name: "IX_Order_ChannelID",
table: "Order",
column: "ChannelID");
migrationBuilder.CreateIndex(
name: "IX_Order_MemberID_Status_CreatedAt",
table: "Order",
columns: new[] { "MemberID", "Status", "CreatedAt" });
migrationBuilder.CreateIndex(
name: "IX_Order_OrderNumber",
table: "Order",
column: "OrderNumber",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Order_Status_CreatedAt",
table: "Order",
columns: new[] { "Status", "CreatedAt" });
migrationBuilder.CreateIndex(
name: "IX_OrderItem_IssuedCouponCodeID",
table: "OrderItem",
column: "IssuedCouponCodeID");
migrationBuilder.CreateIndex(
name: "IX_OrderItem_OrderID",
table: "OrderItem",
column: "OrderID");
migrationBuilder.CreateIndex(
name: "IX_OrderItem_ProductID",
table: "OrderItem",
column: "ProductID");
migrationBuilder.CreateIndex(
name: "IX_OrderRefund_AdminID",
table: "OrderRefund",
column: "AdminID");
migrationBuilder.CreateIndex(
name: "IX_OrderRefund_OrderID_Status",
table: "OrderRefund",
columns: new[] { "OrderID", "Status" });
migrationBuilder.CreateIndex(
name: "IX_OrderRefund_Status",
table: "OrderRefund",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_Product_GameID_IsActive_Order",
table: "Product",
columns: new[] { "GameID", "IsActive", "Order" });
migrationBuilder.CreateIndex(
name: "IX_Product_IsActive_Type",
table: "Product",
columns: new[] { "IsActive", "Type" });
migrationBuilder.CreateIndex(
name: "IX_Shipment_OrderID",
table: "Shipment",
column: "OrderID",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Shipment_Status",
table: "Shipment",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_Shipment_TrackingNumber",
table: "Shipment",
column: "TrackingNumber");
// 기존 WithdrawalRequest row 백필: 신규 분개 컬럼(DonationAmount/StoreRevenueAmount)이 0 으로 채워졌으나,
// 기존 row 는 모두 후원 잔액(Donation) 단일 출금이었으므로 DonationAmount = RequestedAmount 로 백필.
migrationBuilder.Sql("UPDATE WithdrawalRequest SET DonationAmount = RequestedAmount WHERE DonationAmount = 0 AND RequestedAmount > 0;");
}
///
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "GameSettlement");
migrationBuilder.DropTable(
name: "MemberAddress");
migrationBuilder.DropTable(
name: "MemberInventory");
migrationBuilder.DropTable(
name: "OrderRefund");
migrationBuilder.DropTable(
name: "Shipment");
migrationBuilder.DropTable(
name: "OrderItem");
migrationBuilder.DropTable(
name: "CouponCode");
migrationBuilder.DropTable(
name: "Order");
migrationBuilder.DropTable(
name: "Coupon");
migrationBuilder.DropTable(
name: "Product");
migrationBuilder.DropTable(
name: "Game");
migrationBuilder.DropColumn(
name: "DonationAmount",
table: "WithdrawalRequest");
migrationBuilder.DropColumn(
name: "StoreRevenueAmount",
table: "WithdrawalRequest");
}
}
}