浏览代码

予約対応 途中

master
sosuke.iwabuchi 2 年前
父节点
当前提交
0553b26d70
共有 22 个文件被更改,包括 825 次插入113 次删除
  1. +90
    -0
      app/Console/Commands/SeasonTikcetContractSelectionFillCandidates.php
  2. +11
    -10
      app/Http/Controllers/PDFController.php
  3. +16
    -7
      app/Http/Controllers/Web/Image/ImageController.php
  4. +47
    -0
      app/Jobs/SeasonTicketContract/Selection/FillCandidates.php
  5. +9
    -1
      app/Kintone/KintoneAccess.php
  6. +44
    -9
      app/Kintone/KintoneRecordQuery.php
  7. +9
    -0
      app/Kintone/Models/DropDown/SeasonTicketContractEntry/Status.php
  8. +13
    -0
      app/Kintone/Models/DropDown/SeasonTicketContractSelection/SelectionStatus.php
  9. +20
    -4
      app/Kintone/Models/KintoneModel.php
  10. +4
    -0
      app/Kintone/Models/ParkingRoom.php
  11. +8
    -1
      app/Kintone/Models/SeasonTicketContractEntry.php
  12. +0
    -48
      app/Kintone/Models/SeasonTicketContractReserve.php
  13. +104
    -0
      app/Kintone/Models/SeasonTicketContractSelection.php
  14. +0
    -31
      app/Kintone/Models/SubTable/SeasonTicketContractReserve/TargetRoom.php
  15. +47
    -0
      app/Kintone/Models/SubTable/SeasonTicketContractSelection/Candidate.php
  16. +42
    -0
      app/Kintone/Models/SubTable/SeasonTicketContractSelection/Entry.php
  17. +42
    -0
      app/Kintone/Models/SubTable/SeasonTicketContractSelection/Result.php
  18. +31
    -0
      app/Kintone/Models/SubTable/SeasonTicketContractSelection/TargetRoom.php
  19. +242
    -0
      app/Logic/SeasonTicketContractSelectionManager.php
  20. +4
    -1
      app/Util/DateUtil.php
  21. +41
    -0
      app/Util/LoggingUtil.php
  22. +1
    -1
      config/kintone.php

+ 90
- 0
app/Console/Commands/SeasonTikcetContractSelectionFillCandidates.php 查看文件

@@ -0,0 +1,90 @@
<?php

namespace App\Console\Commands;

use App\Jobs\SeasonTicketContract\Selection\FillCandidates;
use App\Kintone\Models\DropDown\SeasonTicketContractSelection\SelectionStatus;
use App\Kintone\Models\SeasonTicketContractSelection;
use App\Util\DBUtil;
use Exception;

class SeasonTikcetContractSelectionFillCandidates extends BaseCommand
{

const COMMAND = "season-ticket-contract-selection:fill-candidates";


/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = self::COMMAND;

/**
* The console command description.
*
* @var string
*/
protected $description = '定期選考の候補者設定のジョブを登録する';

static public function getCommand()
{
return self::COMMAND;
}

/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->managers = collect();
}

/**
* Execute the console command.
*
* @return int
*/
public function service(): int
{
try {
$db = DBUtil::instance();
$db->beginTransaction();


$targets = $this->getTargets();

$this->outputInfo(sprintf("取得対象 %d件", $targets->count()));

// データハンドリング
foreach ($targets as $data) {
$this->handleData($data);
}

$db->commit();
} catch (Exception $e) {
$db->rollBack();
throw $e;
}

return self::RESULTCODE_SUCCESS;
}

private function getTargets()
{
$access = SeasonTicketContractSelection::getAccess();
$query = SeasonTicketContractSelection::getQuery()
->whereIn(SeasonTicketContractSelection::FIELD_STATUS, [SelectionStatus::START]);
return $access->all($query);
}


private function handleData(SeasonTicketContractSelection $data)
{
FillCandidates::dispatch($data->getRecordId());
}
}

+ 11
- 10
app/Http/Controllers/PDFController.php 查看文件

@@ -2,25 +2,26 @@

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Kintone\Models\Receipt;
use App\Logic\ReceiptManager;
use PDF;

class PDFController extends Controller
{
public function index()
public function entry()
{

// https://wkhtmltopdf.org/usage/wkhtmltopdf.txt
$manager = new ReceiptManager(2);

$file = $manager->makePdf();

// $pdf = PDF::loadHTML('<h1>Hello World 岩渕</h1>');
$pdf = PDF::loadView('pdf');
$receipt = $manager->getReceipt();

// キントーンへの保存
$receipt->setFiles(Receipt::FIELD_RECEIPT_PDF_FILE, collect([$file]));
$receipt->save();

// はがきサイズを指定
$pdf->setOption('page-height', 148)
->setOption('page-width', 100)
->setOption('encoding', 'utf-8');
return $pdf->inline();
return response()
->file($file->getFullPath());
}
}

+ 16
- 7
app/Http/Controllers/Web/Image/ImageController.php 查看文件

@@ -2,9 +2,9 @@

namespace App\Http\Controllers\Web\Image;

use App\Exceptions\AppCommonException;
use App\Http\Controllers\Web\WebController;
use App\Kintone\KintoneAccess;
use App\Kintone\Models\SeasonTicketContract;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

@@ -30,12 +30,21 @@ abstract class ImageController extends WebController

protected function run(Request $request): Response
{
$access = $this->getAccess();
$file = $access->fileGet($this->param->id);
return response($file->body(), 200, [
'Content-Length' => $file->header('Content-Length'),
'Content-Type' => $file->header('Content-Type'),
]);
try {

$id = $request->route('id');
if (!$id) {
throw new AppCommonException("パラメータ不正");
}
$access = $this->getAccess();
$file = $access->fileGet($id);
return response($file->body(), 200, [
'Content-Length' => $file->header('Content-Length'),
'Content-Type' => $file->header('Content-Type'),
]);
} catch (Exception $e) {
return response();
}
}

abstract protected function getAccess(): KintoneAccess;


+ 47
- 0
app/Jobs/SeasonTicketContract/Selection/FillCandidates.php 查看文件

@@ -0,0 +1,47 @@
<?php

namespace App\Jobs\SeasonTicketContract\Selection;

use App\Codes\QueueName;
use App\Jobs\BaseJob;
use App\Logic\SeasonTicketContractSelectionManager;
use App\Util\LoggingUtil;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class FillCandidates extends BaseJob
{

/**
* Create a new job instance.
*
* @return void
*/
public function __construct(
private int $recordNo
) {
$this->onQueue(QueueName::JOB->value);
}

protected function handleJob()
{
try {
$manager = new SeasonTicketContractSelectionManager($this->recordNo);
$manager->makeCandidates()
->save();

$selection = $manager->getSelection();

info(sprintf(
"候補者設定 駐車場:%s 締日:%s 候補者数:%d件",
$selection->parkingName,
$selection->selectionFinalDate ? $selection->selectionFinalDate->format('Y/m/d') : "-",
$selection->candidateList->count(),
));
} catch (ModelNotFoundException $e) {
LoggingUtil::errorException($e, sprintf("データ存在なし削除 %s", self::class));
} catch (Exception $e) {
LoggingUtil::errorException($e, sprintf("ジョブ失敗->削除 %s", self::class));
}
}
}

+ 9
- 1
app/Kintone/KintoneAccess.php 查看文件

@@ -195,8 +195,16 @@ class KintoneAccess
"id" => $model->getRecordId(),
"record" => $model->getApiLayout(),
"revision" => $model->getRevision(),

];

logger([
"ACTION" => "KINTONE UPDATE",
"APP" => $this->appName,
"RECORD_NO" => $model->getRecordId(),
"SEND_DATA" => $sendData,
"JSON" => json_encode($sendData, JSON_UNESCAPED_UNICODE),
]);

$response = Http::withHeaders([
"X-Cybozu-API-Token" => $this->apiToken,
])->put($this->getRecordUrl(), $sendData);


+ 44
- 9
app/Kintone/KintoneRecordQuery.php 查看文件

@@ -32,51 +32,83 @@ class KintoneRecordQuery
return $ret;
}

/**
* @param string|\Closure(static $query): static $column
*/
public function where(string|Closure $column, string|int|null $condition = null, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ)
{
return $this->whereQuery("and", $operator->value, $column, $condition);
}
/**
* @param string|\Closure(static $query): static $column
*/
public function notWhere(string|Closure $column, string|int|null $condition = null, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::NEQ)
{
return $this->whereQuery("and", $operator->value, $column, $condition);
}

/**
* @param string|\Closure(static $query): static $column
*/
public function orWhere(string|Closure $column, string|int|null $condition = null, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ)
{
$this->whereQuery("or", $operator->value, $column, $condition);
return $this;
}
/**
* @param string|\Closure(static $query): static $column
*/
public function notOrWhere(string|Closure $column, string|int|null $condition = null, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::NEQ)
{
return $this->whereQuery("or", $operator->value, $column, $condition);
}
/**
* @param string|\Closure(static $query): static $column
*/
public function whereIn(string|Closure $column, array $condition)
{
return $this->whereQuery("and", "in", $column, $condition);
}
/**
* @param string|\Closure(static $query): static $column
*/
public function whereNotIn(string|Closure $column, array $condition)
{
return $this->whereQuery("and", "not in", $column, $condition);
}
/**
* @param string|\Closure(static $query): static $column
*/
public function orWhereIn(string|Closure $column, array $condition)
{
return $this->whereQuery("or", "not in", $column, $condition);
}
public function whereDate(string $column, Carbon $date, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ)
public function whereDate(string $column, Carbon $date, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ, bool $allowNull = false)
{
return $this->whereNotNull($column)->whereQuery("and", $operator->value, $column, $date->format('Y-m-d'));
if (!$allowNull) {
$this->whereNotNull($column);
}
return $this->whereQuery("and", $operator->value, $column, $date->format('Y-m-d'));
}
public function whereDateTime(string $column, Carbon $date, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ)
public function whereDateTime(string $column, Carbon $date, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ, bool $allowNull = false)
{
return $this->whereNotNull($column)->whereQuery("and", $operator->value, $column, $date->toIso8601ZuluString());
if (!$allowNull) {
$this->whereNotNull($column);
}
return $this->whereQuery("and", $operator->value, $column, $date->toIso8601ZuluString());
}
public function orWhereDate(string $column, Carbon $date, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ)
public function orWhereDate(string $column, Carbon $date, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ, bool $allowNull = false)
{
return $this->whereNotNull($column)->whereQuery("or", $operator->value, $column, $date->format('Y-m-d'));
if (!$allowNull) {
$this->whereNotNull($column);
}
return $this->whereQuery("or", $operator->value, $column, $date->format('Y-m-d'));
}
public function orWhereDateTime(string $column, Carbon $date, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ)
public function orWhereDateTime(string $column, Carbon $date, KintoneRecordQueryOperator $operator = KintoneRecordQueryOperator::EQ, bool $allowNull = false)
{
return $this->whereNotNull($column)->whereQuery("or", $operator->value, $column, $date->toIso8601ZuluString());
if (!$allowNull) {
$this->whereNotNull($column);
}
return $this->whereQuery("or", $operator->value, $column, $date->toIso8601ZuluString());
}
public function whereNull(string $column)
{
@@ -109,6 +141,9 @@ class KintoneRecordQuery
return $this;
}

/**
* @param string|\Closure(static $query): static $column
*/
private function whereQuery(
string $andOr,
string $operator,


+ 9
- 0
app/Kintone/Models/DropDown/SeasonTicketContractEntry/Status.php 查看文件

@@ -0,0 +1,9 @@
<?php

namespace App\Kintone\Models\DropDown\SeasonTicketContractEntry;

abstract class Status
{
const RESERVE = "予約";
const WAIT_EMPTY = "空き待ち";
}

+ 13
- 0
app/Kintone/Models/DropDown/SeasonTicketContractSelection/SelectionStatus.php 查看文件

@@ -0,0 +1,13 @@
<?php

namespace App\Kintone\Models\DropDown\SeasonTicketContractSelection;

abstract class SelectionStatus
{
const START = "起票";
const TARGET_SELECTING = "通知者選択中";
const ENTRY_ACCEPTING = "契約希望者受付中";
const RESULT_DECISION = "候補者仮決定";
const FAILED = "選考不調";
const COMPLETE = "終了";
}

+ 20
- 4
app/Kintone/Models/KintoneModel.php 查看文件

@@ -137,6 +137,12 @@ abstract class KintoneModel
public function __construct()
{
$this->data = new stdClass();

foreach (static::FIELDS as $fileCode => $type) {
if ($type === FieldType::SUBTABLE) {
$this->set($fileCode, collect());
}
}
}

public function __get($name)
@@ -244,12 +250,18 @@ abstract class KintoneModel
if ($type === FieldType::SUBTABLE && $this->getFieldType($fieldCode)) {
$ret = collect();
foreach ($value as $v) {
$rowRet =
$rowArray = data_get($v, 'value');
$rowRet = [];
$rowArray = data_get($v, 'value');
$isEmptyRow = true;
foreach ($rowArray as $subFieldCode => $ele) {
$rowRet[$subFieldCode] = $this->readData($ele);
if (!!$rowRet[$subFieldCode]) {
$isEmptyRow = false;
}
}
if (!$isEmptyRow) {
$ret->push($this->getSubTableData($fieldCode, $rowRet));
}
$ret->push($this->getSubTableData($fieldCode, $rowRet));
}
return $ret;
}
@@ -332,7 +344,11 @@ abstract class KintoneModel
foreach ($data as $ele) {
if ($ele instanceof SubTableData) {
$obj = [];
$obj['value'] = $ele->toArray();

$values = $ele->toArray();
foreach ($values as $subFieldCode => $value) {
$obj['value'][$subFieldCode]['value'] = $value;
}
$ret[] = $obj;
}
}


+ 4
- 0
app/Kintone/Models/ParkingRoom.php 查看文件

@@ -12,10 +12,14 @@ class ParkingRoom extends KintoneModel
const CONFIG_KEY = "KINTONE_APP_PARKING_ROOM";

const FIELD_PARKING_NAME = "定期駐車場";
const FIELD_ROOM_NO = "車室番号";
const FIELD_ROOM_TYPE = "車室タイプ";

protected const FIELDS = [
...parent::FIELDS,
self::FIELD_PARKING_NAME => FieldType::SINGLE_LINE_TEXT,
self::FIELD_ROOM_NO => FieldType::SINGLE_LINE_TEXT,
self::FIELD_ROOM_TYPE => FieldType::DROP_DOWN,
];

protected const FIELD_NAMES = [


+ 8
- 1
app/Kintone/Models/SeasonTicketContractEntry.php 查看文件

@@ -7,20 +7,24 @@ use Illuminate\Support\Carbon;
/**
* アプリ名 定期申込・予約
* @property string parkingName
* @property string status
* @property string customerName
* @property string customerNameKana
* @property string phoneNo
* @property string address
* @property string email
* @property Carbon useStartDate
* @property ?Carbon useStartDate
* @property string vehicleNo
* @property int carAmount
* @property string paymentMethod
* @property Carbon entryDatetime
*/
class SeasonTicketContractEntry extends KintoneModel
{
const CONFIG_KEY = "KINTONE_APP_SEASON_TICEKT_CONTRACT_ENTRY";

const FIELD_PARKING_NAME = "駐車場";
const FIELD_STATUS = "status";
const FIELD_CUSTOMER_NAME = "氏名";
const FIELD_CUSTOMER_NAME_KANA = "フリガナ";
const FIELD_PHONE_NO = "電話番号";
@@ -28,7 +32,9 @@ class SeasonTicketContractEntry extends KintoneModel
const FIELD_EMAIL = "メールアドレス";
const FIELD_USE_START_DATE = "利用開始希望日";
const FIELD_VEHICLE_NO = "車両番号";
const FIELD_CAR_AMOUNT = "台数";
const FIELD_PAYMENT_METHOD = "支払方法";
const FIELD_ENTRY_DATETIME = "受付日時";

protected const FIELDS = [
...parent::FIELDS,
@@ -41,6 +47,7 @@ class SeasonTicketContractEntry extends KintoneModel
self::FIELD_USE_START_DATE => FieldType::DATE,
self::FIELD_VEHICLE_NO => FieldType::SINGLE_LINE_TEXT,
self::FIELD_PAYMENT_METHOD => FieldType::DROP_DOWN,
self::FIELD_ENTRY_DATETIME => FieldType::DATETIME,
];

protected const FIELD_NAMES = [


+ 0
- 48
app/Kintone/Models/SeasonTicketContractReserve.php 查看文件

@@ -1,48 +0,0 @@
<?php

namespace App\Kintone\Models;

use App\Kintone\Models\SubTable\SeasonTicketContractReserve\TargetRoom;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;

/**
* アプリ名 定期予約選考
* @property string parkingName
* @property Collection<int, TargetRoom> targetRoomList
*/
class SeasonTicketContractReserve extends KintoneModel
{
const CONFIG_KEY = "KINTONE_APP_SEASON_TICEKT_CONTRACT_RESERVE";

const FIELD_PARKING_NAME = "駐車場";

const FIELD_TARGET_ROOM_LIST = "対象車室一覧";
const FIELD_TARGET_ROOM_LIST_ROOM_NO = "対象車室一覧_車室番号";
const FIELD_TARGET_ROOM_LIST_ROOM_TYPE = "対象車室一覧_車室タイプ";
const FIELD_TARGET_ROOM_LIST_RECORD_NO = "対象車室一覧_車室レコード番号";

protected const FIELDS = [
...parent::FIELDS,
self::FIELD_PARKING_NAME => FieldType::SINGLE_LINE_TEXT,
self::FIELD_TARGET_ROOM_LIST => FieldType::SUBTABLE,
// self::FIELD_TARGET_ROOM_LIST => [
// self::FIELD_TARGET_ROOM_LIST_RECORD_NO => FieldType::NUMBER,
// self::FIELD_TARGET_ROOM_LIST_ROOM_TYPE => FieldType::SINGLE_LINE_TEXT,
// self::FIELD_TARGET_ROOM_LIST_ROOM_NO => FieldType::SINGLE_LINE_TEXT,
// ],
];

protected const SUB_TABLES = [
self::FIELD_TARGET_ROOM_LIST => TargetRoom::class,
];

protected const FIELD_NAMES = [
...parent::FIELD_NAMES,
];

protected const RELATIONS = [
SeasonTicketContractEntry::class,
ParkingRoom::class,
];
}

+ 104
- 0
app/Kintone/Models/SeasonTicketContractSelection.php 查看文件

@@ -0,0 +1,104 @@
<?php

namespace App\Kintone\Models;

use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Candidate;
use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Entry;
use App\Kintone\Models\SubTable\SeasonTicketContractSelection\Result;
use App\Kintone\Models\SubTable\SeasonTicketContractSelection\TargetRoom;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;

/**
* アプリ名 定期予約選考
* @property string parkingName
* @property string status
* @property ?Carbon useStartDate
* @property ?Carbon selectionFinalDate
* @property Collection<int, TargetRoom> targetRoomList
* @property Collection<int, Candidate> candidateList
* @property Collection<int, Entry> entryList
* @property Collection<int, Result> resultList
* @property string selectionMessage
*/
class SeasonTicketContractSelection extends KintoneModel
{
const CONFIG_KEY = "KINTONE_APP_SEASON_TICEKT_CONTRACT_SELECTION";

const FIELD_PARKING_NAME = "駐車場名";
const FIELD_STATUS = "選考ステータス";

const FIELD_USE_START_DATE = "利用開始日";
const FIELD_SELECTION_FINAL_DATE = "選考締切日";

const FIELD_TARGET_ROOM_LIST = "対象車室一覧";
const FIELD_TARGET_ROOM_LIST_ROOM_NO = "対象車室一覧_車室番号";
const FIELD_TARGET_ROOM_LIST_ROOM_TYPE = "対象車室一覧_車室タイプ";
const FIELD_TARGET_ROOM_LIST_RECORD_NO = "対象車室一覧_車室レコード番号";

const FIELD_CANDIDATE_LIST = "申込者一覧";
const FIELD_CANDIDATE_EMAIL_SEND_TARGET = "申込者一覧_通知対象";
const FIELD_CANDIDATE_LIST_RECORD_NO = "申込者一覧_申込レコード番号";
const FIELD_CANDIDATE_LIST_STATUS = "申込者一覧_状態";
const FIELD_CANDIDATE_LIST_PLAN = "申込者一覧_プラン";
const FIELD_CANDIDATE_LIST_ENTRY_NO = "申込者一覧_申込番号";
const FIELD_CANDIDATE_LIST_NAME = "申込者一覧_氏名";
const FIELD_CANDIDATE_LIST_ENTRY_DATETIME = "申込者一覧_受付日時";
const FIELD_CANDIDATE_LIST_WANTS_TO_USE_START_DATE = "申込者一覧_利用開始希望日";
const FIELD_CANDIDATE_LIST_CAR_AMOUNT = "申込者一覧_台数";
const FIELD_CANDIDATE_LIST_EMAIL = "申込者一覧_メールアドレス";

const FIELD_ENTRY_LIST = "契約希望者一覧";
const FIELD_ENTRY_LIST_STATUS = "契約希望者一覧_状態";
const FIELD_ENTRY_LIST_PLAN = "契約希望者一覧_プラン";
const FIELD_ENTRY_LIST_RECORD_NO = "契約希望者一覧_申込レコード番号";
const FIELD_ENTRY_LIST_ENTRY_NO = "契約希望者一覧_申込番号";
const FIELD_ENTRY_LIST_NAME = "契約希望者一覧_氏名";
const FIELD_ENTRY_LIST_ENTRY_DATETIME = "契約希望者一覧_受付日時";
const FIELD_ENTRY_LIST_WANTS_TO_USE_START_DATE = "契約希望者一覧_利用開始希望日";
const FIELD_ENTRY_LIST_CAR_AMOUNT = "契約希望者一覧_台数";

const FIELD_RESULT_LIST = "選考結果一覧";
const FIELD_RESULT_LIST_ROOM_RECORD_NO = "選考結果一覧_車室レコード番号";
const FIELD_RESULT_LIST_ROOM_NO = "選考結果一覧_車室番号";
const FIELD_RESULT_LIST_ROOM_TYPE = "選考結果一覧_車室タイプ";
const FIELD_RESULT_LIST_ENTRY_RECORD_NO = "選考結果一覧_申込レコード番号";
const FIELD_RESULT_LIST_STATUS = "選考結果一覧_ステータス";
const FIELD_RESULT_LIST_PLAN = "選考結果一覧_プラン";
const FIELD_RESULT_LIST_ENTRY_NO = "選考結果一覧_申込番号";
const FIELD_RESULT_LIST_NAME = "選考結果一覧_氏名";

const FIELD_SELECTION_MESSAGE = "自動選考メッセージ";

const FIELD_SEARCH_CANDIDATE_LIST_RECORD_NO = "検索用_申込一覧_レコード番号";
const FIELD_SEARCH_ENTRY_LIST_RECORD_NO = "検索用_契約希望者_レコード番号";
const FIELD_SEARCH_RESULT_LIST_ENTRY_RECORD_NO = "検索用_選考結果_レコード番号";


protected const FIELDS = [
...parent::FIELDS,
self::FIELD_PARKING_NAME => FieldType::SINGLE_LINE_TEXT,
self::FIELD_STATUS => FieldType::DROP_DOWN,
self::FIELD_TARGET_ROOM_LIST => FieldType::SUBTABLE,
self::FIELD_CANDIDATE_LIST => FieldType::SUBTABLE,
self::FIELD_ENTRY_LIST => FieldType::SUBTABLE,
self::FIELD_RESULT_LIST => FieldType::SUBTABLE,
self::FIELD_SELECTION_MESSAGE => FieldType::MULTI_LINE_TEXT,
];

protected const SUB_TABLES = [
self::FIELD_TARGET_ROOM_LIST => TargetRoom::class,
self::FIELD_CANDIDATE_LIST => Candidate::class,
self::FIELD_ENTRY_LIST => Entry::class,
self::FIELD_RESULT_LIST => Result::class,
];

protected const FIELD_NAMES = [
...parent::FIELD_NAMES,
];

protected const RELATIONS = [
SeasonTicketContractEntry::class,
ParkingRoom::class,
];
}

+ 0
- 31
app/Kintone/Models/SubTable/SeasonTicketContractReserve/TargetRoom.php 查看文件

@@ -1,31 +0,0 @@
<?php

namespace App\Kintone\Models\SubTable\SeasonTicketContractReserve;

use App\Kintone\Models\SeasonTicketContractReserve;
use App\Kintone\Models\SubTable\SubTableData;

class TargetRoom extends SubTableData
{

public string $roomNo;
public string $roomType;
public int $roomRecordNo;

public function __construct(array $data)
{
$this->roomNo = data_get($data, SeasonTicketContractReserve::FIELD_TARGET_ROOM_LIST_ROOM_NO, "");
$this->roomType = data_get($data, SeasonTicketContractReserve::FIELD_TARGET_ROOM_LIST_ROOM_TYPE, "");
$this->roomRecordNo = intval(data_get($data, SeasonTicketContractReserve::FIELD_TARGET_ROOM_LIST_RECORD_NO, 0));
parent::__construct($data);
}

public function toArray(): array
{
return [
SeasonTicketContractReserve::FIELD_TARGET_ROOM_LIST_ROOM_NO => $this->roomNo,
SeasonTicketContractReserve::FIELD_TARGET_ROOM_LIST_ROOM_TYPE => $this->roomType,
SeasonTicketContractReserve::FIELD_TARGET_ROOM_LIST_RECORD_NO => $this->roomRecordNo,
];
}
}

+ 47
- 0
app/Kintone/Models/SubTable/SeasonTicketContractSelection/Candidate.php 查看文件

@@ -0,0 +1,47 @@
<?php

namespace App\Kintone\Models\SubTable\SeasonTicketContractSelection;

use App\Kintone\Models\SeasonTicketContractSelection;
use App\Kintone\Models\SubTable\SubTableData;
use App\Util\DateUtil;
use Illuminate\Support\Carbon;

class Candidate extends SubTableData
{

public bool $emailSendTarget;
public int $entryRecordNo;
public string $status;
public string $planName;
public string $entryNo;
public string $name;
public ?Carbon $entryDatetime;
public ?Carbon $wantsToUseStartDate;
public int $carAmount;
public string $email;

public function __construct(array $data = [])
{
$this->emailSendTarget = count(data_get($data, SeasonTicketContractSelection::FIELD_CANDIDATE_EMAIL_SEND_TARGET, [])) !== 0;
$this->entryRecordNo = intval(data_get($data, SeasonTicketContractSelection::FIELD_CANDIDATE_LIST_RECORD_NO, 0));
$this->planName = data_get($data, SeasonTicketContractSelection::FIELD_CANDIDATE_LIST_STATUS, "");
$this->planName = data_get($data, SeasonTicketContractSelection::FIELD_CANDIDATE_LIST_PLAN, "");
$this->entryNo = data_get($data, SeasonTicketContractSelection::FIELD_CANDIDATE_LIST_PLAN, "");
$this->name = data_get($data, SeasonTicketContractSelection::FIELD_CANDIDATE_LIST_NAME, "");
$this->entryDatetime = DateUtil::parse(data_get($data, SeasonTicketContractSelection::FIELD_TARGET_ROOM_LIST_ROOM_NO, ""));
$this->wantsToUseStartDate = DateUtil::parse(data_get($data, SeasonTicketContractSelection::FIELD_CANDIDATE_LIST_WANTS_TO_USE_START_DATE, ""));
$this->carAmount = intval(data_get($data, SeasonTicketContractSelection::FIELD_CANDIDATE_LIST_CAR_AMOUNT, 0));
$this->email = data_get($data, SeasonTicketContractSelection::FIELD_TARGET_ROOM_LIST_ROOM_NO, "");

parent::__construct($data);
}

public function toArray(): array
{
return [
SeasonTicketContractSelection::FIELD_CANDIDATE_EMAIL_SEND_TARGET => $this->emailSendTarget ? ["〇"] : [],
SeasonTicketContractSelection::FIELD_CANDIDATE_LIST_RECORD_NO => $this->entryRecordNo,
];
}
}

+ 42
- 0
app/Kintone/Models/SubTable/SeasonTicketContractSelection/Entry.php 查看文件

@@ -0,0 +1,42 @@
<?php

namespace App\Kintone\Models\SubTable\SeasonTicketContractSelection;

use App\Kintone\Models\SeasonTicketContractSelection;
use App\Kintone\Models\SubTable\SubTableData;
use App\Util\DateUtil;
use Illuminate\Support\Carbon;

class Entry extends SubTableData
{

public int $entryRecordNo;
public string $status;
public string $planName;
public string $entryNo;
public string $name;
public ?Carbon $entryDatetime;
public ?Carbon $wantsToUseStartDate;
public int $carAmount;

public function __construct(array $data = [])
{
$this->entryRecordNo = intval(data_get($data, SeasonTicketContractSelection::FIELD_ENTRY_LIST_RECORD_NO, 0));
$this->status = data_get($data, SeasonTicketContractSelection::FIELD_ENTRY_LIST_STATUS, "");
$this->planName = data_get($data, SeasonTicketContractSelection::FIELD_ENTRY_LIST_PLAN, "");
$this->entryNo = data_get($data, SeasonTicketContractSelection::FIELD_ENTRY_LIST_ENTRY_NO, "");
$this->name = data_get($data, SeasonTicketContractSelection::FIELD_ENTRY_LIST_NAME, "");
$this->entryDatetime = DateUtil::parse(data_get($data, SeasonTicketContractSelection::FIELD_ENTRY_LIST_ENTRY_DATETIME, ""));
$this->wantsToUseStartDate = DateUtil::parse(data_get($data, SeasonTicketContractSelection::FIELD_ENTRY_LIST_WANTS_TO_USE_START_DATE, ""));
$this->carAmount = intval(data_get($data, SeasonTicketContractSelection::FIELD_ENTRY_LIST_CAR_AMOUNT, 0));

parent::__construct($data);
}

public function toArray(): array
{
return [
SeasonTicketContractSelection::FIELD_ENTRY_LIST_RECORD_NO => $this->entryRecordNo,
];
}
}

+ 42
- 0
app/Kintone/Models/SubTable/SeasonTicketContractSelection/Result.php 查看文件

@@ -0,0 +1,42 @@
<?php

namespace App\Kintone\Models\SubTable\SeasonTicketContractSelection;

use App\Kintone\Models\SeasonTicketContractSelection;
use App\Kintone\Models\SubTable\SubTableData;

class Result extends SubTableData
{

public int $roomRecordNo;
public string $roomNo;
public string $roomType;
public int $entryRecordNo;
public string $status;
public string $planName;
public string $entryNo;
public string $name;

public function __construct(array $data = [])
{
$this->roomRecordNo = intval(data_get($data, SeasonTicketContractSelection::FIELD_RESULT_LIST_ROOM_RECORD_NO, 0));
$this->roomNo = data_get($data, SeasonTicketContractSelection::FIELD_RESULT_LIST_ROOM_NO, "");
$this->roomType = data_get($data, SeasonTicketContractSelection::FIELD_RESULT_LIST_ROOM_TYPE, "");

$this->entryRecordNo = intval(data_get($data, SeasonTicketContractSelection::FIELD_RESULT_LIST_ENTRY_RECORD_NO, 0));
$this->status = data_get($data, SeasonTicketContractSelection::FIELD_RESULT_LIST_STATUS, "");
$this->planName = data_get($data, SeasonTicketContractSelection::FIELD_RESULT_LIST_PLAN, "");
$this->entryNo = data_get($data, SeasonTicketContractSelection::FIELD_RESULT_LIST_ENTRY_NO, "");
$this->name = data_get($data, SeasonTicketContractSelection::FIELD_RESULT_LIST_NAME, "");

parent::__construct($data);
}

public function toArray(): array
{
return [
SeasonTicketContractSelection::FIELD_RESULT_LIST_ROOM_RECORD_NO => $this->roomRecordNo,
SeasonTicketContractSelection::FIELD_RESULT_LIST_ENTRY_RECORD_NO => $this->entryRecordNo,
];
}
}

+ 31
- 0
app/Kintone/Models/SubTable/SeasonTicketContractSelection/TargetRoom.php 查看文件

@@ -0,0 +1,31 @@
<?php

namespace App\Kintone\Models\SubTable\SeasonTicketContractSelection;

use App\Kintone\Models\SeasonTicketContractSelection;
use App\Kintone\Models\SubTable\SubTableData;

class TargetRoom extends SubTableData
{

public string $roomNo;
public string $roomType;
public int $roomRecordNo;

public function __construct(array $data = [])
{
$this->roomNo = data_get($data, SeasonTicketContractSelection::FIELD_TARGET_ROOM_LIST_ROOM_NO, "");
$this->roomType = data_get($data, SeasonTicketContractSelection::FIELD_TARGET_ROOM_LIST_ROOM_TYPE, "");
$this->roomRecordNo = intval(data_get($data, SeasonTicketContractSelection::FIELD_TARGET_ROOM_LIST_RECORD_NO, 0));
parent::__construct($data);
}

public function toArray(): array
{
return [
SeasonTicketContractSelection::FIELD_TARGET_ROOM_LIST_ROOM_NO => $this->roomNo,
SeasonTicketContractSelection::FIELD_TARGET_ROOM_LIST_ROOM_TYPE => $this->roomType,
SeasonTicketContractSelection::FIELD_TARGET_ROOM_LIST_RECORD_NO => $this->roomRecordNo,
];
}
}

+ 242
- 0
app/Logic/SeasonTicketContractSelectionManager.php 查看文件

@@ -0,0 +1,242 @@
<?php

namespace App\Logic;

use App\Exceptions\AppCommonException;
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\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 $recordNo = null)
{
if ($recordNo) {
$this->selection = SelectionModel::find($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 sendNotine()
{
// メール送信

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("認証エラー");
}

$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 getHash(int $recordNo): string
{
$source = sprintf("%010d-%010d", $recordNo, intval($this->selection->getRecordId()));
return hash('sha256', $source);
}

private 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;
$totalRoomAmount += $entry->carAmount;
$resultList->push($result);
}
}

if ($this->selection->targetRoomList->count() < $totalRoomAmount) {
$messages->push("!警告:空き枠以上の台数を抽選したため調整してください");
}


$this->selection->status = $resultList->isNotEmpty() ?
SelectionStatus::COMPLETE : 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;
}
}

+ 4
- 1
app/Util/DateUtil.php 查看文件

@@ -24,8 +24,11 @@ class DateUtil
return new Carbon();
}

public static function parse(string $source): Carbon|null
public static function parse(?string $source): Carbon|null
{
if ($source === null) {
return null;
}
$date = Carbon::parse($source);
if ($date->isValid()) {
return $date->timezone(config('app.timezone'));


+ 41
- 0
app/Util/LoggingUtil.php 查看文件

@@ -0,0 +1,41 @@
<?php

namespace App\Util;

use Exception;
use Illuminate\Support\Facades\Log;

class LoggingUtil
{

public static function infoException(Exception $e, string|array $messages = [])
{
Log::error(self::getExceptionContents($e, $messages));
}
public static function warnException(Exception $e, string|array $messages = [])
{
Log::error(self::getExceptionContents($e, $messages));
}
public static function errorException(Exception $e, string|array $messages = [])
{
Log::error(self::getExceptionContents($e, $messages));
}

private static function getExceptionContents(Exception $e, string|array $messages)
{

if (is_string($messages)) {
$message = $messages;
$messages = [];
$messages[] = $message;
}

return [
...$messages,
'_message' => $e->getMessage(),
'_file' => $e->getFile(),
'_line' => $e->getLine(),
'_exceptionType' => $e::class,
];
}
}

+ 1
- 1
config/kintone.php 查看文件

@@ -33,7 +33,7 @@ return [
...App\Kintone\Models\SeasonTicketContract::setConfig(),
...App\Kintone\Models\SeasonTicketContractPlan::setConfig(),
...App\Kintone\Models\SeasonTicketContractEntry::setConfig(),
...App\Kintone\Models\SeasonTicketContractReserve::setConfig(),
...App\Kintone\Models\SeasonTicketContractSelection::setConfig(),
...App\Kintone\Models\PaymentPlan::setConfig(),
...App\Kintone\Models\GeneralApplication::setConfig(),
...App\Kintone\Models\FAQ::setConfig(),


正在加载...
取消
保存