feat: ship creator journey v2 and profile updates
This commit is contained in:
@@ -52,6 +52,9 @@ class Artwork extends Model
|
||||
'hash',
|
||||
'file_ext',
|
||||
'thumb_ext',
|
||||
'has_missing_thumbnails',
|
||||
'missing_thumbnail_variants_json',
|
||||
'thumbnails_checked_at',
|
||||
'file_size',
|
||||
'mime_type',
|
||||
'width',
|
||||
@@ -60,6 +63,27 @@ class Artwork extends Model
|
||||
'visibility',
|
||||
'is_approved',
|
||||
'is_mature',
|
||||
'maturity_level',
|
||||
'maturity_source',
|
||||
'maturity_status',
|
||||
'maturity_ai_score',
|
||||
'maturity_ai_labels',
|
||||
'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_ai_detected_at',
|
||||
'maturity_declared_at',
|
||||
'maturity_flagged_at',
|
||||
'maturity_flag_reason',
|
||||
'maturity_reviewed_by',
|
||||
'maturity_reviewed_at',
|
||||
'maturity_reviewer_note',
|
||||
'maturity_mismatch_count',
|
||||
'published_at',
|
||||
'hash',
|
||||
'thumb_ext',
|
||||
@@ -90,7 +114,28 @@ class Artwork extends Model
|
||||
'visibility' => 'string',
|
||||
'is_approved' => 'boolean',
|
||||
'is_mature' => 'boolean',
|
||||
'maturity_level' => 'string',
|
||||
'maturity_source' => 'string',
|
||||
'maturity_status' => 'string',
|
||||
'maturity_ai_score' => 'float',
|
||||
'maturity_ai_labels' => 'array',
|
||||
'maturity_ai_label' => 'string',
|
||||
'maturity_ai_confidence' => 'float',
|
||||
'maturity_ai_model' => 'string',
|
||||
'maturity_ai_threshold_used' => 'float',
|
||||
'maturity_ai_analysis_time_ms' => 'integer',
|
||||
'maturity_ai_action_hint' => 'string',
|
||||
'maturity_ai_advisory' => 'string',
|
||||
'maturity_ai_status' => 'string',
|
||||
'maturity_ai_detected_at' => 'datetime',
|
||||
'maturity_declared_at' => 'datetime',
|
||||
'maturity_flagged_at' => 'datetime',
|
||||
'maturity_reviewed_at' => 'datetime',
|
||||
'maturity_mismatch_count' => 'integer',
|
||||
'has_missing_thumbnails' => 'boolean',
|
||||
'published_at' => 'datetime',
|
||||
'missing_thumbnail_variants_json' => 'array',
|
||||
'thumbnails_checked_at' => 'datetime',
|
||||
'published_as_type' => 'string',
|
||||
'published_as_id' => 'integer',
|
||||
'publish_at' => 'datetime',
|
||||
@@ -184,6 +229,11 @@ class Artwork extends Model
|
||||
return $this->belongsTo(Group::class);
|
||||
}
|
||||
|
||||
public function maturityAuditFinding(): HasOne
|
||||
{
|
||||
return $this->hasOne(ArtworkMaturityAuditFinding::class);
|
||||
}
|
||||
|
||||
public function uploadedBy(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'uploaded_by_user_id');
|
||||
@@ -297,12 +347,31 @@ class Artwork extends Model
|
||||
return $this->hasMany(ArtworkAward::class);
|
||||
}
|
||||
|
||||
public function medals(): HasMany
|
||||
{
|
||||
return $this->hasMany(ArtworkMedal::class, 'artwork_id');
|
||||
}
|
||||
|
||||
/** All file versions for this artwork (oldest first). */
|
||||
public function versions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ArtworkVersion::class)->orderBy('version_number');
|
||||
}
|
||||
|
||||
public function outgoingEvolutionRelations(): HasMany
|
||||
{
|
||||
return $this->hasMany(ArtworkRelation::class, 'source_artwork_id')
|
||||
->orderBy('sort_order')
|
||||
->orderBy('id');
|
||||
}
|
||||
|
||||
public function incomingEvolutionRelations(): HasMany
|
||||
{
|
||||
return $this->hasMany(ArtworkRelation::class, 'target_artwork_id')
|
||||
->orderByDesc('updated_at')
|
||||
->orderByDesc('id');
|
||||
}
|
||||
|
||||
/** The currently active version record. */
|
||||
public function currentVersion(): BelongsTo
|
||||
{
|
||||
@@ -319,6 +388,11 @@ class Artwork extends Model
|
||||
return $this->hasOne(ArtworkAwardStat::class);
|
||||
}
|
||||
|
||||
public function medalStats(): HasOne
|
||||
{
|
||||
return $this->hasOne(ArtworkMedalStat::class, 'artwork_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Meilisearch document for this artwork.
|
||||
* Includes all fields required for search, filtering, sorting, and display.
|
||||
@@ -385,12 +459,20 @@ class Artwork extends Model
|
||||
'published_at_ts' => $publishedSortAt?->getTimestamp() ?? 0,
|
||||
'is_public' => (bool) $this->is_public,
|
||||
'is_approved' => (bool) $this->is_approved,
|
||||
'is_mature' => (bool) $this->is_mature,
|
||||
'is_mature_effective' => (bool) ($this->is_mature || $this->maturity_level === 'mature' || $this->maturity_status === 'suspected'),
|
||||
'maturity_level' => (string) ($this->maturity_level ?? 'safe'),
|
||||
'maturity_status' => (string) ($this->maturity_status ?? 'clear'),
|
||||
'has_missing_thumbnails' => (bool) ($this->has_missing_thumbnails ?? false),
|
||||
'missing_thumbnail_rank' => (int) (($this->has_missing_thumbnails ?? false) ? 1 : 0),
|
||||
// ── Trending / discovery fields ────────────────────────────────────
|
||||
'trending_score_1h' => (float) ($this->trending_score_1h ?? 0),
|
||||
'trending_score_24h' => (float) ($this->trending_score_24h ?? 0),
|
||||
'trending_score_7d' => (float) ($this->trending_score_7d ?? 0),
|
||||
'favorites_count' => (int) ($stat?->favorites ?? 0),
|
||||
'awards_received_count' => (int) ($awardStat?->score_total ?? 0),
|
||||
'awards_score_7d' => (int) ($awardStat?->score_7d ?? 0),
|
||||
'awards_score_30d' => (int) ($awardStat?->score_30d ?? 0),
|
||||
'downloads_count' => (int) ($stat?->downloads ?? 0),
|
||||
// ── Ranking V2 fields ───────────────────────────────────────────────
|
||||
'ranking_score' => (float) ($stat?->ranking_score ?? 0),
|
||||
@@ -404,6 +486,8 @@ class Artwork extends Model
|
||||
'silver' => $awardStat?->silver_count ?? 0,
|
||||
'bronze' => $awardStat?->bronze_count ?? 0,
|
||||
'score' => $awardStat?->score_total ?? 0,
|
||||
'score_7d' => $awardStat?->score_7d ?? 0,
|
||||
'score_30d' => $awardStat?->score_30d ?? 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -432,6 +516,32 @@ class Artwork extends Model
|
||||
->where("{$table}.published_at", '<=', now());
|
||||
}
|
||||
|
||||
public function scopeSafeForGeneralAudience(Builder $query): Builder
|
||||
{
|
||||
$table = $this->getTable();
|
||||
|
||||
return $query
|
||||
->whereRaw('COALESCE(' . $table . '.is_mature, 0) = 0')
|
||||
->whereRaw("COALESCE(" . $table . ".maturity_status, 'clear') != ?", ['suspected']);
|
||||
}
|
||||
|
||||
public function scopeWithoutMissingThumbnails(Builder $query): Builder
|
||||
{
|
||||
$table = $this->getTable();
|
||||
|
||||
return $query->where(function (Builder $thumbnailQuery) use ($table): void {
|
||||
$thumbnailQuery->whereNull("{$table}.has_missing_thumbnails")
|
||||
->orWhere("{$table}.has_missing_thumbnails", false);
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeOrderMissingThumbnailsLast(Builder $query): Builder
|
||||
{
|
||||
$table = $this->getTable();
|
||||
|
||||
return $query->orderByRaw("CASE WHEN {$table}.has_missing_thumbnails = 1 THEN 1 ELSE 0 END ASC");
|
||||
}
|
||||
|
||||
public function getRouteKeyName(): string
|
||||
{
|
||||
return 'slug';
|
||||
|
||||
Reference in New Issue
Block a user