Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

243 rindas
7.3KB

  1. <?php
  2. namespace App\Logic;
  3. use App\Exceptions\AppCommonException;
  4. use App\Kintone\KintoneRecordQuery;
  5. use App\Kintone\KintoneRecordQueryOperator;
  6. use App\Kintone\Models\DropDown\SeasonTicketContractEntry\Status;
  7. use App\Kintone\Models\SeasonTicketContractEntry as EntryModel;
  8. use App\Kintone\Models\SeasonTicketContractSelection as SelectionModel;
  9. use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Entry;
  10. use App\Kintone\Models\DropDown\SeasonTicketContractSelection\SelectionStatus;
  11. use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Candidate;
  12. use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Result;
  13. use App\Kintone\Models\SubTable\SeasonTicketContractSelection\TargetRoom;
  14. use App\Util\DateUtil;
  15. use Illuminate\Support\Collection;
  16. class SeasonTicketContractSelectionManager
  17. {
  18. private SelectionModel $selection;
  19. public function __construct(?int $recordNo = null)
  20. {
  21. if ($recordNo) {
  22. $this->selection = SelectionModel::find($recordNo);
  23. } else {
  24. $this->selection = new SelectionModel();
  25. $this->selection->status = SelectionStatus::START;
  26. }
  27. }
  28. public function makeCandidates()
  29. {
  30. if ($this->selection->status !== SelectionStatus::START) {
  31. new AppCommonException(
  32. sprintf(
  33. "ステータス不正 候補者一覧を設定するにはステータスが%[s]である必要がある。現ステータス[%s]",
  34. SelectionStatus::START,
  35. $this->selection->status,
  36. )
  37. );
  38. }
  39. $candidateList = $this->selection->candidateList->empty();
  40. // 対象の申込を取得する
  41. // TODO 空き車室に対応したプランの申込に絞る必要あり
  42. $access = EntryModel::getAccess();
  43. $getBaseQuery = function () {
  44. $baseQuery = EntryModel::getQuery();
  45. $baseQuery->where(EntryModel::FIELD_PARKING_NAME, $this->selection->parkingName)
  46. ->orderByAsc(EntryModel::FIELD_ENTRY_DATETIME);
  47. if ($this->selection->useStartDate) {
  48. $useStartDateQuery = function (KintoneRecordQuery $query) {
  49. return $query->whereNull(EntryModel::FIELD_USE_START_DATE)
  50. ->orWhereDate(EntryModel::FIELD_USE_START_DATE, $this->selection->useStartDate, KintoneRecordQueryOperator::GE, true);
  51. };
  52. $baseQuery->where($useStartDateQuery);
  53. }
  54. return $baseQuery;
  55. };
  56. $reserveQuery = $getBaseQuery()->whereIn(EntryModel::FIELD_STATUS, [Status::RESERVE]);
  57. $waitQuery = $getBaseQuery()->whereIn(EntryModel::FIELD_STATUS, [Status::WAIT_EMPTY]);
  58. $reserveList = $access->all($reserveQuery);
  59. $waitList = $access->all($waitQuery);
  60. $list = $reserveList->merge($waitList);
  61. foreach ($list as $ele) {
  62. $candidate = new Candidate();
  63. $candidate->entryRecordNo = $ele->getRecordId();
  64. $candidateList->push($candidate);
  65. }
  66. $this->selection->candidateList = $candidateList;
  67. $this->selection->status = SelectionStatus::TARGET_SELECTING;
  68. return $this;
  69. }
  70. public function sendNotine()
  71. {
  72. // メール送信
  73. return $this;
  74. }
  75. public function makeResult()
  76. {
  77. $messages = $this->doSelection();
  78. $this->selection->selectionMessage = $messages->implode("\r\n");
  79. return $this;
  80. }
  81. public function save()
  82. {
  83. // レコード保存
  84. $this->selection->save();
  85. return $this;
  86. }
  87. public function entry(int $recordNo, string $hashPassword)
  88. {
  89. if (!$this->checkHash($recordNo, $hashPassword)) {
  90. throw new AppCommonException("認証エラー");
  91. }
  92. $list = $this->selection->entryList;
  93. if ($this->hasAlreadyEnterd($recordNo)) {
  94. // すでにエントリー済みのためスキップ
  95. return $this;
  96. }
  97. $entry = new Entry();
  98. $entry->entryRecordNo = $recordNo;
  99. $list->push($entry);
  100. $this->selection->entryList = $list;
  101. return $this;
  102. }
  103. public function hasAlreadyEnterd(int $recordNo): bool
  104. {
  105. $list = $this->selection->entryList;
  106. return $list->search(function (Entry $item) use ($recordNo) {
  107. return $recordNo === $item->entryRecordNo;
  108. }) !== false;
  109. }
  110. public function getSelection()
  111. {
  112. return $this->selection;
  113. }
  114. public function getHash(int $recordNo): string
  115. {
  116. $source = sprintf("%010d-%010d", $recordNo, intval($this->selection->getRecordId()));
  117. return hash('sha256', $source);
  118. }
  119. private function checkHash(int $recordNo, string $hash): bool
  120. {
  121. $expect = $this->getHash($recordNo);
  122. return $expect === $hash;
  123. }
  124. // 抽選用------------------------
  125. private function doSelection()
  126. {
  127. /**
  128. * @var Collection<int, string>
  129. */
  130. $messages = collect();
  131. $resultList = $this->selection->resultList->empty();
  132. // 候補者
  133. if ($this->selection->entryList->isEmpty()) {
  134. $this->selection->status = SelectionStatus::FAILED;
  135. $messages->push("契約希望者不在のため、選考不調");
  136. return $messages;
  137. }
  138. // 希望者一覧のコピー
  139. $entryList = $this->selection->entryList->empty();
  140. foreach ($this->selection->entryList as $entry) {
  141. $entryList->push($entry);
  142. }
  143. // 選考結果の設定
  144. $totalRoomAmount = 0;
  145. foreach ($this->selection->targetRoomList as $room) {
  146. $entry = $this->selectionByRoom($room, $entryList);
  147. if ($entry) {
  148. $result = new Result();
  149. $result->entryRecordNo = $entry->entryRecordNo;
  150. $result->roomRecordNo = $room->roomRecordNo;
  151. $totalRoomAmount += $entry->carAmount;
  152. $resultList->push($result);
  153. }
  154. }
  155. if ($this->selection->targetRoomList->count() < $totalRoomAmount) {
  156. $messages->push("!警告:空き枠以上の台数を抽選したため調整してください");
  157. }
  158. $this->selection->status = $resultList->isNotEmpty() ?
  159. SelectionStatus::COMPLETE : SelectionStatus::FAILED;
  160. $this->selection->resultList = $resultList;
  161. return $messages;
  162. }
  163. /**
  164. * @param TargetRoom $room
  165. * @param Collection<int, Entry> $entries
  166. */
  167. private function selectionByRoom(TargetRoom $room, Collection $entries): ?Entry
  168. {
  169. // 予約者確認
  170. $entry = $entries->filter(function (Entry $entry) {
  171. return $entry->status === Status::RESERVE;
  172. })->sort(function (Entry $a, Entry $b) {
  173. return $a->entryDatetime->lt($b->entryDatetime) ? -1 : 1;
  174. })->first();
  175. if ($entry) {
  176. return $entry;
  177. }
  178. // 空き待ちで抽選
  179. $entry = $entries->filter(function (Entry $entry) {
  180. return $entry->status === Status::WAIT_EMPTY;
  181. })->shuffle(intval(DateUtil::now()->timestamp))->first();
  182. if ($entry) {
  183. return $entry;
  184. }
  185. return null;
  186. }
  187. }