feat: ship creator journey v2 and profile updates
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
<?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::table('artworks', function (Blueprint $table): void {
|
||||
if (! Schema::hasColumn('artworks', 'has_missing_thumbnails')) {
|
||||
$table->boolean('has_missing_thumbnails')
|
||||
->default(false)
|
||||
->after('thumb_ext');
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('artworks', 'missing_thumbnail_variants_json')) {
|
||||
$table->json('missing_thumbnail_variants_json')
|
||||
->nullable()
|
||||
->after('has_missing_thumbnails');
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('artworks', 'thumbnails_checked_at')) {
|
||||
$table->timestamp('thumbnails_checked_at')
|
||||
->nullable()
|
||||
->after('missing_thumbnail_variants_json');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('artworks', function (Blueprint $table): void {
|
||||
if (Schema::hasColumn('artworks', 'has_missing_thumbnails')) {
|
||||
$table->index('has_missing_thumbnails', 'artworks_missing_thumbnails_idx');
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('artworks', 'thumbnails_checked_at')) {
|
||||
$table->index('thumbnails_checked_at', 'artworks_thumbnails_checked_idx');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table): void {
|
||||
try {
|
||||
$table->dropIndex('artworks_missing_thumbnails_idx');
|
||||
} catch (Throwable) {
|
||||
}
|
||||
|
||||
try {
|
||||
$table->dropIndex('artworks_thumbnails_checked_idx');
|
||||
} catch (Throwable) {
|
||||
}
|
||||
|
||||
$columns = [];
|
||||
|
||||
foreach (['has_missing_thumbnails', 'missing_thumbnail_variants_json', 'thumbnails_checked_at'] as $column) {
|
||||
if (Schema::hasColumn('artworks', $column)) {
|
||||
$columns[] = $column;
|
||||
}
|
||||
}
|
||||
|
||||
if ($columns !== []) {
|
||||
$table->dropColumn($columns);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('artwork_awards', function (Blueprint $table): void {
|
||||
$table->index(['artwork_id', 'medal'], 'artwork_awards_artwork_medal_index');
|
||||
$table->index('updated_at', 'artwork_awards_updated_at_index');
|
||||
});
|
||||
|
||||
Schema::table('artwork_award_stats', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('score_7d')->default(0)->after('score_total');
|
||||
$table->unsignedInteger('score_30d')->default(0)->after('score_7d');
|
||||
$table->timestamp('last_medaled_at')->nullable()->after('score_30d');
|
||||
|
||||
$table->index('score_total', 'artwork_award_stats_score_total_index');
|
||||
$table->index('score_30d', 'artwork_award_stats_score_30d_index');
|
||||
$table->index('last_medaled_at', 'artwork_award_stats_last_medaled_at_index');
|
||||
});
|
||||
|
||||
$this->applyWeights([
|
||||
'gold' => 5,
|
||||
'silver' => 3,
|
||||
'bronze' => 1,
|
||||
]);
|
||||
|
||||
$this->rebuildStats([
|
||||
'gold' => 5,
|
||||
'silver' => 3,
|
||||
'bronze' => 1,
|
||||
], true);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
$this->applyWeights([
|
||||
'gold' => 3,
|
||||
'silver' => 2,
|
||||
'bronze' => 1,
|
||||
]);
|
||||
|
||||
$this->rebuildStats([
|
||||
'gold' => 3,
|
||||
'silver' => 2,
|
||||
'bronze' => 1,
|
||||
], false);
|
||||
|
||||
Schema::table('artwork_award_stats', function (Blueprint $table): void {
|
||||
$table->dropIndex('artwork_award_stats_score_total_index');
|
||||
$table->dropIndex('artwork_award_stats_score_30d_index');
|
||||
$table->dropIndex('artwork_award_stats_last_medaled_at_index');
|
||||
$table->dropColumn(['score_7d', 'score_30d', 'last_medaled_at']);
|
||||
});
|
||||
|
||||
Schema::table('artwork_awards', function (Blueprint $table): void {
|
||||
$table->dropIndex('artwork_awards_artwork_medal_index');
|
||||
$table->dropIndex('artwork_awards_updated_at_index');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int> $weights
|
||||
*/
|
||||
private function applyWeights(array $weights): void
|
||||
{
|
||||
foreach ($weights as $medal => $weight) {
|
||||
DB::table('artwork_awards')
|
||||
->where('medal', $medal)
|
||||
->update(['weight' => $weight]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int> $weights
|
||||
*/
|
||||
private function rebuildStats(array $weights, bool $includeRecentColumns): void
|
||||
{
|
||||
DB::table('artwork_award_stats')->delete();
|
||||
|
||||
$rows = DB::table('artwork_awards')
|
||||
->orderBy('artwork_id')
|
||||
->get(['artwork_id', 'medal', 'updated_at']);
|
||||
|
||||
$cutoff7d = Carbon::now()->subDays(7);
|
||||
$cutoff30d = Carbon::now()->subDays(30);
|
||||
$payload = [];
|
||||
|
||||
foreach ($rows->groupBy('artwork_id') as $artworkId => $artworkRows) {
|
||||
$gold = 0;
|
||||
$silver = 0;
|
||||
$bronze = 0;
|
||||
$scoreTotal = 0;
|
||||
$score7d = 0;
|
||||
$score30d = 0;
|
||||
$lastMedaledAt = null;
|
||||
|
||||
foreach ($artworkRows as $row) {
|
||||
$medal = (string) $row->medal;
|
||||
$updatedAt = $row->updated_at ? Carbon::parse($row->updated_at) : null;
|
||||
$weight = (int) ($weights[$medal] ?? 0);
|
||||
|
||||
if ($medal === 'gold') {
|
||||
$gold++;
|
||||
} elseif ($medal === 'silver') {
|
||||
$silver++;
|
||||
} elseif ($medal === 'bronze') {
|
||||
$bronze++;
|
||||
}
|
||||
|
||||
$scoreTotal += $weight;
|
||||
|
||||
if ($updatedAt && $updatedAt->greaterThanOrEqualTo($cutoff7d)) {
|
||||
$score7d += $weight;
|
||||
}
|
||||
|
||||
if ($updatedAt && $updatedAt->greaterThanOrEqualTo($cutoff30d)) {
|
||||
$score30d += $weight;
|
||||
}
|
||||
|
||||
if ($updatedAt && ($lastMedaledAt === null || $updatedAt->greaterThan($lastMedaledAt))) {
|
||||
$lastMedaledAt = $updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
$rowPayload = [
|
||||
'artwork_id' => (int) $artworkId,
|
||||
'gold_count' => $gold,
|
||||
'silver_count' => $silver,
|
||||
'bronze_count' => $bronze,
|
||||
'score_total' => $scoreTotal,
|
||||
'updated_at' => now(),
|
||||
];
|
||||
|
||||
if ($includeRecentColumns) {
|
||||
$rowPayload['score_7d'] = $score7d;
|
||||
$rowPayload['score_30d'] = $score30d;
|
||||
$rowPayload['last_medaled_at'] = $lastMedaledAt;
|
||||
}
|
||||
|
||||
$payload[] = $rowPayload;
|
||||
}
|
||||
|
||||
foreach (array_chunk($payload, 250) as $chunk) {
|
||||
DB::table('artwork_award_stats')->insert($chunk);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('artwork_medals', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('artwork_id');
|
||||
$table->unsignedBigInteger('user_id');
|
||||
$table->enum('medal_type', ['gold', 'silver', 'bronze']);
|
||||
$table->unsignedTinyInteger('weight')->default(1);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['artwork_id', 'user_id'], 'artwork_medals_artwork_user_unique');
|
||||
$table->index(['artwork_id', 'medal_type'], 'artwork_medals_artwork_medal_type_index');
|
||||
$table->index('user_id', 'artwork_medals_user_id_index');
|
||||
$table->index('updated_at', 'artwork_medals_updated_at_index');
|
||||
|
||||
$table->foreign('artwork_id')
|
||||
->references('id')->on('artworks')
|
||||
->cascadeOnDelete();
|
||||
$table->foreign('user_id')
|
||||
->references('id')->on('users')
|
||||
->cascadeOnDelete();
|
||||
});
|
||||
|
||||
Schema::create('artwork_medal_stats', function (Blueprint $table): void {
|
||||
$table->unsignedBigInteger('artwork_id')->primary();
|
||||
$table->unsignedInteger('gold_count')->default(0);
|
||||
$table->unsignedInteger('silver_count')->default(0);
|
||||
$table->unsignedInteger('bronze_count')->default(0);
|
||||
$table->unsignedInteger('score_total')->default(0);
|
||||
$table->unsignedInteger('score_7d')->default(0);
|
||||
$table->unsignedInteger('score_30d')->default(0);
|
||||
$table->timestamp('last_medaled_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('score_total', 'artwork_medal_stats_score_total_index');
|
||||
$table->index('score_30d', 'artwork_medal_stats_score_30d_index');
|
||||
$table->index('last_medaled_at', 'artwork_medal_stats_last_medaled_at_index');
|
||||
|
||||
$table->foreign('artwork_id')
|
||||
->references('id')->on('artworks')
|
||||
->cascadeOnDelete();
|
||||
});
|
||||
|
||||
$this->copyExistingMedals();
|
||||
$this->copyExistingMedalStats();
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('artwork_medal_stats');
|
||||
Schema::dropIfExists('artwork_medals');
|
||||
}
|
||||
|
||||
private function copyExistingMedals(): void
|
||||
{
|
||||
if (! Schema::hasTable('artwork_awards')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rows = DB::table('artwork_awards')
|
||||
->orderBy('id')
|
||||
->get(['artwork_id', 'user_id', 'medal', 'weight', 'created_at', 'updated_at']);
|
||||
|
||||
foreach ($rows->chunk(250) as $chunk) {
|
||||
DB::table('artwork_medals')->insert($chunk->map(static function ($row): array {
|
||||
return [
|
||||
'artwork_id' => (int) $row->artwork_id,
|
||||
'user_id' => (int) $row->user_id,
|
||||
'medal_type' => (string) $row->medal,
|
||||
'weight' => (int) $row->weight,
|
||||
'created_at' => $row->created_at,
|
||||
'updated_at' => $row->updated_at,
|
||||
];
|
||||
})->all());
|
||||
}
|
||||
}
|
||||
|
||||
private function copyExistingMedalStats(): void
|
||||
{
|
||||
if (! Schema::hasTable('artwork_award_stats')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rows = DB::table('artwork_award_stats')->get();
|
||||
|
||||
foreach ($rows->chunk(250) as $chunk) {
|
||||
DB::table('artwork_medal_stats')->insert($chunk->map(static function ($row): array {
|
||||
$updatedAt = $row->updated_at ?? now();
|
||||
|
||||
return [
|
||||
'artwork_id' => (int) $row->artwork_id,
|
||||
'gold_count' => (int) ($row->gold_count ?? 0),
|
||||
'silver_count' => (int) ($row->silver_count ?? 0),
|
||||
'bronze_count' => (int) ($row->bronze_count ?? 0),
|
||||
'score_total' => (int) ($row->score_total ?? 0),
|
||||
'score_7d' => (int) ($row->score_7d ?? 0),
|
||||
'score_30d' => (int) ($row->score_30d ?? 0),
|
||||
'last_medaled_at' => $row->last_medaled_at ?? null,
|
||||
'created_at' => $updatedAt,
|
||||
'updated_at' => $updatedAt,
|
||||
];
|
||||
})->all());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (Schema::hasColumn('artwork_features', 'force_hero')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('artwork_features', function (Blueprint $table): void {
|
||||
$table->boolean('force_hero')->default(false)->after('is_active');
|
||||
$table->index(['force_hero', 'is_active'], 'idx_features_force_hero');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (! Schema::hasColumn('artwork_features', 'force_hero')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('artwork_features', function (Blueprint $table): void {
|
||||
$table->dropIndex('idx_features_force_hero');
|
||||
$table->dropColumn('force_hero');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasTable('artworks')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('artworks', function (Blueprint $table): void {
|
||||
if (! Schema::hasColumn('artworks', 'maturity_level')) {
|
||||
$table->string('maturity_level', 24)->default('safe')->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_source')) {
|
||||
$table->string('maturity_source', 24)->default('legacy')->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_status')) {
|
||||
$table->string('maturity_status', 24)->default('clear')->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_score')) {
|
||||
$table->decimal('maturity_ai_score', 5, 4)->nullable()->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_labels')) {
|
||||
$table->json('maturity_ai_labels')->nullable();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_detected_at')) {
|
||||
$table->timestamp('maturity_ai_detected_at')->nullable()->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_flagged_at')) {
|
||||
$table->timestamp('maturity_flagged_at')->nullable()->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_flag_reason')) {
|
||||
$table->string('maturity_flag_reason', 255)->nullable();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_reviewed_by')) {
|
||||
$table->unsignedBigInteger('maturity_reviewed_by')->nullable()->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_reviewed_at')) {
|
||||
$table->timestamp('maturity_reviewed_at')->nullable()->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_reviewer_note')) {
|
||||
$table->text('maturity_reviewer_note')->nullable();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_mismatch_count')) {
|
||||
$table->unsignedInteger('maturity_mismatch_count')->default(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (! Schema::hasTable('artworks')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('artworks', function (Blueprint $table): void {
|
||||
foreach ([
|
||||
'maturity_level',
|
||||
'maturity_source',
|
||||
'maturity_status',
|
||||
'maturity_ai_score',
|
||||
'maturity_ai_labels',
|
||||
'maturity_ai_detected_at',
|
||||
'maturity_flagged_at',
|
||||
'maturity_flag_reason',
|
||||
'maturity_reviewed_by',
|
||||
'maturity_reviewed_at',
|
||||
'maturity_reviewer_note',
|
||||
'maturity_mismatch_count',
|
||||
] as $column) {
|
||||
if (Schema::hasColumn('artworks', $column)) {
|
||||
$table->dropColumn($column);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasTable('user_profiles')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('user_profiles', function (Blueprint $table): void {
|
||||
if (! Schema::hasColumn('user_profiles', 'mature_content_visibility')) {
|
||||
$table->string('mature_content_visibility', 12)->default('blur');
|
||||
}
|
||||
if (! Schema::hasColumn('user_profiles', 'mature_content_warning_enabled')) {
|
||||
$table->boolean('mature_content_warning_enabled')->default(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (! Schema::hasTable('user_profiles')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('user_profiles', function (Blueprint $table): void {
|
||||
foreach (['mature_content_visibility', 'mature_content_warning_enabled'] as $column) {
|
||||
if (Schema::hasColumn('user_profiles', $column)) {
|
||||
$table->dropColumn($column);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasTable('artworks')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('artworks', function (Blueprint $table): void {
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_label')) {
|
||||
$table->string('maturity_ai_label', 24)->nullable()->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_confidence')) {
|
||||
$table->decimal('maturity_ai_confidence', 5, 4)->nullable()->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_model')) {
|
||||
$table->string('maturity_ai_model', 120)->nullable();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_threshold_used')) {
|
||||
$table->decimal('maturity_ai_threshold_used', 5, 4)->nullable();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_analysis_time_ms')) {
|
||||
$table->unsignedInteger('maturity_ai_analysis_time_ms')->nullable();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_action_hint')) {
|
||||
$table->string('maturity_ai_action_hint', 32)->nullable()->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_advisory')) {
|
||||
$table->text('maturity_ai_advisory')->nullable();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_ai_status')) {
|
||||
$table->string('maturity_ai_status', 24)->default('not_requested')->index();
|
||||
}
|
||||
if (! Schema::hasColumn('artworks', 'maturity_declared_at')) {
|
||||
$table->timestamp('maturity_declared_at')->nullable()->index();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (! Schema::hasTable('artworks')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('artworks', function (Blueprint $table): void {
|
||||
foreach ([
|
||||
'maturity_ai_label',
|
||||
'maturity_ai_confidence',
|
||||
'maturity_ai_model',
|
||||
'maturity_ai_threshold_used',
|
||||
'maturity_ai_analysis_time_ms',
|
||||
'maturity_ai_action_hint',
|
||||
'maturity_ai_advisory',
|
||||
'maturity_ai_status',
|
||||
'maturity_declared_at',
|
||||
] as $column) {
|
||||
if (Schema::hasColumn('artworks', $column)) {
|
||||
$table->dropColumn($column);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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(): void
|
||||
{
|
||||
Schema::table('content_types', function (Blueprint $table): void {
|
||||
$table->string('mascot_path')->nullable()->after('description');
|
||||
$table->string('cover_art_path')->nullable()->after('mascot_path');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('content_types', function (Blueprint $table): void {
|
||||
$table->dropColumn(['mascot_path', 'cover_art_path']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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('content_type_slug_histories', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->foreignId('content_type_id')->constrained('content_types')->cascadeOnDelete();
|
||||
$table->string('old_slug', 64)->unique();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('content_type_id');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('content_type_slug_histories');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
if (Schema::hasTable('artwork_maturity_audit_findings')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::create('artwork_maturity_audit_findings', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->foreignId('artwork_id')->constrained('artworks')->cascadeOnDelete();
|
||||
$table->string('status', 24)->default('open')->index();
|
||||
$table->string('thumbnail_variant', 24)->nullable();
|
||||
$table->string('ai_label', 24)->nullable()->index();
|
||||
$table->decimal('ai_confidence', 5, 4)->nullable()->index();
|
||||
$table->decimal('ai_score', 5, 4)->nullable()->index();
|
||||
$table->json('ai_labels')->nullable();
|
||||
$table->string('ai_model', 120)->nullable();
|
||||
$table->decimal('ai_threshold_used', 5, 4)->nullable();
|
||||
$table->unsignedInteger('ai_analysis_time_ms')->nullable();
|
||||
$table->string('ai_action_hint', 32)->nullable()->index();
|
||||
$table->string('ai_status', 24)->default('not_requested')->index();
|
||||
$table->text('ai_advisory')->nullable();
|
||||
$table->timestamp('detected_at')->nullable()->index();
|
||||
$table->timestamp('last_scanned_at')->nullable()->index();
|
||||
$table->string('resolution_action', 32)->nullable();
|
||||
$table->text('resolution_note')->nullable();
|
||||
$table->foreignId('resolved_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamp('resolved_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique('artwork_id', 'artwork_maturity_audit_findings_artwork_unique');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('artwork_maturity_audit_findings');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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::table('content_types', function (Blueprint $table): void {
|
||||
$table->boolean('hide_from_menu')->default(false)->after('order');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('content_types', function (Blueprint $table): void {
|
||||
$table->dropColumn('hide_from_menu');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
if (Schema::hasTable('artwork_relations')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::create('artwork_relations', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->foreignId('source_artwork_id')->constrained('artworks')->cascadeOnDelete();
|
||||
$table->foreignId('target_artwork_id')->constrained('artworks')->cascadeOnDelete();
|
||||
$table->string('relation_type', 32)->default('remake_of')->index();
|
||||
$table->text('note')->nullable();
|
||||
$table->unsignedSmallInteger('sort_order')->default(0);
|
||||
$table->foreignId('created_by_user_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique([
|
||||
'source_artwork_id',
|
||||
'target_artwork_id',
|
||||
'relation_type',
|
||||
], 'artwork_relations_unique_pair_type');
|
||||
$table->index(['source_artwork_id', 'sort_order'], 'artwork_relations_source_sort_idx');
|
||||
$table->index(['target_artwork_id', 'relation_type'], 'artwork_relations_target_type_idx');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('artwork_relations');
|
||||
}
|
||||
};
|
||||
@@ -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(): void
|
||||
{
|
||||
Schema::create('creator_milestones', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->string('type', 64);
|
||||
$table->timestamp('occurred_at');
|
||||
$table->unsignedSmallInteger('occurred_year')->nullable();
|
||||
$table->foreignId('related_artwork_id')->nullable()->constrained('artworks')->nullOnDelete();
|
||||
$table->boolean('is_public')->default(true);
|
||||
$table->unsignedTinyInteger('priority')->default(0);
|
||||
$table->json('payload_json')->nullable();
|
||||
$table->timestamp('computed_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['user_id', 'occurred_at'], 'creator_milestones_user_occurred_idx');
|
||||
$table->index(['user_id', 'type'], 'creator_milestones_user_type_idx');
|
||||
$table->index(['user_id', 'is_public'], 'creator_milestones_user_public_idx');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('creator_milestones');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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('creator_eras', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('user_id');
|
||||
$table->string('era_type', 48); // early_years | breakthrough | experimental | comeback | current
|
||||
$table->string('title', 100);
|
||||
$table->string('description', 300)->nullable();
|
||||
$table->timestamp('starts_at');
|
||||
$table->timestamp('ends_at')->nullable();
|
||||
$table->boolean('is_current')->default(false);
|
||||
$table->json('metadata')->nullable(); // uploads_count, featured_count, top_categories, top_artwork_id, dominant_years
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete();
|
||||
$table->index(['user_id', 'starts_at']);
|
||||
$table->index(['user_id', 'is_current']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('creator_eras');
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user