'datetime', 'submitted_at' => 'datetime', ]; public function user(): BelongsTo { return $this->belongsTo(User::class); } public function competition(): BelongsTo { return $this->belongsTo(Competition::class); } public function files(): HasMany { return $this->hasMany(ApplicationFile::class); } public function reviewRecords(): HasMany { return $this->hasMany(ApplicationReviewRecord::class); } public function reviewScores(): HasMany { return $this->hasMany(ApplicationReviewScore::class); } public function isSubmitted(): bool { return $this->status === 'submitted'; } /** 评委已产生评审记录或打分后,选手端不可再修改报名/附件 */ public function isSignupLockedByReview(): bool { return $this->reviewRecords()->exists() || $this->reviewScores()->exists(); } /** 已超过赛事设置的报名截止时间(未设置截止时间则不据此拦截) */ public function isPastSignupCloseForCompetition(): bool { $this->loadMissing('competition'); $close = $this->competition?->signup_close_at; return $close !== null && now()->isAfter($close); } /** * 选手是否仍可编辑报名表:报名截止时间内且评委尚未评审;草稿与已提交均可继续改报直至截止或评审落库。 */ public function participantMayEditSignup(): bool { if ($this->isSignupLockedByReview()) { return false; } return ! $this->isPastSignupCloseForCompetition(); } /** * @param 'status'|'file' $field 422 时 errors 字段名(报名表用 status,附件接口用 file) */ public function assertMayEditSignup(string $field = 'status'): void { if ($this->isSignupLockedByReview()) { $msg = $field === 'file' ? '已有评委评审或打分记录,不可再上传或删除附件' : '已有评委评审或打分记录,报名内容不可再修改'; throw ValidationException::withMessages([$field => [$msg]]); } if ($this->isPastSignupCloseForCompetition()) { $msg = $field === 'file' ? '报名已截止,不可再上传或删除附件' : '报名已截止,无法再修改或提交报名'; throw ValidationException::withMessages([$field => [$msg]]); } } }