From 609ac39ffb155ef96d4c2dc5f942b24d67fa1c60 Mon Sep 17 00:00:00 2001 From: hskwon Date: Wed, 23 Jul 2025 15:41:01 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EB=B0=8F=20DB=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit products - common_codes - parts - products - files - price_histories - boms - bom_items boards - boards - board_settings - posts - board_comments - board_files - post_custom_field_values permissions - menus - roles - user_menu_permissions - role_menu_permissions tenants - tenants - plans - subscriptions - payments --- app/Models/Boards/Board.php | 21 +++++++++ app/Models/Boards/BoardComment.php | 29 ++++++++++++ app/Models/Boards/BoardFile.php | 15 +++++++ app/Models/Boards/BoardSetting.php | 15 +++++++ app/Models/Boards/Post.php | 30 +++++++++++++ app/Models/Boards/PostCustomFieldValue.php | 18 ++++++++ app/Models/Menu.php | 23 ++++++++++ app/Models/Payment.php | 23 ++++++++++ app/Models/Plan.php | 25 +++++++++++ app/Models/Products/Bom.php | 25 +++++++++++ app/Models/Products/BomItem.php | 22 +++++++++ app/Models/Products/CommonCode.php | 19 ++++++++ app/Models/Products/File.php | 12 +++++ app/Models/Products/Part.php | 21 +++++++++ app/Models/Products/PriceHistory.php | 12 +++++ app/Models/Products/Product.php | 19 ++++++++ app/Models/Role.php | 17 +++++++ app/Models/RoleMenuPermission.php | 22 +++++++++ app/Models/Subscription.php | 31 +++++++++++++ app/Models/Tenant.php | 45 +++++++++++++++++++ app/Models/UserMenuPermission.php | 22 +++++++++ ...07_23_125353_create_common_codes_table.php | 33 ++++++++++++++ .../2025_07_23_125353_create_parts_table.php | 35 +++++++++++++++ ...025_07_23_125353_create_products_table.php | 32 +++++++++++++ .../2025_07_23_125354_create_files_table.php | 28 ++++++++++++ ...23_125354_create_price_histories_table.php | 34 ++++++++++++++ .../2025_07_23_125356_create_boms_table.php | 37 +++++++++++++++ ...25_07_23_125357_create_bom_items_table.php | 33 ++++++++++++++ ...2025_07_23_132400_create_tenants_table.php | 37 +++++++++++++++ .../2025_07_23_132401_create_boards_table.php | 31 +++++++++++++ ..._23_132616_create_board_settings_table.php | 28 ++++++++++++ .../2025_07_23_132616_create_posts_table.php | 35 +++++++++++++++ ..._23_132617_create_board_comments_table.php | 31 +++++++++++++ ..._07_23_132617_create_board_files_table.php | 25 +++++++++++ ..._create_post_custom_field_values_table.php | 25 +++++++++++ .../2025_07_23_140449_create_menus_table.php | 30 +++++++++++++ .../2025_07_23_140449_create_roles_table.php | 23 ++++++++++ ...449_create_user_menu_permissions_table.php | 31 +++++++++++++ ...450_create_role_menu_permissions_table.php | 31 +++++++++++++ .../2025_07_23_144843_create_plans_table.php | 27 +++++++++++ ...7_23_144843_create_subscriptions_table.php | 25 +++++++++++ ...025_07_23_144844_create_payments_table.php | 27 +++++++++++ 42 files changed, 1104 insertions(+) create mode 100644 app/Models/Boards/Board.php create mode 100644 app/Models/Boards/BoardComment.php create mode 100644 app/Models/Boards/BoardFile.php create mode 100644 app/Models/Boards/BoardSetting.php create mode 100644 app/Models/Boards/Post.php create mode 100644 app/Models/Boards/PostCustomFieldValue.php create mode 100644 app/Models/Menu.php create mode 100644 app/Models/Payment.php create mode 100644 app/Models/Plan.php create mode 100644 app/Models/Products/Bom.php create mode 100644 app/Models/Products/BomItem.php create mode 100644 app/Models/Products/CommonCode.php create mode 100644 app/Models/Products/File.php create mode 100644 app/Models/Products/Part.php create mode 100644 app/Models/Products/PriceHistory.php create mode 100644 app/Models/Products/Product.php create mode 100644 app/Models/Role.php create mode 100644 app/Models/RoleMenuPermission.php create mode 100644 app/Models/Subscription.php create mode 100644 app/Models/Tenant.php create mode 100644 app/Models/UserMenuPermission.php create mode 100644 database/migrations/2025_07_23_125353_create_common_codes_table.php create mode 100644 database/migrations/2025_07_23_125353_create_parts_table.php create mode 100644 database/migrations/2025_07_23_125353_create_products_table.php create mode 100644 database/migrations/2025_07_23_125354_create_files_table.php create mode 100644 database/migrations/2025_07_23_125354_create_price_histories_table.php create mode 100644 database/migrations/2025_07_23_125356_create_boms_table.php create mode 100644 database/migrations/2025_07_23_125357_create_bom_items_table.php create mode 100644 database/migrations/2025_07_23_132400_create_tenants_table.php create mode 100644 database/migrations/2025_07_23_132401_create_boards_table.php create mode 100644 database/migrations/2025_07_23_132616_create_board_settings_table.php create mode 100644 database/migrations/2025_07_23_132616_create_posts_table.php create mode 100644 database/migrations/2025_07_23_132617_create_board_comments_table.php create mode 100644 database/migrations/2025_07_23_132617_create_board_files_table.php create mode 100644 database/migrations/2025_07_23_132617_create_post_custom_field_values_table.php create mode 100644 database/migrations/2025_07_23_140449_create_menus_table.php create mode 100644 database/migrations/2025_07_23_140449_create_roles_table.php create mode 100644 database/migrations/2025_07_23_140449_create_user_menu_permissions_table.php create mode 100644 database/migrations/2025_07_23_140450_create_role_menu_permissions_table.php create mode 100644 database/migrations/2025_07_23_144843_create_plans_table.php create mode 100644 database/migrations/2025_07_23_144843_create_subscriptions_table.php create mode 100644 database/migrations/2025_07_23_144844_create_payments_table.php diff --git a/app/Models/Boards/Board.php b/app/Models/Boards/Board.php new file mode 100644 index 0000000..b72b437 --- /dev/null +++ b/app/Models/Boards/Board.php @@ -0,0 +1,21 @@ +hasMany(BoardSetting::class, 'board_id'); + } + public function posts() { + return $this->hasMany(Post::class, 'board_id'); + } +} diff --git a/app/Models/Boards/BoardComment.php b/app/Models/Boards/BoardComment.php new file mode 100644 index 0000000..5f481c1 --- /dev/null +++ b/app/Models/Boards/BoardComment.php @@ -0,0 +1,29 @@ +belongsTo(Post::class, 'post_id'); + } + public function user() { + return $this->belongsTo(User::class, 'user_id'); + } + public function parent() { + return $this->belongsTo(BoardComment::class, 'parent_id'); + } + public function children() { + return $this->hasMany(BoardComment::class, 'parent_id')->where('status', 'active'); + } +} diff --git a/app/Models/Boards/BoardFile.php b/app/Models/Boards/BoardFile.php new file mode 100644 index 0000000..c16a080 --- /dev/null +++ b/app/Models/Boards/BoardFile.php @@ -0,0 +1,15 @@ +belongsTo(Post::class, 'post_id'); + } +} diff --git a/app/Models/Boards/BoardSetting.php b/app/Models/Boards/BoardSetting.php new file mode 100644 index 0000000..b8eb532 --- /dev/null +++ b/app/Models/Boards/BoardSetting.php @@ -0,0 +1,15 @@ +belongsTo(Board::class, 'board_id'); + } +} diff --git a/app/Models/Boards/Post.php b/app/Models/Boards/Post.php new file mode 100644 index 0000000..62a536c --- /dev/null +++ b/app/Models/Boards/Post.php @@ -0,0 +1,30 @@ +hasMany(PostCustomFieldValue::class, 'post_id'); + } + public function files() { + return $this->hasMany(BoardFile::class, 'post_id'); + } + public function comments() { + return $this->hasMany(BoardComment::class, 'post_id')->whereNull('parent_id')->where('status', 'active'); + } + public function board() { + return $this->belongsTo(Board::class, 'board_id'); + } +} diff --git a/app/Models/Boards/PostCustomFieldValue.php b/app/Models/Boards/PostCustomFieldValue.php new file mode 100644 index 0000000..7cf92d9 --- /dev/null +++ b/app/Models/Boards/PostCustomFieldValue.php @@ -0,0 +1,18 @@ +belongsTo(Post::class, 'post_id'); + } + public function field() { + return $this->belongsTo(BoardSetting::class, 'field_id'); + } +} diff --git a/app/Models/Menu.php b/app/Models/Menu.php new file mode 100644 index 0000000..9a0bc27 --- /dev/null +++ b/app/Models/Menu.php @@ -0,0 +1,23 @@ +belongsTo(Menu::class, 'parent_id'); + } + + public function children() + { + return $this->hasMany(Menu::class, 'parent_id'); + } +} diff --git a/app/Models/Payment.php b/app/Models/Payment.php new file mode 100644 index 0000000..a307739 --- /dev/null +++ b/app/Models/Payment.php @@ -0,0 +1,23 @@ +belongsTo(Subscription::class); + } +} diff --git a/app/Models/Plan.php b/app/Models/Plan.php new file mode 100644 index 0000000..011329a --- /dev/null +++ b/app/Models/Plan.php @@ -0,0 +1,25 @@ + 'array', + 'is_active' => 'boolean', + 'price' => 'float', + ]; + + public function subscriptions() { + return $this->hasMany(Subscription::class); + } +} diff --git a/app/Models/Products/Bom.php b/app/Models/Products/Bom.php new file mode 100644 index 0000000..3b9196d --- /dev/null +++ b/app/Models/Products/Bom.php @@ -0,0 +1,25 @@ +belongsTo(Product::class); + } + public function category() { + return $this->belongsTo(CommonCode::class, 'category_id'); + } + public function items() { + return $this->hasMany(BomItem::class); + } + public function image() { + return $this->belongsTo(File::class, 'image_file_id'); + } +} diff --git a/app/Models/Products/BomItem.php b/app/Models/Products/BomItem.php new file mode 100644 index 0000000..3ef05fa --- /dev/null +++ b/app/Models/Products/BomItem.php @@ -0,0 +1,22 @@ +belongsTo(Bom::class); + } + public function parent() { + return $this->belongsTo(self::class, 'parent_id'); + } + public function children() { + return $this->hasMany(self::class, 'parent_id'); + } +} diff --git a/app/Models/Products/CommonCode.php b/app/Models/Products/CommonCode.php new file mode 100644 index 0000000..59c0689 --- /dev/null +++ b/app/Models/Products/CommonCode.php @@ -0,0 +1,19 @@ +belongsTo(self::class, 'parent_id'); + } + public function children() { + return $this->hasMany(self::class, 'parent_id'); + } +} diff --git a/app/Models/Products/File.php b/app/Models/Products/File.php new file mode 100644 index 0000000..ae38a0d --- /dev/null +++ b/app/Models/Products/File.php @@ -0,0 +1,12 @@ +belongsTo(CommonCode::class, 'category_id'); + } + public function partType() { + return $this->belongsTo(CommonCode::class, 'part_type_id'); + } +} diff --git a/app/Models/Products/PriceHistory.php b/app/Models/Products/PriceHistory.php new file mode 100644 index 0000000..8eb2c3d --- /dev/null +++ b/app/Models/Products/PriceHistory.php @@ -0,0 +1,12 @@ +belongsTo(CommonCode::class, 'category_id'); + } + public function boms() { + return $this->hasMany(Bom::class); + } +} diff --git a/app/Models/Role.php b/app/Models/Role.php new file mode 100644 index 0000000..4043f53 --- /dev/null +++ b/app/Models/Role.php @@ -0,0 +1,17 @@ +hasMany(RoleMenuPermission::class, 'role_id'); + } +} diff --git a/app/Models/RoleMenuPermission.php b/app/Models/RoleMenuPermission.php new file mode 100644 index 0000000..de4350a --- /dev/null +++ b/app/Models/RoleMenuPermission.php @@ -0,0 +1,22 @@ +belongsTo(Role::class, 'role_id'); + } + + public function menu() + { + return $this->belongsTo(Menu::class, 'menu_id'); + } +} diff --git a/app/Models/Subscription.php b/app/Models/Subscription.php new file mode 100644 index 0000000..11c591e --- /dev/null +++ b/app/Models/Subscription.php @@ -0,0 +1,31 @@ +belongsTo(Tenant::class); + } + + public function plan() { + return $this->belongsTo(Plan::class); + } + + public function payments() { + return $this->hasMany(Payment::class); + } +} diff --git a/app/Models/Tenant.php b/app/Models/Tenant.php new file mode 100644 index 0000000..a6856dc --- /dev/null +++ b/app/Models/Tenant.php @@ -0,0 +1,45 @@ + 'datetime', + 'expires_at' => 'datetime', + 'last_paid_at' => 'datetime', + 'max_users' => 'integer', + ]; + + // 관계 정의 (예시) + public function plan() + { + return $this->belongsTo(Plan::class, 'plan_id'); + } + + public function subscription() + { + return $this->belongsTo(Subscription::class, 'subscription_id'); + } +} diff --git a/app/Models/UserMenuPermission.php b/app/Models/UserMenuPermission.php new file mode 100644 index 0000000..d1ec319 --- /dev/null +++ b/app/Models/UserMenuPermission.php @@ -0,0 +1,22 @@ +belongsTo(User::class, 'user_id'); + } + + public function menu() + { + return $this->belongsTo(Menu::class, 'menu_id'); + } +} diff --git a/database/migrations/2025_07_23_125353_create_common_codes_table.php b/database/migrations/2025_07_23_125353_create_common_codes_table.php new file mode 100644 index 0000000..715a581 --- /dev/null +++ b/database/migrations/2025_07_23_125353_create_common_codes_table.php @@ -0,0 +1,33 @@ +id(); + $table->unsignedBigInteger('tenant_id')->nullable()->comment('멀티테넌시'); + $table->string('code_group', 30)->default('category')->comment('코드 그룹'); + $table->string('code', 20)->comment('코드값'); + $table->string('name', 100)->comment('이름'); + $table->unsignedBigInteger('parent_id')->nullable()->comment('상위코드ID(트리)'); + $table->string('description', 255)->nullable()->comment('설명'); + $table->tinyInteger('is_active')->default(1)->comment('사용여부'); + $table->integer('sort_order')->default(0)->comment('정렬순서'); + $table->timestamps(); + $table->softDeletes(); + + $table->unique(['tenant_id','code_group','code']); + $table->foreign('parent_id')->references('id')->on('common_codes'); + }); + } + + public function down() + { + Schema::dropIfExists('common_codes'); + } +}; diff --git a/database/migrations/2025_07_23_125353_create_parts_table.php b/database/migrations/2025_07_23_125353_create_parts_table.php new file mode 100644 index 0000000..e03dae9 --- /dev/null +++ b/database/migrations/2025_07_23_125353_create_parts_table.php @@ -0,0 +1,35 @@ +id(); + $table->unsignedBigInteger('tenant_id')->comment('멀티테넌시'); + $table->string('code', 30)->comment('부품코드'); + $table->string('name', 100)->comment('부품명'); + $table->unsignedBigInteger('category_id')->comment('카테고리ID(common_codes)'); + $table->unsignedBigInteger('part_type_id')->nullable()->comment('부품타입ID(common_codes)'); + $table->string('unit', 20)->nullable()->comment('단위'); + $table->json('attributes')->nullable()->comment('동적 속성'); + $table->string('description', 255)->nullable()->comment('설명'); + $table->tinyInteger('is_active')->default(1)->comment('사용여부'); + $table->timestamps(); + $table->softDeletes(); + + $table->unique(['tenant_id','code']); + $table->foreign('category_id')->references('id')->on('common_codes'); + $table->foreign('part_type_id')->references('id')->on('common_codes'); + }); + } + + public function down() + { + Schema::dropIfExists('parts'); + } +}; diff --git a/database/migrations/2025_07_23_125353_create_products_table.php b/database/migrations/2025_07_23_125353_create_products_table.php new file mode 100644 index 0000000..4602b37 --- /dev/null +++ b/database/migrations/2025_07_23_125353_create_products_table.php @@ -0,0 +1,32 @@ +id(); + $table->unsignedBigInteger('tenant_id')->comment('멀티테넌시'); + $table->string('code', 30)->comment('제품코드'); + $table->string('name', 100)->comment('제품명'); + $table->unsignedBigInteger('category_id')->comment('카테고리ID(common_codes)'); + $table->json('attributes')->nullable()->comment('동적 속성'); + $table->string('description', 255)->nullable()->comment('설명'); + $table->tinyInteger('is_active')->default(1)->comment('사용여부'); + $table->timestamps(); + $table->softDeletes(); + + $table->unique(['tenant_id','code']); + $table->foreign('category_id')->references('id')->on('common_codes'); + }); + } + + public function down() + { + Schema::dropIfExists('products'); + } +}; diff --git a/database/migrations/2025_07_23_125354_create_files_table.php b/database/migrations/2025_07_23_125354_create_files_table.php new file mode 100644 index 0000000..ef7816f --- /dev/null +++ b/database/migrations/2025_07_23_125354_create_files_table.php @@ -0,0 +1,28 @@ +id(); + $table->unsignedBigInteger('tenant_id')->comment('멀티테넌시'); + $table->string('file_path', 255)->comment('저장경로'); + $table->string('file_name', 255); + $table->integer('file_size')->nullable(); + $table->string('mime_type', 50)->nullable(); + $table->string('description', 255)->nullable(); + $table->timestamps(); + $table->softDeletes(); + }); + } + + public function down() + { + Schema::dropIfExists('files'); + } +}; diff --git a/database/migrations/2025_07_23_125354_create_price_histories_table.php b/database/migrations/2025_07_23_125354_create_price_histories_table.php new file mode 100644 index 0000000..eefc1d2 --- /dev/null +++ b/database/migrations/2025_07_23_125354_create_price_histories_table.php @@ -0,0 +1,34 @@ +id(); + $table->unsignedBigInteger('tenant_id')->comment('멀티테넌시'); + $table->string('item_type_code', 20)->comment('대상구분(product/part/bom 등, code_group=price_item_type)'); + $table->unsignedBigInteger('item_id')->comment('대상ID'); + $table->string('price_type_code', 20)->comment('단가구분(default/20%up/10%discount 등, code_group=price_type)'); + $table->decimal('price', 18, 4)->comment('단가'); + $table->date('started_at'); + $table->date('ended_at')->nullable(); + $table->timestamps(); + $table->softDeletes(); + + $table->index( + ['tenant_id', 'item_type_code', 'item_id', 'started_at'], + 'idx_price_histories_main' + ); + }); + } + + public function down() + { + Schema::dropIfExists('price_histories'); + } +}; diff --git a/database/migrations/2025_07_23_125356_create_boms_table.php b/database/migrations/2025_07_23_125356_create_boms_table.php new file mode 100644 index 0000000..284e81a --- /dev/null +++ b/database/migrations/2025_07_23_125356_create_boms_table.php @@ -0,0 +1,37 @@ +id(); + $table->unsignedBigInteger('tenant_id')->comment('멀티테넌시'); + $table->unsignedBigInteger('product_id')->comment('제품ID'); + $table->string('code', 30)->comment('BOM코드'); + $table->string('name', 100)->comment('BOM명'); + $table->unsignedBigInteger('category_id')->comment('카테고리ID(common_codes)'); + $table->json('attributes')->nullable()->comment('동적 속성'); + $table->string('description', 255)->nullable()->comment('설명'); + $table->boolean('is_default')->default(0)->comment('기본BOM여부'); + $table->boolean('is_active')->default(1)->comment('사용여부'); + $table->unsignedBigInteger('image_file_id')->nullable()->comment('첨부파일ID'); + $table->timestamps(); + $table->softDeletes(); + + $table->unique(['tenant_id', 'product_id', 'code']); + $table->foreign('product_id')->references('id')->on('products'); + $table->foreign('category_id')->references('id')->on('common_codes'); + $table->foreign('image_file_id')->references('id')->on('files'); + }); + } + + public function down() + { + Schema::dropIfExists('boms'); + } +}; diff --git a/database/migrations/2025_07_23_125357_create_bom_items_table.php b/database/migrations/2025_07_23_125357_create_bom_items_table.php new file mode 100644 index 0000000..83ddb02 --- /dev/null +++ b/database/migrations/2025_07_23_125357_create_bom_items_table.php @@ -0,0 +1,33 @@ +id(); + $table->unsignedBigInteger('tenant_id')->comment('멀티테넌시'); + $table->unsignedBigInteger('bom_id')->comment('BOM ID'); + $table->unsignedBigInteger('parent_id')->nullable()->comment('상위 BOM Item'); + $table->string('item_type', 20)->comment('item 종류: part, product, bom'); + $table->unsignedBigInteger('ref_id')->comment('참조 ID'); + $table->decimal('quantity', 18, 4)->default(1); + $table->json('attributes')->nullable()->comment('동적 속성'); + $table->integer('sort_order')->default(0); + $table->timestamps(); + $table->softDeletes(); + + $table->foreign('bom_id')->references('id')->on('boms'); + $table->foreign('parent_id')->references('id')->on('bom_items'); + }); + } + + public function down() + { + Schema::dropIfExists('bom_items'); + } +}; diff --git a/database/migrations/2025_07_23_132400_create_tenants_table.php b/database/migrations/2025_07_23_132400_create_tenants_table.php new file mode 100644 index 0000000..28beda5 --- /dev/null +++ b/database/migrations/2025_07_23_132400_create_tenants_table.php @@ -0,0 +1,37 @@ +bigIncrements('id')->comment('PK'); + $table->string('name', 100)->comment('회사/조직명'); + $table->string('code', 50)->unique()->comment('테넌트 코드'); + $table->string('email', 80)->nullable()->comment('대표 이메일'); + $table->string('phone', 20)->nullable()->comment('대표 전화번호'); + $table->string('address', 255)->nullable()->comment('주소'); + $table->string('tenant_st_code', 20)->default('trial')->comment('테넌트 상태(trial,active,suspended,cancelled)'); + $table->unsignedBigInteger('plan_id')->nullable()->comment('현재 요금제(플랜) ID'); + $table->unsignedBigInteger('subscription_id')->nullable()->comment('현재 구독 정보 ID'); + $table->integer('max_users')->default(10)->comment('최대 사용자 수'); + $table->dateTime('trial_ends_at')->nullable()->comment('트라이얼 종료일'); + $table->dateTime('expires_at')->nullable()->comment('계약 만료일'); + $table->dateTime('last_paid_at')->nullable()->comment('마지막 결제일'); + $table->string('billing_tp_code', 20)->default('monthly')->comment('결제 주기(monthly,yearly)'); + $table->timestamps(); + $table->softDeletes(); + + // FK는 필요시 추가 + // $table->foreign('plan_id')->references('id')->on('plans'); + // $table->foreign('subscription_id')->references('id')->on('subscriptions'); + }); + } + + public function down(): void { + Schema::dropIfExists('tenants'); + } +}; diff --git a/database/migrations/2025_07_23_132401_create_boards_table.php b/database/migrations/2025_07_23_132401_create_boards_table.php new file mode 100644 index 0000000..9744ccb --- /dev/null +++ b/database/migrations/2025_07_23_132401_create_boards_table.php @@ -0,0 +1,31 @@ +id()->comment('게시판 고유번호'); + $table->unsignedBigInteger('tenant_id')->comment('테넌트 고유번호'); + $table->string('board_code', 30)->comment('게시판 코드'); + $table->string('name', 100)->comment('게시판 이름'); + $table->string('description', 255)->nullable()->comment('게시판 설명'); + $table->string('editor_type', 20)->default('wysiwyg')->comment('에디터 유형'); + $table->boolean('allow_files')->default(true)->comment('파일첨부 허용 여부'); + $table->integer('max_file_count')->default(5)->comment('최대 첨부파일 수'); + $table->integer('max_file_size')->default(20480)->comment('최대 첨부파일 크기(KB)'); + $table->json('extra_settings')->nullable()->comment('추가 설정(JSON)'); + $table->boolean('is_active')->default(true)->comment('활성화 여부'); + $table->timestamps(); + + $table->unique(['tenant_id', 'board_code']); + $table->foreign('tenant_id')->references('id')->on('tenants'); + }); + } + public function down(): void { + Schema::dropIfExists('boards'); + } +}; diff --git a/database/migrations/2025_07_23_132616_create_board_settings_table.php b/database/migrations/2025_07_23_132616_create_board_settings_table.php new file mode 100644 index 0000000..3d1e8c4 --- /dev/null +++ b/database/migrations/2025_07_23_132616_create_board_settings_table.php @@ -0,0 +1,28 @@ +id()->comment('커스텀필드 고유번호'); + $table->unsignedBigInteger('board_id')->comment('게시판 고유번호'); + $table->string('name', 100)->comment('필드명'); + $table->string('field_key', 50)->comment('필드키'); + $table->string('field_type', 20)->comment('필드유형'); + $table->json('field_meta')->nullable()->comment('필드메타'); + $table->boolean('is_required')->default(false)->comment('필수여부'); + $table->integer('sort_order')->default(0)->comment('정렬순서'); + $table->timestamps(); + + $table->unique(['board_id', 'field_key']); + $table->foreign('board_id')->references('id')->on('boards'); + }); + } + public function down(): void { + Schema::dropIfExists('board_settings'); + } +}; diff --git a/database/migrations/2025_07_23_132616_create_posts_table.php b/database/migrations/2025_07_23_132616_create_posts_table.php new file mode 100644 index 0000000..bfdc105 --- /dev/null +++ b/database/migrations/2025_07_23_132616_create_posts_table.php @@ -0,0 +1,35 @@ +id()->comment('게시글 고유번호'); + $table->unsignedBigInteger('tenant_id')->comment('테넌트 고유번호'); + $table->unsignedBigInteger('board_id')->comment('게시판 고유번호'); + $table->unsignedBigInteger('user_id')->nullable()->comment('작성자 고유번호'); + $table->string('title', 255)->comment('제목'); + $table->longText('content')->comment('내용'); + $table->string('editor_type', 20)->default('wysiwyg')->comment('에디터 유형'); + $table->string('ip_address', 45)->nullable()->comment('작성자 IP'); + $table->boolean('is_notice')->default(false)->comment('공지글 여부'); + $table->boolean('is_secret')->default(false)->comment('비밀글 여부'); + $table->integer('views')->default(0)->comment('조회수'); + $table->string('status', 20)->default('active')->comment('상태'); + $table->timestamps(); + $table->softDeletes()->comment('삭제일시(Soft Delete)'); + + $table->index(['tenant_id', 'board_id']); + $table->index('status'); + $table->foreign('tenant_id')->references('id')->on('tenants'); + $table->foreign('board_id')->references('id')->on('boards'); + }); + } + public function down(): void { + Schema::dropIfExists('posts'); + } +}; diff --git a/database/migrations/2025_07_23_132617_create_board_comments_table.php b/database/migrations/2025_07_23_132617_create_board_comments_table.php new file mode 100644 index 0000000..1c5f8a5 --- /dev/null +++ b/database/migrations/2025_07_23_132617_create_board_comments_table.php @@ -0,0 +1,31 @@ +id()->comment('댓글 고유번호'); + $table->unsignedBigInteger('post_id')->comment('게시글 고유번호'); + $table->unsignedBigInteger('tenant_id')->comment('테넌트 고유번호'); + $table->unsignedBigInteger('user_id')->nullable()->comment('작성자 고유번호'); + $table->unsignedBigInteger('parent_id')->nullable()->comment('상위 댓글ID(대댓글)'); + $table->text('content')->comment('댓글 내용'); + $table->string('ip_address', 45)->nullable()->comment('작성자 IP'); + $table->string('status', 20)->default('active')->comment('상태'); + $table->timestamps(); + $table->softDeletes()->comment('삭제일시(Soft Delete)'); + + $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade'); + $table->foreign('tenant_id')->references('id')->on('tenants'); + $table->foreign('user_id')->references('id')->on('users'); + $table->foreign('parent_id')->references('id')->on('board_comments'); + }); + } + public function down(): void { + Schema::dropIfExists('board_comments'); + } +}; diff --git a/database/migrations/2025_07_23_132617_create_board_files_table.php b/database/migrations/2025_07_23_132617_create_board_files_table.php new file mode 100644 index 0000000..f9294b7 --- /dev/null +++ b/database/migrations/2025_07_23_132617_create_board_files_table.php @@ -0,0 +1,25 @@ +id()->comment('첨부파일 고유번호'); + $table->unsignedBigInteger('post_id')->comment('게시글 고유번호'); + $table->string('file_path', 255)->comment('저장경로'); + $table->string('file_name', 255)->comment('원본 파일명'); + $table->integer('file_size')->comment('파일 크기(Byte)'); + $table->string('file_type', 100)->nullable()->comment('파일 MIME 타입'); + $table->timestamps(); + + $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade'); + }); + } + public function down(): void { + Schema::dropIfExists('board_files'); + } +}; diff --git a/database/migrations/2025_07_23_132617_create_post_custom_field_values_table.php b/database/migrations/2025_07_23_132617_create_post_custom_field_values_table.php new file mode 100644 index 0000000..f611e9b --- /dev/null +++ b/database/migrations/2025_07_23_132617_create_post_custom_field_values_table.php @@ -0,0 +1,25 @@ +id()->comment('필드값 고유번호'); + $table->unsignedBigInteger('post_id')->comment('게시글 고유번호'); + $table->unsignedBigInteger('field_id')->comment('커스텀필드 고유번호'); + $table->text('value')->nullable()->comment('필드값'); + $table->timestamps(); + + $table->unique(['post_id', 'field_id']); + $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade'); + $table->foreign('field_id')->references('id')->on('board_settings'); + }); + } + public function down(): void { + Schema::dropIfExists('post_custom_field_values'); + } +}; diff --git a/database/migrations/2025_07_23_140449_create_menus_table.php b/database/migrations/2025_07_23_140449_create_menus_table.php new file mode 100644 index 0000000..dd7b621 --- /dev/null +++ b/database/migrations/2025_07_23_140449_create_menus_table.php @@ -0,0 +1,30 @@ +bigIncrements('id')->comment('PK: 메뉴 ID'); + $table->unsignedBigInteger('tenant_id')->nullable()->index()->comment('FK: 테넌트 ID(null=공용메뉴)'); + $table->unsignedBigInteger('parent_id')->nullable()->index()->comment('상위 메뉴 ID'); + $table->string('name', 100)->comment('메뉴명'); + $table->string('url', 255)->nullable()->comment('메뉴 URL'); + $table->boolean('is_active')->default(true)->comment('활성여부(1=활성,0=비활성)'); + $table->integer('sort_order')->default(0)->comment('정렬순서'); + $table->boolean('hidden')->default(false)->comment('숨김여부'); + $table->boolean('is_external')->default(false)->comment('외부링크여부'); + $table->string('external_url', 255)->nullable()->comment('외부링크 URL'); + $table->string('icon', 50)->nullable()->comment('아이콘명'); + $table->timestamps(); + }); + } + public function down() + { + Schema::dropIfExists('menus'); + } +}; diff --git a/database/migrations/2025_07_23_140449_create_roles_table.php b/database/migrations/2025_07_23_140449_create_roles_table.php new file mode 100644 index 0000000..9146ba8 --- /dev/null +++ b/database/migrations/2025_07_23_140449_create_roles_table.php @@ -0,0 +1,23 @@ +bigIncrements('id')->comment('PK: 역할 ID'); + $table->unsignedBigInteger('tenant_id')->nullable()->index()->comment('FK: 테넌트 ID(null=공용역할)'); + $table->string('name', 50)->comment('역할명'); + $table->string('description', 255)->nullable()->comment('설명'); + $table->timestamps(); + }); + } + public function down() + { + Schema::dropIfExists('roles'); + } +}; diff --git a/database/migrations/2025_07_23_140449_create_user_menu_permissions_table.php b/database/migrations/2025_07_23_140449_create_user_menu_permissions_table.php new file mode 100644 index 0000000..8ec19b6 --- /dev/null +++ b/database/migrations/2025_07_23_140449_create_user_menu_permissions_table.php @@ -0,0 +1,31 @@ +bigIncrements('id')->comment('PK: 사용자-메뉴 권한 ID'); + $table->unsignedBigInteger('user_id')->comment('FK: 사용자 ID'); + $table->unsignedBigInteger('menu_id')->comment('FK: 메뉴 ID'); + $table->boolean('access')->default(false)->comment('메뉴 접근 권한'); + $table->boolean('read')->default(false)->comment('조회 권한'); + $table->boolean('write')->default(false)->comment('등록/수정/삭제 권한'); + $table->boolean('export')->default(false)->comment('다운로드 권한'); + $table->boolean('approve')->default(false)->comment('승인/반려 권한'); + $table->timestamps(); + + $table->unique(['user_id', 'menu_id']); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('menu_id')->references('id')->on('menus')->onDelete('cascade'); + }); + } + public function down() + { + Schema::dropIfExists('user_menu_permissions'); + } +}; diff --git a/database/migrations/2025_07_23_140450_create_role_menu_permissions_table.php b/database/migrations/2025_07_23_140450_create_role_menu_permissions_table.php new file mode 100644 index 0000000..8249ba0 --- /dev/null +++ b/database/migrations/2025_07_23_140450_create_role_menu_permissions_table.php @@ -0,0 +1,31 @@ +bigIncrements('id')->comment('PK: 역할-메뉴 권한 ID'); + $table->unsignedBigInteger('role_id')->comment('FK: 역할 ID'); + $table->unsignedBigInteger('menu_id')->comment('FK: 메뉴 ID'); + $table->boolean('access')->default(false)->comment('메뉴 접근 권한'); + $table->boolean('read')->default(false)->comment('조회 권한'); + $table->boolean('write')->default(false)->comment('등록/수정/삭제 권한'); + $table->boolean('export')->default(false)->comment('다운로드 권한'); + $table->boolean('approve')->default(false)->comment('승인/반려 권한'); + $table->timestamps(); + + $table->unique(['role_id', 'menu_id']); + $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade'); + $table->foreign('menu_id')->references('id')->on('menus')->onDelete('cascade'); + }); + } + public function down() + { + Schema::dropIfExists('role_menu_permissions'); + } +}; diff --git a/database/migrations/2025_07_23_144843_create_plans_table.php b/database/migrations/2025_07_23_144843_create_plans_table.php new file mode 100644 index 0000000..0554513 --- /dev/null +++ b/database/migrations/2025_07_23_144843_create_plans_table.php @@ -0,0 +1,27 @@ +id()->comment('PK'); + $table->string('name')->comment('플랜명'); + $table->string('code', 30)->unique()->comment('플랜 코드'); + $table->text('description')->nullable()->comment('설명'); + $table->decimal('price', 12, 2)->comment('월 요금'); + $table->string('billing_cycle', 20)->default('monthly')->comment('청구 주기'); + $table->json('features')->nullable()->comment('제공 기능/제한사항'); + $table->boolean('is_active')->default(true)->comment('사용여부'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + public function down(): void { + Schema::dropIfExists('plans'); + } +}; diff --git a/database/migrations/2025_07_23_144843_create_subscriptions_table.php b/database/migrations/2025_07_23_144843_create_subscriptions_table.php new file mode 100644 index 0000000..d07eea7 --- /dev/null +++ b/database/migrations/2025_07_23_144843_create_subscriptions_table.php @@ -0,0 +1,25 @@ +id()->comment('PK'); + $table->foreignId('tenant_id')->constrained('tenants')->comment('테넌트ID'); + $table->foreignId('plan_id')->constrained('plans')->comment('플랜ID'); + $table->date('started_at')->comment('구독 시작일'); + $table->date('ended_at')->nullable()->comment('구독 종료일(해지시)'); + $table->string('status', 20)->default('active')->comment('상태(active, canceled, expired 등)'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + public function down(): void { + Schema::dropIfExists('subscriptions'); + } +}; diff --git a/database/migrations/2025_07_23_144844_create_payments_table.php b/database/migrations/2025_07_23_144844_create_payments_table.php new file mode 100644 index 0000000..e736ffd --- /dev/null +++ b/database/migrations/2025_07_23_144844_create_payments_table.php @@ -0,0 +1,27 @@ +id()->comment('PK'); + $table->foreignId('subscription_id')->constrained('subscriptions')->comment('구독ID'); + $table->decimal('amount', 12, 2)->comment('결제금액'); + $table->string('payment_method', 30)->comment('결제수단'); + $table->string('transaction_id', 100)->nullable()->comment('PG 거래ID'); + $table->dateTime('paid_at')->comment('결제일시'); + $table->string('status', 20)->default('paid')->comment('결제상태(paid, failed 등)'); + $table->text('memo')->nullable()->comment('비고'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + public function down(): void { + Schema::dropIfExists('payments'); + } +};