20260705002416_AddConsumableItemsAndProfileCooldown.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using System;
  2. using Microsoft.EntityFrameworkCore.Migrations;
  3. #nullable disable
  4. namespace Infrastructure.Migrations.AppDb
  5. {
  6. /// <inheritdoc />
  7. public partial class AddConsumableItemsAndProfileCooldown : Migration
  8. {
  9. /// <inheritdoc />
  10. protected override void Up(MigrationBuilder migrationBuilder)
  11. {
  12. migrationBuilder.AlterColumn<int>(
  13. name: "GameID",
  14. table: "Product",
  15. type: "int",
  16. nullable: true,
  17. comment: "게임 ID (선택 — null=플랫폼 직접판매)",
  18. oldClrType: typeof(int),
  19. oldType: "int");
  20. migrationBuilder.AddColumn<int>(
  21. name: "DurationDays",
  22. table: "Product",
  23. type: "int",
  24. nullable: false,
  25. defaultValue: 0,
  26. comment: "소모품 효과 지속일 (0=즉시소모)");
  27. migrationBuilder.AddColumn<byte>(
  28. name: "ItemKind",
  29. table: "Product",
  30. type: "tinyint",
  31. nullable: true,
  32. comment: "소모품 종류 (null=일반상품, 5~11=소모품)");
  33. migrationBuilder.AddColumn<DateTime>(
  34. name: "LastThumbChangedAt",
  35. table: "Member",
  36. type: "datetime2",
  37. nullable: true);
  38. migrationBuilder.AddColumn<int>(
  39. name: "Account_ChangeThumbDay",
  40. table: "Config",
  41. type: "int",
  42. nullable: true,
  43. comment: "프로필 이미지 갱신 주기(일)");
  44. migrationBuilder.CreateTable(
  45. name: "MemberItem",
  46. columns: table => new
  47. {
  48. ID = table.Column<int>(type: "int", nullable: false, comment: "PK")
  49. .Annotation("SqlServer:Identity", "1, 1"),
  50. MemberID = table.Column<int>(type: "int", nullable: false, comment: "회원 ID"),
  51. ProductID = table.Column<int>(type: "int", nullable: false, comment: "상품 ID"),
  52. OrderItemID = table.Column<int>(type: "int", nullable: false, comment: "발급 근거 주문항목 ID"),
  53. Kind = table.Column<byte>(type: "tinyint", nullable: false, comment: "아이템 종류 (5~11)"),
  54. DurationDays = table.Column<int>(type: "int", nullable: false, defaultValue: 0, comment: "효과 지속일 (0=즉시소모)"),
  55. AcquiredAt = table.Column<DateTime>(type: "datetime2", nullable: false, comment: "획득 일시"),
  56. UsedAt = table.Column<DateTime>(type: "datetime2", nullable: true, comment: "소모/활성 일시 (null=미사용)"),
  57. RevokedAt = table.Column<DateTime>(type: "datetime2", nullable: true, comment: "회수 일시 (환불)"),
  58. RowVersion = table.Column<byte[]>(type: "rowversion", rowVersion: true, nullable: false)
  59. },
  60. constraints: table =>
  61. {
  62. table.PrimaryKey("PK_MemberItem", x => x.ID);
  63. table.ForeignKey(
  64. name: "FK_MemberItem_Member_MemberID",
  65. column: x => x.MemberID,
  66. principalTable: "Member",
  67. principalColumn: "ID",
  68. onDelete: ReferentialAction.Cascade);
  69. table.ForeignKey(
  70. name: "FK_MemberItem_Product_ProductID",
  71. column: x => x.ProductID,
  72. principalTable: "Product",
  73. principalColumn: "ID",
  74. onDelete: ReferentialAction.Restrict);
  75. },
  76. comment: "계정 귀속 소모품 아이템");
  77. migrationBuilder.CreateTable(
  78. name: "MemberItemUsageLog",
  79. columns: table => new
  80. {
  81. ID = table.Column<int>(type: "int", nullable: false, comment: "PK")
  82. .Annotation("SqlServer:Identity", "1, 1"),
  83. MemberID = table.Column<int>(type: "int", nullable: false, comment: "회원 ID"),
  84. MemberItemID = table.Column<int>(type: "int", nullable: false, comment: "MemberItem ID"),
  85. ProductID = table.Column<int>(type: "int", nullable: false, comment: "상품 ID"),
  86. Kind = table.Column<byte>(type: "tinyint", nullable: false, comment: "아이템 종류"),
  87. Action = table.Column<byte>(type: "tinyint", nullable: false, comment: "액션 (1=Used, 2=Revoked)"),
  88. Detail = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: true, comment: "결과 요약"),
  89. CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, comment: "등록 일시")
  90. },
  91. constraints: table =>
  92. {
  93. table.PrimaryKey("PK_MemberItemUsageLog", x => x.ID);
  94. },
  95. comment: "소모품 아이템 사용/회수 로그");
  96. migrationBuilder.CreateTable(
  97. name: "MemberThumbChangeLog",
  98. columns: table => new
  99. {
  100. ID = table.Column<int>(type: "int", nullable: false, comment: "PK")
  101. .Annotation("SqlServer:Identity", "1, 1"),
  102. MemberID = table.Column<int>(type: "int", nullable: false, comment: "회원 ID"),
  103. BeforeThumb = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true, comment: "이전 이미지"),
  104. AfterThumb = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true, comment: "바꾼 이미지"),
  105. Referer = table.Column<string>(type: "nvarchar(max)", nullable: true, comment: "이전 페이지 주소"),
  106. IpAddress = table.Column<string>(type: "nvarchar(45)", maxLength: 45, nullable: true, comment: "IP Address (IPv6 호환)"),
  107. UserAgent = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true, comment: "User Agent"),
  108. CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, comment: "등록 일시")
  109. },
  110. constraints: table =>
  111. {
  112. table.PrimaryKey("PK_MemberThumbChangeLog", x => x.ID);
  113. table.ForeignKey(
  114. name: "FK_MemberThumbChangeLog_Member_MemberID",
  115. column: x => x.MemberID,
  116. principalTable: "Member",
  117. principalColumn: "ID",
  118. onDelete: ReferentialAction.Cascade);
  119. },
  120. comment: "프로필 이미지 변경 내역");
  121. migrationBuilder.CreateIndex(
  122. name: "IX_Product_ItemKind_IsActive",
  123. table: "Product",
  124. columns: new[] { "ItemKind", "IsActive" });
  125. migrationBuilder.CreateIndex(
  126. name: "IX_MemberItem_MemberID_Kind_UsedAt",
  127. table: "MemberItem",
  128. columns: new[] { "MemberID", "Kind", "UsedAt" });
  129. migrationBuilder.CreateIndex(
  130. name: "IX_MemberItem_OrderItemID",
  131. table: "MemberItem",
  132. column: "OrderItemID");
  133. migrationBuilder.CreateIndex(
  134. name: "IX_MemberItem_ProductID",
  135. table: "MemberItem",
  136. column: "ProductID");
  137. migrationBuilder.CreateIndex(
  138. name: "IX_MemberItemUsageLog_MemberID_CreatedAt",
  139. table: "MemberItemUsageLog",
  140. columns: new[] { "MemberID", "CreatedAt" });
  141. migrationBuilder.CreateIndex(
  142. name: "IX_MemberThumbChangeLog_MemberID",
  143. table: "MemberThumbChangeLog",
  144. column: "MemberID");
  145. // 기존 Config 싱글턴에 프로필(닉네임·한마디·이미지) 90일 쿨다운 활성화 — Wave 3 P3 결정 #3.
  146. // Name/Intro 는 미설정(NULL)일 때만 90 으로, Thumb 는 신규 컬럼이라 전부 90 으로 채운다.
  147. migrationBuilder.Sql(@"
  148. UPDATE [Config]
  149. SET [Account_ChangeNameDay] = COALESCE([Account_ChangeNameDay], 90),
  150. [Account_ChangeIntroDay] = COALESCE([Account_ChangeIntroDay], 90),
  151. [Account_ChangeThumbDay] = 90
  152. WHERE [Account_ChangeThumbDay] IS NULL;");
  153. }
  154. /// <inheritdoc />
  155. protected override void Down(MigrationBuilder migrationBuilder)
  156. {
  157. migrationBuilder.DropTable(
  158. name: "MemberItem");
  159. migrationBuilder.DropTable(
  160. name: "MemberItemUsageLog");
  161. migrationBuilder.DropTable(
  162. name: "MemberThumbChangeLog");
  163. migrationBuilder.DropIndex(
  164. name: "IX_Product_ItemKind_IsActive",
  165. table: "Product");
  166. migrationBuilder.DropColumn(
  167. name: "DurationDays",
  168. table: "Product");
  169. migrationBuilder.DropColumn(
  170. name: "ItemKind",
  171. table: "Product");
  172. migrationBuilder.DropColumn(
  173. name: "LastThumbChangedAt",
  174. table: "Member");
  175. migrationBuilder.DropColumn(
  176. name: "Account_ChangeThumbDay",
  177. table: "Config");
  178. migrationBuilder.AlterColumn<int>(
  179. name: "GameID",
  180. table: "Product",
  181. type: "int",
  182. nullable: false,
  183. defaultValue: 0,
  184. comment: "게임 ID (필수)",
  185. oldClrType: typeof(int),
  186. oldType: "int",
  187. oldNullable: true,
  188. oldComment: "게임 ID (선택 — null=플랫폼 직접판매)");
  189. }
  190. }
  191. }