|
- <?php
-
- namespace App\Logic;
-
- use App\Email\Members\SelectionNotice;
- use App\Exceptions\AppCommonException;
- use App\Exceptions\GeneralErrorMessageException;
- use App\Kintone\KintoneRecordQuery;
- use App\Kintone\KintoneRecordQueryOperator;
- use App\Kintone\Models\DropDown\SeasonTicketContractEntry\Status;
- use App\Kintone\Models\SeasonTicketContractEntry as EntryModel;
- use App\Kintone\Models\SeasonTicketContractSelection as SelectionModel;
- use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Entry;
- use App\Kintone\Models\DropDown\SeasonTicketContractSelection\SelectionStatus;
- use App\Kintone\Models\SeasonTicketContractEntry;
- use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Candidate;
- use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Result;
- use App\Kintone\Models\SubTable\SeasonTicketContractSelection\TargetRoom;
- use App\Util\DateUtil;
- use Illuminate\Support\Collection;
-
- class SeasonTicketContractSelectionManager
- {
-
- private SelectionModel $selection;
-
- public function __construct(int|SelectionModel|null $recordNo = null)
- {
- if (is_int($recordNo)) {
- $this->selection = SelectionModel::find($recordNo);
- } else if ($recordNo instanceof SelectionModel) {
- $this->selection = $recordNo;
- } else {
- $this->selection = new SelectionModel();
- $this->selection->status = SelectionStatus::START;
- }
- }
-
- public function makeCandidates()
- {
- if ($this->selection->status !== SelectionStatus::START) {
- new AppCommonException(
- sprintf(
- "ステータス不正 候補者一覧を設定するにはステータスが%[s]である必要がある。現ステータス[%s]",
- SelectionStatus::START,
- $this->selection->status,
- )
- );
- }
-
- $candidateList = $this->selection->candidateList->empty();
-
- // 対象の申込を取得する
- // TODO 空き車室に対応したプランの申込に絞る必要あり
- $access = EntryModel::getAccess();
-
- $getBaseQuery = function () {
- $baseQuery = EntryModel::getQuery();
- $baseQuery->where(EntryModel::FIELD_PARKING_NAME, $this->selection->parkingName)
- ->orderByAsc(EntryModel::FIELD_ENTRY_DATETIME);
- if ($this->selection->useStartDate) {
- $useStartDateQuery = function (KintoneRecordQuery $query) {
- return $query->whereNull(EntryModel::FIELD_USE_START_DATE)
- ->orWhereDate(EntryModel::FIELD_USE_START_DATE, $this->selection->useStartDate, KintoneRecordQueryOperator::GE, true);
- };
- $baseQuery->where($useStartDateQuery);
- }
- return $baseQuery;
- };
-
- $reserveQuery = $getBaseQuery()->whereIn(EntryModel::FIELD_STATUS, [Status::RESERVE]);
- $waitQuery = $getBaseQuery()->whereIn(EntryModel::FIELD_STATUS, [Status::WAIT_EMPTY]);
-
-
- $reserveList = $access->all($reserveQuery);
- $waitList = $access->all($waitQuery);
-
- $list = $reserveList->merge($waitList);
-
- foreach ($list as $ele) {
- $candidate = new Candidate();
- $candidate->entryRecordNo = $ele->getRecordId();
- $candidateList->push($candidate);
- }
- $this->selection->candidateList = $candidateList;
-
- $this->selection->status = SelectionStatus::TARGET_SELECTING;
-
- return $this;
- }
-
- public function sendNotice()
- {
- // メール送信
- foreach ($this->selection->candidateList as $candidate) {
- if ($candidate->emailSendTarget) {
- $entry = SeasonTicketContractEntry::find($candidate->entryRecordNo);
-
- if (!in_array($entry->status, [Status::RESERVE, Status::WAIT_EMPTY])) {
- continue;
- }
- if (!$entry->email) {
- continue;
- }
-
- $email = new SelectionNotice($this->selection, $entry);
- (new EmailManager($email->setEmail($entry->email)))->confirm();
- }
- }
-
- $this->selection->status = SelectionStatus::ENTRY_ACCEPTING;
-
- return $this;
- }
-
- public function makeResult()
- {
- $messages = $this->doSelection();
-
- $this->selection->selectionMessage = $messages->implode("\r\n");
-
- return $this;
- }
-
- public function save()
- {
- // レコード保存
- $this->selection->save();
-
- return $this;
- }
-
- public function entry(int $recordNo, string $hashPassword)
- {
- if (!$this->checkHash($recordNo, $hashPassword)) {
- throw new AppCommonException("認証エラー");
- }
-
- if ($this->selection->status !== SelectionStatus::ENTRY_ACCEPTING) {
- throw new GeneralErrorMessageException("募集期間が終了しています");
- }
-
- $list = $this->selection->entryList;
-
- if ($this->hasAlreadyEnterd($recordNo)) {
- // すでにエントリー済みのためスキップ
- return $this;
- }
-
- $entry = new Entry();
- $entry->entryRecordNo = $recordNo;
-
- $list->push($entry);
-
- $this->selection->entryList = $list;
-
- return $this;
- }
-
- public function hasAlreadyEnterd(int $recordNo): bool
- {
- $list = $this->selection->entryList;
- return $list->search(function (Entry $item) use ($recordNo) {
- return $recordNo === $item->entryRecordNo;
- }) !== false;
- }
-
- public function getSelection()
- {
- return $this->selection;
- }
-
- public function getEntry(int $entryRecordNo)
- {
- return SeasonTicketContractEntry::find($entryRecordNo);
- }
-
- /**
- * @param integer $recordNo 申込レコード番号
- * @return string
- */
- public function getHash(int $recordNo): string
- {
- $source = sprintf("%010d-%010d", $recordNo, intval($this->selection->getRecordId()));
- return hash('sha256', $source);
- }
-
- public function checkHash(int $recordNo, string $hash): bool
- {
- $expect = $this->getHash($recordNo);
- return $expect === $hash;
- }
-
-
- // 抽選用------------------------
-
- private function doSelection()
- {
- /**
- * @var Collection<int, string>
- */
- $messages = collect();
-
- $resultList = $this->selection->resultList->empty();
-
- // 候補者
- if ($this->selection->entryList->isEmpty()) {
- $this->selection->status = SelectionStatus::FAILED;
- $messages->push("契約希望者不在のため、選考不調");
- return $messages;
- }
-
-
- // 希望者一覧のコピー
- $entryList = $this->selection->entryList->empty();
- foreach ($this->selection->entryList as $entry) {
- $entryList->push($entry);
- }
-
- // 選考結果の設定
- $totalRoomAmount = 0;
- foreach ($this->selection->targetRoomList as $room) {
-
- $entry = $this->selectionByRoom($room, $entryList);
-
- if ($entry) {
- $result = new Result();
- $result->entryRecordNo = $entry->entryRecordNo;
- $result->roomRecordNo = $room->roomRecordNo;
- $result->name = $entry->name;
- $result->entryNo = $entry->entryNo;
- $totalRoomAmount += $entry->carAmount;
- $resultList->push($result);
- }
- }
-
- if ($this->selection->targetRoomList->count() < $totalRoomAmount) {
- $messages->push("!警告:空き枠以上の台数を抽選したため調整してください");
- }
-
-
- $this->selection->status = $resultList->isNotEmpty() ?
- SelectionStatus::RESULT_DECISION : SelectionStatus::FAILED;
-
- $this->selection->resultList = $resultList;
-
- return $messages;
- }
- /**
- * @param TargetRoom $room
- * @param Collection<int, Entry> $entries
- */
- private function selectionByRoom(TargetRoom $room, Collection $entries): ?Entry
- {
-
- // 予約者確認
- $entry = $entries->filter(function (Entry $entry) {
- return $entry->status === Status::RESERVE;
- })->sort(function (Entry $a, Entry $b) {
- return $a->entryDatetime->lt($b->entryDatetime) ? -1 : 1;
- })->first();
-
-
- if ($entry) {
- return $entry;
- }
-
- // 空き待ちで抽選
- $entry = $entries->filter(function (Entry $entry) {
- return $entry->status === Status::WAIT_EMPTY;
- })->shuffle(intval(DateUtil::now()->timestamp))->first();
-
- if ($entry) {
- return $entry;
- }
-
- return null;
- }
- }
|