feat : 테이블 및 DB 마이그레이션 파일 추가

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
This commit is contained in:
2025-07-23 15:41:01 +09:00
parent 9f1e74159f
commit 609ac39ffb
42 changed files with 1104 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Models\Boards;
use Illuminate\Database\Eloquent\Model;
class Board extends Model
{
protected $table = 'boards';
protected $fillable = [
'tenant_id', 'board_code', 'name', 'description', 'editor_type',
'allow_files', 'max_file_count', 'max_file_size', 'extra_settings', 'is_active'
];
public function customFields() {
return $this->hasMany(BoardSetting::class, 'board_id');
}
public function posts() {
return $this->hasMany(Post::class, 'board_id');
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Models\Boards;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class BoardComment extends Model
{
use SoftDeletes;
protected $table = 'board_comments';
protected $fillable = [
'post_id', 'tenant_id', 'user_id', 'parent_id', 'content', 'ip_address', 'status'
];
public function post() {
return $this->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');
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Models\Boards;
use Illuminate\Database\Eloquent\Model;
class BoardFile extends Model
{
protected $table = 'board_files';
protected $fillable = ['post_id', 'file_path', 'file_name', 'file_size', 'file_type'];
public function post() {
return $this->belongsTo(Post::class, 'post_id');
}
}

View File

@@ -0,0 +1,15 @@
<?php
use Illuminate\Database\Eloquent\Model;
class BoardSetting extends Model
{
protected $table = 'board_settings';
protected $fillable = [
'board_id', 'name', 'field_key', 'field_type', 'field_meta', 'is_required', 'sort_order'
];
public function board() {
return $this->belongsTo(Board::class, 'board_id');
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Models\Boards;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected $table = 'posts';
protected $fillable = [
'tenant_id', 'board_id', 'user_id', 'title', 'content', 'editor_type',
'ip_address', 'is_notice', 'is_secret', 'views', 'status'
];
public function customFieldValues() {
return $this->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');
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models\Boards;
use Illuminate\Database\Eloquent\Model;
class PostCustomFieldValue extends Model
{
protected $table = 'post_custom_field_values';
protected $fillable = ['post_id', 'field_id', 'value'];
public function post() {
return $this->belongsTo(Post::class, 'post_id');
}
public function field() {
return $this->belongsTo(BoardSetting::class, 'field_id');
}
}

23
app/Models/Menu.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Menu extends Model
{
protected $fillable = [
'tenant_id', 'parent_id', 'name', 'url', 'is_active', 'sort_order',
'hidden', 'is_external', 'external_url', 'icon'
];
public function parent()
{
return $this->belongsTo(Menu::class, 'parent_id');
}
public function children()
{
return $this->hasMany(Menu::class, 'parent_id');
}
}

23
app/Models/Payment.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Payment extends Model
{
use SoftDeletes;
protected $fillable = [
'subscription_id', 'amount', 'payment_method', 'transaction_id', 'paid_at', 'status', 'memo'
];
protected $dates = [
'paid_at',
];
public function subscription() {
return $this->belongsTo(Subscription::class);
}
}

25
app/Models/Plan.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Plan extends Model
{
use SoftDeletes;
protected $fillable = [
'name', 'code', 'description', 'price', 'billing_cycle', 'features', 'is_active',
];
protected $casts = [
'features' => 'array',
'is_active' => 'boolean',
'price' => 'float',
];
public function subscriptions() {
return $this->hasMany(Subscription::class);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Models\Products;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Bom extends Model
{
use SoftDeletes;
protected $fillable = ['tenant_id','product_id','code','name','category_id','attributes','description','is_default','is_active','image_file_id'];
public function product() {
return $this->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');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Models\Products;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class BomItem extends Model
{
use SoftDeletes;
protected $fillable = ['tenant_id','bom_id','parent_id','item_type','ref_id','quantity','attributes','sort_order'];
public function bom() {
return $this->belongsTo(Bom::class);
}
public function parent() {
return $this->belongsTo(self::class, 'parent_id');
}
public function children() {
return $this->hasMany(self::class, 'parent_id');
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Models\Products;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class CommonCode extends Model
{
use SoftDeletes;
protected $fillable = ['tenant_id','code_group','code','name','parent_id','description','is_active','sort_order'];
public function parent() {
return $this->belongsTo(self::class, 'parent_id');
}
public function children() {
return $this->hasMany(self::class, 'parent_id');
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Models\Products;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class File extends Model
{
use SoftDeletes;
protected $fillable = ['tenant_id','file_path','file_name','file_size','mime_type','description'];
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Models;
namespace App\Models\Products;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Part extends Model
{
use SoftDeletes;
protected $fillable = ['tenant_id','code','name','category_id','part_type_id','unit','attributes','description','is_active'];
public function category() {
return $this->belongsTo(CommonCode::class, 'category_id');
}
public function partType() {
return $this->belongsTo(CommonCode::class, 'part_type_id');
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Models\Products;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class PriceHistory extends Model
{
use SoftDeletes;
protected $fillable = ['tenant_id','item_type_code','item_id','price_type_code','price','started_at','ended_at'];
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Models\Products;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Product extends Model
{
use SoftDeletes;
protected $fillable = ['tenant_id','code','name','category_id','attributes','description','is_active'];
public function category() {
return $this->belongsTo(CommonCode::class, 'category_id');
}
public function boms() {
return $this->hasMany(Bom::class);
}
}

17
app/Models/Role.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
protected $fillable = [
'tenant_id', 'name', 'description'
];
public function menuPermissions()
{
return $this->hasMany(RoleMenuPermission::class, 'role_id');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class RoleMenuPermission extends Model
{
protected $fillable = [
'role_id', 'menu_id', 'access', 'read', 'write', 'export', 'approve'
];
public function role()
{
return $this->belongsTo(Role::class, 'role_id');
}
public function menu()
{
return $this->belongsTo(Menu::class, 'menu_id');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Subscription extends Model
{
use SoftDeletes;
protected $fillable = [
'tenant_id', 'plan_id', 'started_at', 'ended_at', 'status',
];
protected $dates = [
'started_at', 'ended_at',
];
public function tenant() {
return $this->belongsTo(Tenant::class);
}
public function plan() {
return $this->belongsTo(Plan::class);
}
public function payments() {
return $this->hasMany(Payment::class);
}
}

45
app/Models/Tenant.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Tenant extends Model
{
use SoftDeletes;
protected $fillable = [
'name',
'code',
'email',
'phone',
'address',
'tenant_st_code',
'plan_id',
'subscription_id',
'max_users',
'trial_ends_at',
'expires_at',
'last_paid_at',
'billing_tp_code',
];
protected $casts = [
'trial_ends_at' => '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');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UserMenuPermission extends Model
{
protected $fillable = [
'user_id', 'menu_id', 'access', 'read', 'write', 'export', 'approve'
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function menu()
{
return $this->belongsTo(Menu::class, 'menu_id');
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('common_codes', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('parts', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('files', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('price_histories', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('boms', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('bom_items', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('tenants', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('boards', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('board_settings', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('posts', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('board_comments', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('board_files', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('post_custom_field_values', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('menus', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,23 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('user_menu_permissions', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('role_menu_permissions', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('plans', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('subscriptions', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void {
Schema::create('payments', function (Blueprint $table) {
$table->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');
}
};