From 3ce7844a2a346e66f082d09cf6c90701f0189187 Mon Sep 17 00:00:00 2001 From: lion <120344285@qq.com> Date: Tue, 12 May 2026 11:12:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 3 + .../Controllers/Api/ApplicationController.php | 130 ++++++++++++++++-- config/contest.php | 7 +- 3 files changed, 126 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index 3f481ae..90b8a6d 100644 --- a/.env.example +++ b/.env.example @@ -40,6 +40,9 @@ SMS_MOCK=false # 同一手机号再次发送验证码最短间隔(秒) SMS_RESEND_INTERVAL_SECONDS=60 +# 报名表未在 JSON 中写 company_name.required_when 时:企业名称在何「参赛组别」取值下必填(须与下拉 value 一致,自定义 value 如 entry_group2 时填 entry_group2) +# ENTRY_GROUP_COMPANY_REQUIRED_VALUE=entry_group2 + MAIL_MAILER=smtp MAIL_HOST=mailpit MAIL_PORT=1025 diff --git a/app/Http/Controllers/Api/ApplicationController.php b/app/Http/Controllers/Api/ApplicationController.php index f24b675..eb12624 100644 --- a/app/Http/Controllers/Api/ApplicationController.php +++ b/app/Http/Controllers/Api/ApplicationController.php @@ -84,6 +84,102 @@ class ApplicationController extends Controller return array_values(array_filter(array_map('strval', $raw), fn (string $v) => $v !== '')); } + /** + * 报名表 schema 中 entry_group 下拉允许的「值」(与选项 `value` 一致,如 entry_group1)。 + * + * @return list + */ + private function entryGroupSelectValuesFromSignupSchema(Competition $competition): array + { + $competition->loadMissing('formSchema'); + $rows = $competition->formSchema?->schema_json; + if (! is_array($rows)) { + return []; + } + foreach ($rows as $row) { + if (! is_array($row) || ($row['key'] ?? '') !== 'entry_group') { + continue; + } + $opts = $row['options'] ?? []; + if (! is_array($opts)) { + return []; + } + $out = []; + foreach ($opts as $opt) { + if (is_array($opt) && array_key_exists('value', $opt)) { + $s = trim((string) $opt['value']); + if ($s !== '') { + $out[] = $s; + } + } elseif (is_string($opt)) { + $s = trim($opt); + if ($s !== '') { + $out[] = $s; + } + } + } + + return array_values(array_unique($out)); + } + + return []; + } + + /** + * 参赛组别取何值时企业名称必填:优先读 company_name 的 required_when,否则读 config。 + * + * @return list + */ + private function companyNameRequiredWhenEntryGroupValues(Competition $competition): array + { + $competition->loadMissing('formSchema'); + $rows = $competition->formSchema?->schema_json; + if (! is_array($rows)) { + $rows = []; + } + foreach ($rows as $row) { + if (! is_array($row) || ($row['key'] ?? '') !== 'company_name') { + continue; + } + $rw = $row['required_when'] ?? null; + if (! is_array($rw) || ($rw['field'] ?? '') !== 'entry_group') { + break; + } + $vals = $rw['values'] ?? []; + if (! is_array($vals)) { + break; + } + $out = []; + foreach ($vals as $v) { + $s = trim((string) $v); + if ($s !== '') { + $out[] = $s; + } + } + if (count($out) > 0) { + return array_values(array_unique($out)); + } + break; + } + + $cv = trim((string) config('contest.entry_group_company_required_value', '创业组')); + + return $cv !== '' ? [$cv] : []; + } + + /** + * @return list + */ + private function allowedEntryGroupValues(Competition $competition): array + { + $fromSchema = $this->entryGroupSelectValuesFromSignupSchema($competition); + if (count($fromSchema) > 0) { + return $fromSchema; + } + + return $this->entryGroupOptionValues(); + } + public function show(Request $request): JsonResponse { $app = $this->currentApplication($request); @@ -102,18 +198,23 @@ class ApplicationController extends Controller $trackCodes = $this->enabledTrackCodes($competition); $degrees = config('contest.degrees', []); $countries = config('contest.location_countries', []); - $entryGroupValues = $this->entryGroupOptionValues(); - - $trackRules = count($trackCodes) - ? ['nullable', 'string', Rule::in($trackCodes)] - : ['nullable', 'string', 'max:100']; $companyRules = ['nullable', 'string', 'max:255']; if ($this->signupSchemaHasKey($competition, 'entry_group')) { - $cv = (string) config('contest.entry_group_company_required_value', '创业组'); - $companyRules[] = 'required_if:entry_group,'.$cv; + $reqVals = $this->companyNameRequiredWhenEntryGroupValues($competition); + if (count($reqVals) > 0) { + $companyRules[] = Rule::requiredIf(function () use ($request, $reqVals): bool { + $eg = (string) $request->input('entry_group', ''); + + return in_array($eg, $reqVals, true); + }); + } } + $trackRules = count($trackCodes) + ? ['nullable', 'string', Rule::in($trackCodes)] + : ['nullable', 'string', 'max:100']; + $rules = [ 'player_name' => ['nullable', 'string', 'max:120'], 'school' => ['nullable', 'string', 'max:200'], @@ -130,7 +231,7 @@ class ApplicationController extends Controller 'intro' => ['nullable', 'string', 'max:5000'], ]; if ($this->signupSchemaHasKey($competition, 'entry_group')) { - $rules['entry_group'] = ['required', 'string', Rule::in($entryGroupValues)]; + $rules['entry_group'] = ['required', 'string', Rule::in($this->allowedEntryGroupValues($competition))]; } if ($this->signupSchemaRequiresCommitment($competition)) { $rules['commitment_accepted'] = ['sometimes', 'boolean']; @@ -191,12 +292,17 @@ class ApplicationController extends Controller $degrees = config('contest.degrees', []); $countries = config('contest.location_countries', []); - $entryGroupValues = $this->entryGroupOptionValues(); $companyRules = ['nullable', 'string', 'max:255']; if ($this->signupSchemaHasKey($competition, 'entry_group')) { - $cv = (string) config('contest.entry_group_company_required_value', '创业组'); - $companyRules[] = 'required_if:entry_group,'.$cv; + $reqVals = $this->companyNameRequiredWhenEntryGroupValues($competition); + if (count($reqVals) > 0) { + $companyRules[] = Rule::requiredIf(function () use ($request, $reqVals): bool { + $eg = (string) $request->input('entry_group', ''); + + return in_array($eg, $reqVals, true); + }); + } } $rules = [ @@ -215,7 +321,7 @@ class ApplicationController extends Controller 'intro' => ['nullable', 'string', 'max:5000'], ]; if ($this->signupSchemaHasKey($competition, 'entry_group')) { - $rules['entry_group'] = ['required', 'string', Rule::in($entryGroupValues)]; + $rules['entry_group'] = ['required', 'string', Rule::in($this->allowedEntryGroupValues($competition))]; } if ($this->signupSchemaRequiresCommitment($competition)) { $rules['commitment_accepted'] = ['required', 'accepted']; diff --git a/config/contest.php b/config/contest.php index 944a54a..fa001ad 100644 --- a/config/contest.php +++ b/config/contest.php @@ -6,8 +6,11 @@ return [ 'location_countries' => ['中国', '海外'], /** 参赛组别(报名表 entry_group 可选值,与选手端 schema 一致) */ 'entry_groups' => ['创新组', '创业组'], - /** 选择该组别时企业名称必填(须为 entry_groups 中一项) */ - 'entry_group_company_required_value' => '创业组', + /** + * 选择该组别时企业名称必填:存储的是下拉 option 的 **value**(与展示 label 可不同)。 + * 若报名表 JSON 里 company_name.required_when 已配置,则以 schema 为准;否则用此配置(无 schema 兜底时常见)。 + */ + 'entry_group_company_required_value' => env('ENTRY_GROUP_COMPANY_REQUIRED_VALUE', '创业组'), /** 单文件上限(KB);须保证 PHP upload_max_filesize、post_max_size 均大于此值(见 public/.user.ini 与 composer serve-dev) */ 'file_max_kb' => 20480, 'file_mimes' => ['pdf', 'ppt', 'pptx', 'doc', 'docx', 'wps', 'rar', 'zip'],