|
|
<?php
|
|
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
|
|
use App\Http\Controllers\Api\Concerns\ResolvesParticipantApplication;
|
|
|
use App\Http\Controllers\Controller;
|
|
|
use App\Models\Application;
|
|
|
use App\Models\Competition;
|
|
|
use App\Models\SignupChannel;
|
|
|
use App\Support\ChannelCallbackUrlBuilder;
|
|
|
use App\Support\SignupFormFileRules;
|
|
|
use Illuminate\Http\JsonResponse;
|
|
|
use Illuminate\Http\Request;
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
use Illuminate\Validation\Rule;
|
|
|
use Illuminate\Validation\ValidationException;
|
|
|
use Throwable;
|
|
|
|
|
|
class ApplicationController extends Controller
|
|
|
{
|
|
|
use ResolvesParticipantApplication;
|
|
|
|
|
|
private const USER_PREFILL_SOURCES = [
|
|
|
'user.name' => 'name',
|
|
|
'user.mobile' => 'mobile',
|
|
|
'user.email' => 'email',
|
|
|
];
|
|
|
|
|
|
private function ensureParticipantMayEditSignup(Application $app): void
|
|
|
{
|
|
|
$app->assertMayEditSignup('status');
|
|
|
}
|
|
|
|
|
|
private function currentApplication(Request $request): Application
|
|
|
{
|
|
|
return $this->participantApplication($request);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return list<string>
|
|
|
*/
|
|
|
private function enabledTrackCodes(Competition $competition): array
|
|
|
{
|
|
|
return $competition->tracks()
|
|
|
->where('is_enabled', true)
|
|
|
->pluck('track_code')
|
|
|
->all();
|
|
|
}
|
|
|
|
|
|
/** 报名表 schema 是否包含参赛承诺书勾选(与选手端 key 一致) */
|
|
|
private function signupSchemaRequiresCommitment(Competition $competition): bool
|
|
|
{
|
|
|
$competition->loadMissing('formSchema');
|
|
|
$rows = $competition->formSchema?->schema_json;
|
|
|
if (! is_array($rows) || count($rows) === 0) {
|
|
|
return true;
|
|
|
}
|
|
|
foreach ($rows as $row) {
|
|
|
if (is_array($row) && ($row['key'] ?? '') === 'commitment_accepted') {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/** 当前赛事报名表 schema 是否包含指定字段 key */
|
|
|
private function signupSchemaHasKey(Competition $competition, string $key): bool
|
|
|
{
|
|
|
$competition->loadMissing('formSchema');
|
|
|
$rows = $competition->formSchema?->schema_json;
|
|
|
if (! is_array($rows) || count($rows) === 0) {
|
|
|
// 与选手端 normalizeSignupSchema([]) 兜底 DEFAULT_SIGNUP_FORM_SCHEMA 一致(含 entry_group)
|
|
|
return $key === 'entry_group';
|
|
|
}
|
|
|
foreach ($rows as $row) {
|
|
|
if (is_array($row) && ($row['key'] ?? '') === $key) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return list<string>
|
|
|
*/
|
|
|
private function entryGroupOptionValues(): array
|
|
|
{
|
|
|
$raw = config('contest.entry_groups', ['创新组', '创业组']);
|
|
|
if (! is_array($raw) || count($raw) === 0) {
|
|
|
return ['创新组', '创业组'];
|
|
|
}
|
|
|
|
|
|
return array_values(array_filter(array_map('strval', $raw), fn (string $v) => $v !== ''));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 报名表 schema 中 entry_group 下拉允许的「值」(与选项 `value` 一致,如 entry_group1)。
|
|
|
*
|
|
|
* @return list<string>
|
|
|
*/
|
|
|
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<string>
|
|
|
*/
|
|
|
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<string>
|
|
|
*/
|
|
|
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);
|
|
|
$app->load(['files']);
|
|
|
|
|
|
return response()->json($this->transform($app));
|
|
|
}
|
|
|
|
|
|
public function update(Request $request): JsonResponse
|
|
|
{
|
|
|
$competition = $this->resolvePublishedCompetitionFromRequest($request);
|
|
|
$app = $this->currentApplication($request);
|
|
|
|
|
|
$this->ensureParticipantMayEditSignup($app);
|
|
|
|
|
|
$trackCodes = $this->enabledTrackCodes($competition);
|
|
|
$degrees = config('contest.degrees', []);
|
|
|
$countries = config('contest.location_countries', []);
|
|
|
|
|
|
$companyRules = ['nullable', 'string', 'max:255'];
|
|
|
if ($this->signupSchemaHasKey($competition, 'entry_group')) {
|
|
|
$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'],
|
|
|
'degree' => ['nullable', 'string', Rule::in($degrees)],
|
|
|
'contact_email' => ['nullable', 'email', 'max:255'],
|
|
|
'contact_mobile' => ['nullable', 'regex:/^1[3-9]\d{9}$/'],
|
|
|
'company_name' => $companyRules,
|
|
|
'project_name' => ['nullable', 'string', 'max:255'],
|
|
|
'track' => $trackRules,
|
|
|
'location_country' => ['nullable', 'string', Rule::in($countries)],
|
|
|
'location_province' => ['nullable', 'string', 'max:100'],
|
|
|
'location_city' => ['nullable', 'string', 'max:100'],
|
|
|
'oversea_country' => ['nullable', 'string', 'max:100'],
|
|
|
'intro' => ['nullable', 'string', 'max:5000'],
|
|
|
];
|
|
|
if ($this->signupSchemaHasKey($competition, 'entry_group')) {
|
|
|
$rules['entry_group'] = ['required', 'string', Rule::in($this->allowedEntryGroupValues($competition))];
|
|
|
}
|
|
|
if ($this->signupSchemaRequiresCommitment($competition)) {
|
|
|
$rules['commitment_accepted'] = ['sometimes', 'boolean'];
|
|
|
$rules['promise_signature'] = ['nullable', 'string', 'max:2097152'];
|
|
|
}
|
|
|
|
|
|
$data = $request->validate($rules);
|
|
|
|
|
|
$commitmentAccepted = $data['commitment_accepted'] ?? null;
|
|
|
$promiseSignature = $data['promise_signature'] ?? null;
|
|
|
unset($data['commitment_accepted'], $data['promise_signature']);
|
|
|
|
|
|
$app->fill($data);
|
|
|
|
|
|
if ($this->signupSchemaRequiresCommitment($competition)) {
|
|
|
if ($commitmentAccepted === true) {
|
|
|
if (! is_string($promiseSignature) || trim($promiseSignature) === '') {
|
|
|
throw ValidationException::withMessages([
|
|
|
'promise_signature' => ['请完成手写签名后再保存'],
|
|
|
]);
|
|
|
}
|
|
|
$app->promise_signed_at = now();
|
|
|
$app->promise_signature = $promiseSignature;
|
|
|
} elseif ($commitmentAccepted === false) {
|
|
|
$app->promise_signed_at = null;
|
|
|
$app->promise_signature = null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (! empty($data['player_name']) && empty($request->user()->name)) {
|
|
|
$request->user()->update(['name' => $data['player_name']]);
|
|
|
}
|
|
|
|
|
|
if (! empty($data['contact_email']) && empty($request->user()->email)) {
|
|
|
$request->user()->update(['email' => $data['contact_email']]);
|
|
|
}
|
|
|
|
|
|
$app->save();
|
|
|
|
|
|
$app->load(['files']);
|
|
|
|
|
|
return response()->json($this->transform($app));
|
|
|
}
|
|
|
|
|
|
public function submit(Request $request): JsonResponse
|
|
|
{
|
|
|
$competition = $this->resolvePublishedCompetitionFromRequest($request);
|
|
|
$app = $this->currentApplication($request);
|
|
|
|
|
|
$this->ensureParticipantMayEditSignup($app);
|
|
|
|
|
|
$trackCodes = $this->enabledTrackCodes($competition);
|
|
|
if (count($trackCodes) === 0) {
|
|
|
throw ValidationException::withMessages([
|
|
|
'track' => ['本场赛事尚未配置可用赛道,请联系管理员'],
|
|
|
]);
|
|
|
}
|
|
|
|
|
|
$degrees = config('contest.degrees', []);
|
|
|
$countries = config('contest.location_countries', []);
|
|
|
|
|
|
$companyRules = ['nullable', 'string', 'max:255'];
|
|
|
if ($this->signupSchemaHasKey($competition, 'entry_group')) {
|
|
|
$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 = [
|
|
|
'player_name' => ['required', 'string', 'max:120'],
|
|
|
'school' => ['required', 'string', 'max:200'],
|
|
|
'degree' => ['required', 'string', Rule::in($degrees)],
|
|
|
'contact_email' => ['required', 'email', 'max:255'],
|
|
|
'contact_mobile' => ['required', 'regex:/^1[3-9]\d{9}$/'],
|
|
|
'company_name' => $companyRules,
|
|
|
'project_name' => ['required', 'string', 'max:255'],
|
|
|
'track' => ['required', 'string', Rule::in($trackCodes)],
|
|
|
'location_country' => ['required', 'string', Rule::in($countries)],
|
|
|
'location_province' => ['nullable', 'string', 'max:100', 'required_if:location_country,中国'],
|
|
|
'location_city' => ['nullable', 'string', 'max:100', 'required_if:location_country,中国'],
|
|
|
'oversea_country' => ['nullable', 'string', 'max:100', 'required_if:location_country,海外'],
|
|
|
'intro' => ['nullable', 'string', 'max:5000'],
|
|
|
];
|
|
|
if ($this->signupSchemaHasKey($competition, 'entry_group')) {
|
|
|
$rules['entry_group'] = ['required', 'string', Rule::in($this->allowedEntryGroupValues($competition))];
|
|
|
}
|
|
|
if ($this->signupSchemaRequiresCommitment($competition)) {
|
|
|
$rules['commitment_accepted'] = ['required', 'accepted'];
|
|
|
$rules['promise_signature'] = ['required', 'string', 'max:2097152'];
|
|
|
}
|
|
|
|
|
|
$data = $request->validate($rules);
|
|
|
|
|
|
$promiseSignature = $data['promise_signature'] ?? '';
|
|
|
unset($data['commitment_accepted'], $data['promise_signature']);
|
|
|
|
|
|
$planCount = $app->files()->where('kind', 'plan')->count();
|
|
|
if ($planCount < 1) {
|
|
|
throw ValidationException::withMessages([
|
|
|
'files' => ['请至少上传一份商业计划书'],
|
|
|
]);
|
|
|
}
|
|
|
$planRow = SignupFormFileRules::fileFieldRow($competition, 'plan');
|
|
|
$maxPlanFiles = SignupFormFileRules::maxCount($planRow);
|
|
|
if ($maxPlanFiles !== null && $planCount > $maxPlanFiles) {
|
|
|
throw ValidationException::withMessages([
|
|
|
'files' => ['商业计划书最多可上传 '.$maxPlanFiles.' 个文件'],
|
|
|
]);
|
|
|
}
|
|
|
if ($this->signupSchemaHasKey($competition, 'supporting')) {
|
|
|
$supportingCount = $app->files()->where('kind', 'supporting')->count();
|
|
|
$supRow = SignupFormFileRules::fileFieldRow($competition, 'supporting');
|
|
|
$maxSup = SignupFormFileRules::maxCount($supRow);
|
|
|
if ($maxSup !== null && $supportingCount > $maxSup) {
|
|
|
throw ValidationException::withMessages([
|
|
|
'files' => ['佐证材料最多可上传 '.$maxSup.' 个文件'],
|
|
|
]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$app->fill($data);
|
|
|
if ($this->signupSchemaRequiresCommitment($competition)) {
|
|
|
if (! is_string($promiseSignature) || trim($promiseSignature) === '') {
|
|
|
throw ValidationException::withMessages([
|
|
|
'promise_signature' => ['请完成参赛承诺书手写签名'],
|
|
|
]);
|
|
|
}
|
|
|
$app->promise_signature = $promiseSignature;
|
|
|
$app->promise_signed_at = now();
|
|
|
}
|
|
|
$app->status = 'submitted';
|
|
|
$app->submitted_at = now();
|
|
|
$app->save();
|
|
|
|
|
|
$request->user()->update([
|
|
|
'name' => $data['player_name'],
|
|
|
'email' => $data['contact_email'],
|
|
|
]);
|
|
|
|
|
|
$app->load(['files']);
|
|
|
|
|
|
$response = $this->transform($app);
|
|
|
$successNotice = $this->successNotice($competition);
|
|
|
if ($successNotice !== null) {
|
|
|
$response['success_notice'] = $successNotice;
|
|
|
}
|
|
|
$channelCallback = $this->channelCallback($app, $request);
|
|
|
if ($channelCallback !== null) {
|
|
|
$response['channel_callback'] = $channelCallback;
|
|
|
}
|
|
|
|
|
|
return response()->json($response);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return array{enabled: true, message: string}|null
|
|
|
*/
|
|
|
private function successNotice(Competition $competition): ?array
|
|
|
{
|
|
|
$settings = is_array($competition->settings) ? $competition->settings : [];
|
|
|
$notice = $settings['success_notice'] ?? null;
|
|
|
if (! is_array($notice)) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
if (($notice['enabled'] ?? false) !== true) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
$message = trim((string) ($notice['message'] ?? ''));
|
|
|
if ($message === '') {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
'enabled' => true,
|
|
|
'message' => mb_substr($message, 0, 1000),
|
|
|
];
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return array<string, string>|null
|
|
|
*/
|
|
|
private function channelCallback(Application $app, Request $request): ?array
|
|
|
{
|
|
|
if ($app->signup_channel_id === null && trim((string) $app->signup_channel_code) === '') {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
$context = [
|
|
|
'channel_code' => $app->signup_channel_code,
|
|
|
'competition_id' => $app->competition_id,
|
|
|
'mobile_masked' => $this->maskMobile((string) $request->user()?->mobile),
|
|
|
'state' => $app->signup_channel_state,
|
|
|
'user_id' => $app->user_id,
|
|
|
'application_id' => $app->id,
|
|
|
'result' => 'failed',
|
|
|
'error_code' => null,
|
|
|
'client_ip' => $request->ip(),
|
|
|
'callback_result' => 'failed',
|
|
|
'created_at' => now()->toIso8601String(),
|
|
|
];
|
|
|
|
|
|
try {
|
|
|
$channel = $app->signup_channel_id !== null
|
|
|
? SignupChannel::query()->find($app->signup_channel_id)
|
|
|
: null;
|
|
|
if ($channel === null && trim((string) $app->signup_channel_code) !== '') {
|
|
|
$channel = SignupChannel::query()
|
|
|
->where('channel_code', $app->signup_channel_code)
|
|
|
->first();
|
|
|
}
|
|
|
if ($channel === null || $channel->competition_id !== $app->competition_id) {
|
|
|
$context['error_code'] = 'CHANNEL_NOT_FOUND';
|
|
|
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
$context['channel_code'] = $channel->channel_code;
|
|
|
if ($channel->status !== SignupChannel::STATUS_ENABLED) {
|
|
|
$context['error_code'] = 'CHANNEL_DISABLED';
|
|
|
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
$callbackType = $channel->success_callback_type ?: SignupChannel::CALLBACK_TYPE_WEB;
|
|
|
if ($callbackType === SignupChannel::CALLBACK_TYPE_NONE) {
|
|
|
$context['error_code'] = 'CALLBACK_DISABLED';
|
|
|
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
if ($callbackType === SignupChannel::CALLBACK_TYPE_WEB
|
|
|
&& trim((string) $channel->success_callback_url) === '') {
|
|
|
$context['error_code'] = 'CALLBACK_MISSING';
|
|
|
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
if ($callbackType === SignupChannel::CALLBACK_TYPE_MINI_PROGRAM) {
|
|
|
$method = $channel->mini_program_callback_method
|
|
|
?: SignupChannel::MINI_PROGRAM_METHOD_REDIRECT_TO;
|
|
|
$callback = [
|
|
|
'type' => SignupChannel::CALLBACK_TYPE_MINI_PROGRAM,
|
|
|
'path' => ChannelCallbackUrlBuilder::buildMiniProgramPath(
|
|
|
$app,
|
|
|
$channel,
|
|
|
$method !== SignupChannel::MINI_PROGRAM_METHOD_SWITCH_TAB
|
|
|
),
|
|
|
'method' => $method,
|
|
|
];
|
|
|
} else {
|
|
|
$callback = [
|
|
|
'type' => SignupChannel::CALLBACK_TYPE_WEB,
|
|
|
'redirect_url' => ChannelCallbackUrlBuilder::build($app, $channel),
|
|
|
];
|
|
|
}
|
|
|
|
|
|
$context['result'] = 'success';
|
|
|
$context['callback_result'] = 'success';
|
|
|
|
|
|
return $callback;
|
|
|
} catch (Throwable $exception) {
|
|
|
$context['error_code'] = 'CALLBACK_INVALID';
|
|
|
Log::warning('channel.callback exception', [
|
|
|
'exception' => $exception::class,
|
|
|
]);
|
|
|
|
|
|
return null;
|
|
|
} finally {
|
|
|
Log::info('channel.callback', $context);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private function maskMobile(string $mobile): string
|
|
|
{
|
|
|
return preg_match('/^\d{11}$/', $mobile)
|
|
|
? substr($mobile, 0, 3).'****'.substr($mobile, -4)
|
|
|
: '***';
|
|
|
}
|
|
|
|
|
|
private function isBlankSignupValue(mixed $value): bool
|
|
|
{
|
|
|
return $value === null || (is_string($value) && trim($value) === '');
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @param array<string, mixed> $payload
|
|
|
* @return array<string, mixed>
|
|
|
*/
|
|
|
private function applySignupPrefillSources(Application $app, array $payload): array
|
|
|
{
|
|
|
$app->loadMissing(['user', 'competition.formSchema']);
|
|
|
$rows = $app->competition?->formSchema?->schema_json;
|
|
|
if (! is_array($rows)) {
|
|
|
return $payload;
|
|
|
}
|
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
if (! is_array($row)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
$key = trim((string) ($row['key'] ?? ''));
|
|
|
$source = trim((string) ($row['prefill_from'] ?? ''));
|
|
|
if ($key === '' || $source === '' || ! array_key_exists($source, self::USER_PREFILL_SOURCES)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if (! array_key_exists($key, $payload) || ! $this->isBlankSignupValue($payload[$key])) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
$userColumn = self::USER_PREFILL_SOURCES[$source];
|
|
|
$value = $app->user?->{$userColumn};
|
|
|
if ($this->isBlankSignupValue($value)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
$payload[$key] = $value;
|
|
|
}
|
|
|
|
|
|
return $payload;
|
|
|
}
|
|
|
|
|
|
private function transform(Application $app): array
|
|
|
{
|
|
|
$payload = [
|
|
|
'id' => $app->id,
|
|
|
'competition_id' => $app->competition_id,
|
|
|
'status' => $app->status,
|
|
|
'player_name' => $app->player_name,
|
|
|
'school' => $app->school,
|
|
|
'degree' => $app->degree,
|
|
|
'contact_email' => $app->contact_email,
|
|
|
'contact_mobile' => $app->contact_mobile,
|
|
|
'entry_group' => $app->entry_group,
|
|
|
'company_name' => $app->company_name,
|
|
|
'project_name' => $app->project_name,
|
|
|
'track' => $app->track,
|
|
|
'location_country' => $app->location_country,
|
|
|
'location_province' => $app->location_province,
|
|
|
'location_city' => $app->location_city,
|
|
|
'oversea_country' => $app->oversea_country,
|
|
|
'intro' => $app->intro,
|
|
|
'promise_signed_at' => $app->promise_signed_at?->toIso8601String(),
|
|
|
'promise_signature' => $app->promise_signature,
|
|
|
'submitted_at' => $app->submitted_at?->toIso8601String(),
|
|
|
'participant_may_edit' => $app->participantMayEditSignup(),
|
|
|
'files' => $app->files->map(fn ($f) => [
|
|
|
'id' => $f->id,
|
|
|
'kind' => $f->kind,
|
|
|
'original_name' => $f->original_name,
|
|
|
'size' => $f->size,
|
|
|
'url' => $f->participantPreviewSignedUrl(),
|
|
|
])->values(),
|
|
|
];
|
|
|
|
|
|
return $this->applySignupPrefillSources($app, $payload);
|
|
|
}
|
|
|
}
|