Compare commits
4 Commits
dce7e1daad
...
e70c834bb0
| Author | SHA1 | Date | |
|---|---|---|---|
| e70c834bb0 | |||
| eed5d85741 | |||
|
|
3960e062b9 | ||
| bbc7ae7e83 |
207
app/Console/Commands/ShjFiveCommand.php
Normal file
207
app/Console/Commands/ShjFiveCommand.php
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use App\Services\ShjFiveService;
|
||||||
|
use App\Models\Batch\BatchLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHJ-5 空き待ち通知処理コマンド
|
||||||
|
*
|
||||||
|
* 駐輪場の空き状況を確認し、空き待ち予約者への通知処理を実行する
|
||||||
|
* バックグラウンドで実行される定期バッチ処理
|
||||||
|
*/
|
||||||
|
class ShjFiveCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* コンソールコマンドの名前とシグネチャ
|
||||||
|
*
|
||||||
|
* 引数なし - 全ての駐輪場を対象に処理を実行
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'shj:5';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンソールコマンドの説明
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'SHJ-5 空き待ち通知処理 - 駐輪場の空き状況確認と空き待ち者への通知を実行';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHJ-5サービスクラス
|
||||||
|
*
|
||||||
|
* @var ShjFiveService
|
||||||
|
*/
|
||||||
|
protected $shjFiveService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*
|
||||||
|
* @param ShjFiveService $shjFiveService
|
||||||
|
*/
|
||||||
|
public function __construct(ShjFiveService $shjFiveService)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->shjFiveService = $shjFiveService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンソールコマンドを実行
|
||||||
|
*
|
||||||
|
* 処理フロー:
|
||||||
|
* 1. バッチログ開始記録
|
||||||
|
* 2. 駐輪場の空き状況を取得する
|
||||||
|
* 3. 空き状況判定
|
||||||
|
* 4. 空き待ち者の情報を取得する
|
||||||
|
* 5. 取得件数判定
|
||||||
|
* 6. 空き待ち者への通知、またはオペレーターキュー追加処理
|
||||||
|
* 7. バッチ処理ログを作成する
|
||||||
|
* 8. 処理結果返却
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$batchLog = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 開始ログ出力
|
||||||
|
$startTime = now();
|
||||||
|
$this->info('SHJ-5 空き待ち通知処理を開始します。');
|
||||||
|
|
||||||
|
Log::info('SHJ-5 空き待ち通知処理開始', [
|
||||||
|
'start_time' => $startTime
|
||||||
|
]);
|
||||||
|
|
||||||
|
// SHJ-8共通処理呼び出し - 仕様書準拠
|
||||||
|
$batchLog = BatchLog::createBatchLog(
|
||||||
|
'SHJ-5空き待ち通知処理',
|
||||||
|
BatchLog::STATUS_START,
|
||||||
|
[
|
||||||
|
'device_id' => 1, // 仕様書:device_id=1
|
||||||
|
'process_code' => 1, // 仕様書:process_code=1
|
||||||
|
'status_comment' => '処理開始' // 仕様書:内部変数.ステータスコメント
|
||||||
|
],
|
||||||
|
'SHJ-5処理開始: 駐輪場空き状況確認と空き待ち通知処理'
|
||||||
|
);
|
||||||
|
|
||||||
|
// SHJ-5メイン処理実行
|
||||||
|
$result = $this->shjFiveService->executeParkVacancyNotification();
|
||||||
|
|
||||||
|
$endTime = now();
|
||||||
|
$this->info('SHJ-5 空き待ち通知処理が完了しました。');
|
||||||
|
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
||||||
|
|
||||||
|
// 処理結果表示
|
||||||
|
$this->displayProcessResult($result);
|
||||||
|
|
||||||
|
// SHJ-8共通処理 - バッチログ完了記録(仕様書準拠)
|
||||||
|
$logStatus = $result['success'] ? BatchLog::STATUS_SUCCESS : BatchLog::STATUS_ERROR;
|
||||||
|
|
||||||
|
// 仕様書準拠:サービス側で作成した完全なステータスコメントを使用
|
||||||
|
$statusComment = $result['success'] ?
|
||||||
|
($result['status_comment'] ?? 'ステータスコメント生成エラー') :
|
||||||
|
sprintf('処理失敗: %s', $result['message'] ?? 'エラー');
|
||||||
|
|
||||||
|
$batchLog->update([
|
||||||
|
'status' => $logStatus,
|
||||||
|
'end_time' => $endTime,
|
||||||
|
'message' => $result['message'],
|
||||||
|
'parameters' => [
|
||||||
|
'device_id' => 1, // 仕様書:device_id=1
|
||||||
|
'process_code' => 1, // 仕様書:process_code=1
|
||||||
|
'status_comment' => $statusComment, // 仕様書:内部変数.ステータスコメント(完全版)
|
||||||
|
'processed_parks_count' => $result['processed_parks_count'] ?? 0,
|
||||||
|
'vacant_parks_count' => $result['vacant_parks_count'] ?? 0,
|
||||||
|
'total_waiting_users' => $result['total_waiting_users'] ?? 0,
|
||||||
|
'notification_success_count' => $result['notification_success_count'] ?? 0,
|
||||||
|
'operator_queue_count' => $result['operator_queue_count'] ?? 0,
|
||||||
|
'error_count' => $result['error_count'] ?? 0,
|
||||||
|
'duration_seconds' => $result['duration_seconds'] ?? 0
|
||||||
|
],
|
||||||
|
'execution_count' => 1,
|
||||||
|
'success_count' => $result['notification_success_count'] ?? 0,
|
||||||
|
'error_count' => $result['error_count'] ?? 0
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::info('SHJ-5 空き待ち通知処理完了', [
|
||||||
|
'end_time' => $endTime,
|
||||||
|
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
||||||
|
'result' => $result
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $result['success'] ? self::SUCCESS : self::FAILURE;
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('SHJ-5 空き待ち通知処理で予期しないエラーが発生しました: ' . $e->getMessage());
|
||||||
|
|
||||||
|
// SHJ-8共通処理 - エラーログ記録(仕様書準拠)
|
||||||
|
if ($batchLog) {
|
||||||
|
$statusComment = sprintf(
|
||||||
|
'メール正常終了件数:0/メール異常終了件数:1/キュー登録正常終了件数:0/キュー登録異常終了件数:1 - 処理エラー: %s',
|
||||||
|
$e->getMessage()
|
||||||
|
);
|
||||||
|
$batchLog->update([
|
||||||
|
'status' => BatchLog::STATUS_ERROR,
|
||||||
|
'end_time' => now(),
|
||||||
|
'message' => 'SHJ-5処理中にエラーが発生: ' . $e->getMessage(),
|
||||||
|
'parameters' => [
|
||||||
|
'device_id' => 1, // 仕様書:device_id=1
|
||||||
|
'process_code' => 1, // 仕様書:process_code=1
|
||||||
|
'status_comment' => $statusComment // 仕様書:内部変数.ステータスコメント(完全版)
|
||||||
|
],
|
||||||
|
'error_details' => $e->getMessage(),
|
||||||
|
'error_count' => 1
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::error('SHJ-5 空き待ち通知処理例外エラー', [
|
||||||
|
'exception' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 処理結果を表示
|
||||||
|
*
|
||||||
|
* @param array $result 処理結果
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function displayProcessResult(array $result): void
|
||||||
|
{
|
||||||
|
$this->line('');
|
||||||
|
$this->info('=== 処理結果 ===');
|
||||||
|
|
||||||
|
if ($result['success']) {
|
||||||
|
$this->info("✓ 処理実行成功: " . $result['message']);
|
||||||
|
} else {
|
||||||
|
$this->error("✗ 処理実行失敗: " . $result['message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->line('');
|
||||||
|
$this->info('=== 処理統計 ===');
|
||||||
|
$this->line(" 処理対象駐輪場数: " . ($result['processed_parks_count'] ?? 0));
|
||||||
|
$this->line(" 空きあり駐輪場数: " . ($result['vacant_parks_count'] ?? 0));
|
||||||
|
$this->line(" 空き待ち者総数: " . ($result['total_waiting_users'] ?? 0));
|
||||||
|
$this->line(" 通知送信成功: " . ($result['notification_success_count'] ?? 0) . "件");
|
||||||
|
$this->line(" オペレーターキュー追加: " . ($result['operator_queue_count'] ?? 0) . "件");
|
||||||
|
$this->line(" エラー件数: " . ($result['error_count'] ?? 0) . "件");
|
||||||
|
$this->line(" 処理時間: " . ($result['duration_seconds'] ?? 0) . "秒");
|
||||||
|
|
||||||
|
// エラー詳細があれば表示
|
||||||
|
if (!empty($result['errors'])) {
|
||||||
|
$this->line('');
|
||||||
|
$this->warn('=== エラー詳細 ===');
|
||||||
|
foreach ($result['errors'] as $index => $error) {
|
||||||
|
$this->line(" " . ($index + 1) . ". " . $error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
154
app/Console/Commands/ShjThirteenCommand.php
Normal file
154
app/Console/Commands/ShjThirteenCommand.php
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use App\Services\ShjThirteenService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHJ-13 契約台数追加処理コマンド
|
||||||
|
*
|
||||||
|
* 指定されたパラメータで契約台数をpark_number・zoneテーブルに反映する
|
||||||
|
* 主にSHJ-4Bから呼び出されるが、独立実行も可能
|
||||||
|
*/
|
||||||
|
class ShjThirteenCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* コンソールコマンドの名前とシグネチャ
|
||||||
|
*
|
||||||
|
* 引数:
|
||||||
|
* - park_id: 駐輪場ID (必須)
|
||||||
|
* - psection_id: 車種区分ID (必須)
|
||||||
|
* - ptype_id: 駐輪分類ID (必須)
|
||||||
|
* - zone_id: ゾーンID (必須)
|
||||||
|
*
|
||||||
|
* オプション:
|
||||||
|
* - contract_id: 契約ID (任意、ログ用)
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'shj:13
|
||||||
|
{park_id : 駐輪場ID}
|
||||||
|
{psection_id : 車種区分ID}
|
||||||
|
{ptype_id : 駐輪分類ID}
|
||||||
|
{zone_id : ゾーンID}
|
||||||
|
{--contract_id= : 契約ID(ログ用、任意)}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンソールコマンドの説明
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'SHJ-13 契約台数追加処理 - park_number・zoneテーブルの契約台数を+1更新しログ記録';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHJ-13サービスクラス
|
||||||
|
*
|
||||||
|
* @var ShjThirteenService
|
||||||
|
*/
|
||||||
|
protected $shjThirteenService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンストラクタ
|
||||||
|
*
|
||||||
|
* @param ShjThirteenService $shjThirteenService
|
||||||
|
*/
|
||||||
|
public function __construct(ShjThirteenService $shjThirteenService)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->shjThirteenService = $shjThirteenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* コンソールコマンドを実行
|
||||||
|
*
|
||||||
|
* 処理フロー:
|
||||||
|
* 1. 引数取得・検証
|
||||||
|
* 2. ShjThirteenService実行
|
||||||
|
* 3. 結果表示
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 開始ログ出力
|
||||||
|
$startTime = now();
|
||||||
|
$this->info('SHJ-13 契約台数追加処理を開始します。');
|
||||||
|
|
||||||
|
// 引数取得
|
||||||
|
$contractData = [
|
||||||
|
'park_id' => (int) $this->argument('park_id'),
|
||||||
|
'psection_id' => (int) $this->argument('psection_id'),
|
||||||
|
'ptype_id' => (int) $this->argument('ptype_id'),
|
||||||
|
'zone_id' => (int) $this->argument('zone_id'),
|
||||||
|
'contract_id' => $this->option('contract_id'),
|
||||||
|
];
|
||||||
|
|
||||||
|
Log::info('SHJ-13 契約台数追加処理開始', [
|
||||||
|
'start_time' => $startTime,
|
||||||
|
'contract_data' => $contractData,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->info("駐輪場ID: {$contractData['park_id']}");
|
||||||
|
$this->info("車種区分ID: {$contractData['psection_id']}");
|
||||||
|
$this->info("駐輪分類ID: {$contractData['ptype_id']}");
|
||||||
|
$this->info("ゾーンID: {$contractData['zone_id']}");
|
||||||
|
if ($contractData['contract_id']) {
|
||||||
|
$this->info("契約ID: {$contractData['contract_id']}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHJ-13処理実行
|
||||||
|
$this->info('【処理実行】契約台数追加処理を実行しています...');
|
||||||
|
$result = $this->shjThirteenService->execute($contractData);
|
||||||
|
|
||||||
|
// 処理結果確認・表示
|
||||||
|
if ($result['result'] === 0) {
|
||||||
|
$endTime = now();
|
||||||
|
$this->info('SHJ-13 契約台数追加処理が正常に完了しました。');
|
||||||
|
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
|
||||||
|
|
||||||
|
Log::info('SHJ-13 契約台数追加処理完了', [
|
||||||
|
'end_time' => $endTime,
|
||||||
|
'duration_seconds' => $startTime->diffInSeconds($endTime),
|
||||||
|
'contract_data' => $contractData,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 成功時の結果出力
|
||||||
|
$this->line('処理結果: 0'); // 0 = 正常終了
|
||||||
|
$this->line('異常情報: '); // 正常時は空文字
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
} else {
|
||||||
|
$this->error('SHJ-13 契約台数追加処理でエラーが発生しました。');
|
||||||
|
$this->error("エラーコード: {$result['error_code']}");
|
||||||
|
$this->error("エラーメッセージ: {$result['error_message']}");
|
||||||
|
|
||||||
|
Log::error('SHJ-13 契約台数追加処理エラー', [
|
||||||
|
'contract_data' => $contractData,
|
||||||
|
'error_result' => $result,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// エラー時の結果出力
|
||||||
|
$this->line('処理結果: 1'); // 1 = 異常終了
|
||||||
|
$this->line('異常情報: ' . $result['error_message']);
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('SHJ-13 契約台数追加処理で予期しないエラーが発生しました: ' . $e->getMessage());
|
||||||
|
Log::error('SHJ-13 契約台数追加処理例外エラー', [
|
||||||
|
'exception' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 例外時の結果出力
|
||||||
|
$this->line('処理結果: 1'); // 1 = 異常終了
|
||||||
|
$this->line('異常情報: システムエラー: ' . $e->getMessage());
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,14 +31,10 @@ class ParkingSearchController extends Controller
|
|||||||
// 検索処理
|
// 検索処理
|
||||||
public function getParkData($city_name, $station_neighbor_station, $park_name)
|
public function getParkData($city_name, $station_neighbor_station, $park_name)
|
||||||
{
|
{
|
||||||
// 検索仕様
|
|
||||||
// 駐輪場マスタの全件(条件を絞った場合はその条件に一致するもの)を取得。
|
|
||||||
// 併せて各マスタから追加情報を取得するが、その際のレコードは全て1対1で結びつく想定で暫定実装する
|
|
||||||
// ※設計書に詳細な記載なし。DBの定義上は1対多の可能性もあるが、その場合現在の画面イメージと矛盾するため、実態として無い想定で進める
|
|
||||||
|
|
||||||
// 駐輪場情報検索
|
// 駐輪場情報検索
|
||||||
$park = \DB::table('park as p')
|
$park = \DB::table('park as p')
|
||||||
->select(
|
->select(
|
||||||
|
'p.park_id',
|
||||||
'p.park_name',
|
'p.park_name',
|
||||||
'p.park_adrs',
|
'p.park_adrs',
|
||||||
'p.price_memo',
|
'p.price_memo',
|
||||||
@ -49,15 +45,12 @@ class ParkingSearchController extends Controller
|
|||||||
'p.update_grace_period_end_date',
|
'p.update_grace_period_end_date',
|
||||||
'p.update_grace_period_end_time',
|
'p.update_grace_period_end_time',
|
||||||
'c.city_name',
|
'c.city_name',
|
||||||
's.station_neighbor_station',
|
's.station_neighbor_station'
|
||||||
'z.psection_id',
|
|
||||||
'z.zone_standard'
|
|
||||||
)
|
)
|
||||||
->leftJoin('city as c', 'p.city_id', '=', 'c.city_id')
|
->leftJoin('city as c', 'p.city_id', '=', 'c.city_id')
|
||||||
->leftJoin(\DB::raw(
|
->leftJoin(\DB::raw(
|
||||||
'(SELECT park_id, station_neighbor_station FROM station WHERE station_id IN (SELECT MAX(station_id) FROM station GROUP BY park_id)) as s'
|
'(SELECT park_id, station_neighbor_station FROM station WHERE station_id IN (SELECT MAX(station_id) FROM station GROUP BY park_id)) as s'
|
||||||
),'p.park_id','=','s.park_id')
|
),'p.park_id','=','s.park_id');
|
||||||
->leftJoin('zone as z', 'p.park_id', '=', 'z.park_id');
|
|
||||||
|
|
||||||
// プルダウン指定の条件でwhere句を付与
|
// プルダウン指定の条件でwhere句を付与
|
||||||
if (!empty($city_name)) {
|
if (!empty($city_name)) {
|
||||||
@ -76,42 +69,102 @@ class ParkingSearchController extends Controller
|
|||||||
$now = date('Y-m-d H:i:s');
|
$now = date('Y-m-d H:i:s');
|
||||||
foreach ($park as $row) {
|
foreach ($park as $row) {
|
||||||
|
|
||||||
// ゾーンマスタの情報から空き台数を取得する
|
// ゾーンマスタの情報を取得する
|
||||||
$vacantInfo = \DB::table('zone')
|
$zoneInfo = \DB::table('zone as z')
|
||||||
->selectRaw('SUM(zone_tolerance) - SUM(zone_number) as vacant')
|
->select(
|
||||||
->where('psection_id', $row->psection_id)
|
'z.psection_id',
|
||||||
->groupBy('psection_id')
|
'z.ptype_id',
|
||||||
->first();
|
\DB::raw('SUM(z.zone_standard) as zone_standard'),
|
||||||
|
\DB::raw('SUM(z.zone_number) as zone_number'),
|
||||||
|
\DB::raw('SUM(z.zone_tolerance) as zone_tolerance'),
|
||||||
|
'ps.psection_subject',
|
||||||
|
'pt.ptype_subject'
|
||||||
|
)
|
||||||
|
->join('ptype as pt', 'z.ptype_id', '=', 'pt.ptype_id')
|
||||||
|
->leftJoin('psection as ps', 'z.psection_id', '=', 'ps.psection_id')
|
||||||
|
->where('z.park_id', $row->park_id)
|
||||||
|
->groupBy('z.park_id', 'z.ptype_id', 'z.psection_id', 'ps.psection_subject', 'pt.ptype_subject')
|
||||||
|
->get();
|
||||||
|
|
||||||
// 定期予約マスタから予約中の台数を取得する
|
// ゾーンマスタが0件の場合、次のデータへ
|
||||||
$reservedCount = \DB::table('reserve')
|
if ($zoneInfo->isEmpty()) {
|
||||||
->where('psection_id', $row->psection_id)
|
$form_data[] = [
|
||||||
->where('valid_flag', 1)
|
'park_name' => $row->park_name,
|
||||||
->count();
|
'park_adrs' => $row->park_adrs,
|
||||||
|
'price_memo' => $row->price_memo,
|
||||||
|
'park_latitude' => $row->park_latitude,
|
||||||
|
'park_longitude' => $row->park_longitude,
|
||||||
|
'city_name' => $row->city_name,
|
||||||
|
'station_neighbor_station' => $row->station_neighbor_station,
|
||||||
|
'zone_data' => []
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 更新期間内判定
|
// 更新期間内判定
|
||||||
$update_start = $row->update_grace_period_start_date . ' ' . $row->update_grace_period_start_time;
|
$update_start = $row->update_grace_period_start_date . ' ' . $row->update_grace_period_start_time;
|
||||||
$update_end = $row->update_grace_period_end_date . ' ' . $row->update_grace_period_end_time;
|
$update_end = $row->update_grace_period_end_date . ' ' . $row->update_grace_period_end_time;
|
||||||
$is_update_period = ($now >= $update_start && $now <= $update_end);
|
$is_update_period = ($now >= $update_start && $now <= $update_end);
|
||||||
|
|
||||||
// ボタン表示有無判定
|
// ゾーンマスタの件数分だけループする
|
||||||
$vacant = ($vacantInfo ? $vacantInfo->vacant : 0) - $reservedCount;
|
$zone_data = [];
|
||||||
if ($vacant > 0 && $is_update_period) { // 定期契約ボタン (空き台数が1台以上かつ更新期間内)
|
foreach ($zoneInfo as $zone) {
|
||||||
|
|
||||||
|
// 予約中件数取得
|
||||||
|
$reservedCount = \DB::table('reserve')
|
||||||
|
->where('park_id', $row->park_id)
|
||||||
|
->where('psection_id', $zone->psection_id)
|
||||||
|
->where('ptype_id', $zone->ptype_id)
|
||||||
|
->where('valid_flag', 1)
|
||||||
|
->count();
|
||||||
|
|
||||||
|
// ステータス(表示ボタン)判定
|
||||||
|
$status = 0; // 0:非表示, 1:定期契約, 2:空き待ち予約, 3:販売期間外
|
||||||
|
$zone_vacant = $zone->zone_tolerance - $zone->zone_number - $reservedCount;
|
||||||
|
if ($zone_vacant > 0 && $is_update_period) { // 定期契約ボタン (空き台数が1台以上かつ更新期間内)
|
||||||
$status = 1;
|
$status = 1;
|
||||||
} elseif ($vacant <= 0 && $is_update_period) { // 空き待ち予約ボタン (空き台数が0台以下かつ更新期間内)
|
} elseif ($zone_vacant <= 0 && $is_update_period) { // 空き待ち予約ボタン (空き台数が0台以下かつ更新期間内)
|
||||||
$status = 2;
|
$status = 2;
|
||||||
} elseif ($vacant <= 0 && !$is_update_period) { // 販売期間外ボタン (空き台数が0台以下かつ更新期間外)
|
} elseif ($zone_vacant <= 0 && !$is_update_period) { // 販売期間外ボタン (空き台数が0台以下かつ更新期間外)
|
||||||
$status = 3;
|
$status = 3;
|
||||||
} else {
|
|
||||||
$status = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返却用データに追加
|
||||||
|
$zone_data[] = [
|
||||||
|
'psection_subject' => $zone->psection_subject,
|
||||||
|
'ptype_subject' => $zone->ptype_subject,
|
||||||
|
'zone_standard' => $zone->zone_standard,
|
||||||
|
'zone_vacant' => $zone_vacant,
|
||||||
|
'status' => $status
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// $zone_dataを並び替え
|
||||||
|
usort($zone_data, function($a, $b) {
|
||||||
|
// 第一優先: ptype_subject昇順
|
||||||
|
$ptypeCmp = strcmp($a['ptype_subject'], $b['ptype_subject']);
|
||||||
|
if ($ptypeCmp !== 0) {
|
||||||
|
return $ptypeCmp;
|
||||||
|
}
|
||||||
|
// 第二優先: psection_subject昇順
|
||||||
|
$psectionCmp = strcmp($a['psection_subject'], $b['psection_subject']);
|
||||||
|
if ($psectionCmp !== 0) {
|
||||||
|
return $psectionCmp;
|
||||||
|
}
|
||||||
|
// 第三優先: status昇順
|
||||||
|
return $a['status'] <=> $b['status'];
|
||||||
|
});
|
||||||
|
|
||||||
// 画面返却用データに追加
|
// 画面返却用データに追加
|
||||||
$form_data[] = [
|
$form_data[] = [
|
||||||
'park_name' => $row->park_name,
|
'park_name' => $row->park_name,
|
||||||
|
'park_adrs' => $row->park_adrs,
|
||||||
|
'price_memo' => $row->price_memo,
|
||||||
|
'park_latitude' => $row->park_latitude,
|
||||||
|
'park_longitude' => $row->park_longitude,
|
||||||
'city_name' => $row->city_name,
|
'city_name' => $row->city_name,
|
||||||
'station_neighbor_station' => $row->station_neighbor_station,
|
'station_neighbor_station' => $row->station_neighbor_station,
|
||||||
'status' => $status
|
'zone_data' => $zone_data
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +186,10 @@ class ParkingSearchController extends Controller
|
|||||||
'わ行'=>'わをんワヲン '
|
'わ行'=>'わをんワヲン '
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 車種区分リスト取得
|
||||||
|
$psections = \DB::table('psection')->select('psection_subject')->orderBy('psection_id', 'asc')->limit(4)->get();
|
||||||
|
|
||||||
// 情報返却
|
// 情報返却
|
||||||
return ['form_data' => $form_data, 'cities' => $cities, 'stations' => $stations, 'parks' => $parks, 'conditions' => $conditions];
|
return ['form_data' => $form_data, 'conditions' => $conditions, 'cities' => $cities, 'stations' => $stations, 'parks' => $parks, 'psections' => $psections];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
650
app/Services/ShjFiveService.php
Normal file
650
app/Services/ShjFiveService.php
Normal file
@ -0,0 +1,650 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use App\Models\OperatorQue;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHJ-5 空き待ち通知処理サービス
|
||||||
|
*
|
||||||
|
* 駐輪場の空き状況を確認し、空き待ち予約者への通知処理を実行する
|
||||||
|
* 仕様書に基づくバックグラウンド定期バッチ処理
|
||||||
|
*/
|
||||||
|
class ShjFiveService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* SHJ-5 メイン処理を実行
|
||||||
|
*
|
||||||
|
* 処理フロー:
|
||||||
|
* 1. 駐輪場の空き状況を取得する
|
||||||
|
* 2. 空き状況判定
|
||||||
|
* 3. 空き待ち者の情報を取得する
|
||||||
|
* 4. 取得件数判定
|
||||||
|
* 5. 空き待ち者への通知、またはオペレーターキュー追加処理
|
||||||
|
* 6. バッチ処理ログを作成する
|
||||||
|
*
|
||||||
|
* @return array 処理結果
|
||||||
|
*/
|
||||||
|
public function executeParkVacancyNotification(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$startTime = now();
|
||||||
|
Log::info('SHJ-5 空き待ち通知処理開始');
|
||||||
|
|
||||||
|
// 処理統計
|
||||||
|
$processedParksCount = 0;
|
||||||
|
$vacantParksCount = 0;
|
||||||
|
$totalWaitingUsers = 0;
|
||||||
|
$notificationSuccessCount = 0;
|
||||||
|
$operatorQueueCount = 0;
|
||||||
|
$mailErrors = []; // メール異常終了件数専用
|
||||||
|
$errors = []; // 全体エラー収集用
|
||||||
|
$allQueueItems = []; // 全オペレーターキュー作成用データ
|
||||||
|
|
||||||
|
// 【処理1】駐輪場の空き状況を取得する
|
||||||
|
$parkVacancyList = $this->getParkVacancyStatus();
|
||||||
|
Log::info('駐輪場空き状況取得完了', [
|
||||||
|
'total_parks' => count($parkVacancyList)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 各駐輪場に対する処理
|
||||||
|
foreach ($parkVacancyList as $parkVacancyData) {
|
||||||
|
// 配列をオブジェクトに変換
|
||||||
|
$parkVacancy = (object) $parkVacancyData;
|
||||||
|
$processedParksCount++;
|
||||||
|
|
||||||
|
Log::info('駐輪場処理開始', [
|
||||||
|
'park_id' => $parkVacancy->park_id,
|
||||||
|
'park_name' => $parkVacancy->park_name,
|
||||||
|
'psection_id' => $parkVacancy->psection_id,
|
||||||
|
'ptype_id' => $parkVacancy->ptype_id,
|
||||||
|
'vacant_count' => $parkVacancy->vacant_count
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 【判断1】空き状況判定
|
||||||
|
if ($parkVacancy->vacant_count < 1) {
|
||||||
|
Log::info('空きなし - 処理スキップ', [
|
||||||
|
'park_id' => $parkVacancy->park_id,
|
||||||
|
'vacant_count' => $parkVacancy->vacant_count
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$vacantParksCount++;
|
||||||
|
|
||||||
|
// 【処理2】空き待ち者の情報を取得する
|
||||||
|
$waitingUsers = $this->getWaitingUsersInfo(
|
||||||
|
$parkVacancy->park_id,
|
||||||
|
$parkVacancy->psection_id,
|
||||||
|
$parkVacancy->ptype_id
|
||||||
|
);
|
||||||
|
|
||||||
|
// 【判断2】取得件数判定
|
||||||
|
if (empty($waitingUsers)) {
|
||||||
|
Log::info('空き待ち者なし', [
|
||||||
|
'park_id' => $parkVacancy->park_id
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalWaitingUsers += count($waitingUsers);
|
||||||
|
Log::info('空き待ち者情報取得完了', [
|
||||||
|
'park_id' => $parkVacancy->park_id,
|
||||||
|
'waiting_users_count' => count($waitingUsers)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 【処理3】空き待ち者への通知、またはオペレーターキュー追加処理
|
||||||
|
$notificationResult = $this->processWaitingUsersNotification(
|
||||||
|
$waitingUsers,
|
||||||
|
$parkVacancy
|
||||||
|
);
|
||||||
|
|
||||||
|
$notificationSuccessCount += $notificationResult['notification_success_count'];
|
||||||
|
$operatorQueueCount += $notificationResult['operator_queue_count'];
|
||||||
|
|
||||||
|
if (!empty($notificationResult['errors'])) {
|
||||||
|
$mailErrors = array_merge($mailErrors, $notificationResult['errors']);
|
||||||
|
$errors = array_merge($errors, $notificationResult['errors']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// オペレーターキュー作成用データを収集
|
||||||
|
if (!empty($notificationResult['queue_items'])) {
|
||||||
|
$allQueueItems = array_merge($allQueueItems ?? [], $notificationResult['queue_items']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【処理4】仕様書準拠:先に呼び出し、成功時のみ内部変数を更新
|
||||||
|
$queueErrorCount = 0; // キュー登録異常終了件数(累計)
|
||||||
|
$queueSuccessCount = 0; // キュー登録正常終了件数(累計)
|
||||||
|
|
||||||
|
foreach ($allQueueItems as $queueItem) {
|
||||||
|
// 仕様書準拠:在呼叫前先計算"如果這次成功會是第幾件",確保記錄反映最新件數
|
||||||
|
$predictedSuccessCount = $queueSuccessCount + 1;
|
||||||
|
$predictedErrorCount = $queueErrorCount; // 暂时保持当前错误计数
|
||||||
|
|
||||||
|
$queueResult = $this->addToOperatorQueue(
|
||||||
|
$queueItem['waiting_user'],
|
||||||
|
$queueItem['park_vacancy'],
|
||||||
|
$queueItem['batch_comment'],
|
||||||
|
$notificationSuccessCount, // 最終メール正常終了件数
|
||||||
|
$predictedSuccessCount, // 預測成功時的件數(包含本次)
|
||||||
|
count($mailErrors), // 現在のメール異常終了件数(動態計算)
|
||||||
|
$predictedErrorCount // 現在のキュー登録異常終了件数
|
||||||
|
);
|
||||||
|
|
||||||
|
// 仕様書:根据实际结果决定是否采用预测值
|
||||||
|
if ($queueResult['success']) {
|
||||||
|
$queueSuccessCount = $predictedSuccessCount; // 采用预测的成功计数
|
||||||
|
} else {
|
||||||
|
$queueErrorCount++; // 失败时递增错误计数
|
||||||
|
|
||||||
|
// 仕様書:包含具体错误消息,满足"エラーメッセージ/スタックトレースを保持"要求
|
||||||
|
$errorDetail = $queueResult['error'] ?? 'Unknown error';
|
||||||
|
$queueErrorInfo = sprintf('キュー登録失敗:予約ID:%d - %s',
|
||||||
|
$queueItem['waiting_user']->reserve_id ?? 0,
|
||||||
|
$errorDetail
|
||||||
|
);
|
||||||
|
$errors[] = $queueErrorInfo; // 加入总错误统计(包含具体原因)
|
||||||
|
|
||||||
|
Log::error('オペレーターキュー作成失敗', [
|
||||||
|
'user_id' => $queueItem['waiting_user']->user_id,
|
||||||
|
'reserve_id' => $queueItem['waiting_user']->reserve_id ?? 0,
|
||||||
|
'error' => $errorDetail
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$endTime = now();
|
||||||
|
$duration = $startTime->diffInSeconds($endTime);
|
||||||
|
|
||||||
|
Log::info('SHJ-5 空き待ち通知処理完了', [
|
||||||
|
'duration_seconds' => $duration,
|
||||||
|
'processed_parks_count' => $processedParksCount,
|
||||||
|
'vacant_parks_count' => $vacantParksCount,
|
||||||
|
'total_waiting_users' => $totalWaitingUsers,
|
||||||
|
'notification_success_count' => $notificationSuccessCount,
|
||||||
|
'operator_queue_success_count' => $queueSuccessCount, // 仕様書:正常完了件数
|
||||||
|
'queue_error_count' => $queueErrorCount,
|
||||||
|
'mail_error_count' => count($mailErrors), // メール異常終了件数(分離)
|
||||||
|
'total_error_count' => count($errors) // 全体エラー件数
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 仕様書に基づく内部変数.ステータスコメント生成
|
||||||
|
$statusComment = sprintf(
|
||||||
|
'メール正常終了件数:%d/メール異常終了件数:%d/キュー登録正常終了件数:%d/キュー登録異常終了件数:%d',
|
||||||
|
$notificationSuccessCount,
|
||||||
|
count($mailErrors), // メール異常終了件数(キュー失敗を除外)
|
||||||
|
$queueSuccessCount ?? 0, // 実際のキュー登録成功件数
|
||||||
|
$queueErrorCount ?? 0 // 実際のキュー登録失敗件数
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'SHJ-5 空き待ち通知処理が正常に完了しました',
|
||||||
|
'processed_parks_count' => $processedParksCount,
|
||||||
|
'vacant_parks_count' => $vacantParksCount,
|
||||||
|
'total_waiting_users' => $totalWaitingUsers,
|
||||||
|
'notification_success_count' => $notificationSuccessCount,
|
||||||
|
'operator_queue_count' => $queueSuccessCount ?? 0, // 仕様書:正常完了件数を使用
|
||||||
|
'error_count' => count($errors),
|
||||||
|
'errors' => $errors,
|
||||||
|
'duration_seconds' => $duration,
|
||||||
|
'status_comment' => $statusComment // SHJ-8用の完全なステータスコメント
|
||||||
|
];
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('SHJ-5 空き待ち通知処理でエラーが発生', [
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'SHJ-5 空き待ち通知処理でエラーが発生: ' . $e->getMessage(),
|
||||||
|
'error_details' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【処理1】駐輪場の空き状況を取得する
|
||||||
|
*
|
||||||
|
* 仕様書に基づくSQL:
|
||||||
|
* - zone表から標準台数と現在の契約台数を比較
|
||||||
|
* - 空きがある駐輪場の情報を取得
|
||||||
|
*
|
||||||
|
* @return array 駐輪場空き状況リスト
|
||||||
|
*/
|
||||||
|
private function getParkVacancyStatus(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// ゾーン毎の契約台数を取得
|
||||||
|
$contractCounts = DB::table('regular_contract as T1')
|
||||||
|
->select([
|
||||||
|
'T1.park_id',
|
||||||
|
'T1.psection_id',
|
||||||
|
'T5.ptype_id',
|
||||||
|
DB::raw('count(T1.contract_id) as contract_count')
|
||||||
|
])
|
||||||
|
->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
|
||||||
|
->join('price_a as T5', function($join) {
|
||||||
|
$join->on('T1.park_id', '=', 'T5.park_id')
|
||||||
|
->on('T1.price_parkplaceid', '=', 'T5.price_parkplaceid')
|
||||||
|
->on('T1.psection_id', '=', 'T5.psection_id');
|
||||||
|
})
|
||||||
|
->where([
|
||||||
|
['T1.contract_flag', '=', 1], // 有効契約
|
||||||
|
['T2.park_close_flag', '=', 0], // 駐輪場未閉鎖
|
||||||
|
])
|
||||||
|
// 契約有効期間内の条件
|
||||||
|
->whereRaw("date_format(now(), '%Y%m%d') BETWEEN T1.contract_periods AND T1.contract_periode")
|
||||||
|
->groupBy(['T1.park_id', 'T1.psection_id', 'T5.ptype_id'])
|
||||||
|
->get()
|
||||||
|
->keyBy(function($item) {
|
||||||
|
return $item->park_id . '_' . $item->psection_id . '_' . $item->ptype_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ゾーン情報と照合して空き状況を算出
|
||||||
|
$vacancyList = DB::table('zone as T1')
|
||||||
|
->select([
|
||||||
|
'T1.park_id',
|
||||||
|
'T2.park_name',
|
||||||
|
'T1.psection_id',
|
||||||
|
'T1.ptype_id',
|
||||||
|
'T1.zone_standard',
|
||||||
|
'T3.psection_subject',
|
||||||
|
'T4.ptype_subject'
|
||||||
|
])
|
||||||
|
->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
|
||||||
|
->join('psection as T3', 'T1.psection_id', '=', 'T3.psection_id')
|
||||||
|
->join('ptype as T4', 'T1.ptype_id', '=', 'T4.ptype_id')
|
||||||
|
->where([
|
||||||
|
['T1.delete_flag', '=', 0], // ゾーン有効
|
||||||
|
['T2.park_close_flag', '=', 0], // 駐輪場開設
|
||||||
|
])
|
||||||
|
->get()
|
||||||
|
->map(function($zone) use ($contractCounts) {
|
||||||
|
$key = $zone->park_id . '_' . $zone->psection_id . '_' . $zone->ptype_id;
|
||||||
|
$contractCount = isset($contractCounts[$key]) ? $contractCounts[$key]->contract_count : 0;
|
||||||
|
|
||||||
|
$zone->contract_count = $contractCount;
|
||||||
|
$zone->vacant_count = max(0, $zone->zone_standard - $contractCount);
|
||||||
|
|
||||||
|
return $zone;
|
||||||
|
})
|
||||||
|
->filter(function($zone) {
|
||||||
|
return $zone->vacant_count > 0; // 空きがあるもののみ
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
|
||||||
|
Log::info('駐輪場空き状況算出完了', [
|
||||||
|
'total_zones' => count($vacancyList),
|
||||||
|
'vacant_zones' => $vacancyList->filter(function($v) {
|
||||||
|
return $v->vacant_count > 0;
|
||||||
|
})->count()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $vacancyList->toArray();
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('駐輪場空き状況取得エラー', [
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【処理2】空き待ち者の情報を取得する
|
||||||
|
*
|
||||||
|
* 仕様書に基づく取得条件:
|
||||||
|
* - 契約未紐付(contract_id IS NULL)
|
||||||
|
* - 退会でない(user_quit_flag <> 1)
|
||||||
|
* - 有効な予約(valid_flag = 1)
|
||||||
|
* - 予約日時順で取得(reserve_date昇順)
|
||||||
|
*
|
||||||
|
* @param int $parkId 駐輪場ID
|
||||||
|
* @param int $psectionId 車種区分ID
|
||||||
|
* @param int $ptypeId 駐輪分類ID
|
||||||
|
* @return array 空き待ち者情報リスト
|
||||||
|
*/
|
||||||
|
private function getWaitingUsersInfo(int $parkId, int $psectionId, int $ptypeId): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$waitingUsers = DB::table('reserve as T1')
|
||||||
|
->select([
|
||||||
|
'T1.reserve_id',
|
||||||
|
'T1.user_id',
|
||||||
|
'T1.park_id',
|
||||||
|
'T1.psection_id',
|
||||||
|
'T1.ptype_id',
|
||||||
|
'T1.reserve_order',
|
||||||
|
'T1.reserve_date',
|
||||||
|
'T1.reserve_manual', // 手動通知フラグ
|
||||||
|
'T1.contract_id', // 契約紐付確認用
|
||||||
|
'T2.user_name',
|
||||||
|
'T2.user_primemail',
|
||||||
|
'T2.user_submail', // 副メールアドレス
|
||||||
|
'T2.user_manual_regist_flag', // 手動登録フラグ
|
||||||
|
'T2.user_quit_flag', // 退会フラグ
|
||||||
|
'T3.park_name',
|
||||||
|
'T4.psection_subject',
|
||||||
|
'T5.ptype_subject'
|
||||||
|
])
|
||||||
|
->join('user as T2', 'T1.user_id', '=', 'T2.user_id')
|
||||||
|
->join('park as T3', 'T1.park_id', '=', 'T3.park_id')
|
||||||
|
->join('psection as T4', 'T1.psection_id', '=', 'T4.psection_id')
|
||||||
|
->join('ptype as T5', 'T1.ptype_id', '=', 'T5.ptype_id')
|
||||||
|
->where([
|
||||||
|
['T1.park_id', '=', $parkId],
|
||||||
|
['T1.psection_id', '=', $psectionId],
|
||||||
|
['T1.ptype_id', '=', $ptypeId],
|
||||||
|
['T1.valid_flag', '=', 1], // 有効な予約
|
||||||
|
['T2.user_quit_flag', '<>', 1] // 退会でない
|
||||||
|
])
|
||||||
|
->whereNull('T1.contract_id') // 契約未紐付
|
||||||
|
->orderBy('T1.reserve_date', 'asc') // 仕様書に基づく予約日時順
|
||||||
|
->get()
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
Log::info('空き待ち者情報取得完了', [
|
||||||
|
'park_id' => $parkId,
|
||||||
|
'psection_id' => $psectionId,
|
||||||
|
'ptype_id' => $ptypeId,
|
||||||
|
'waiting_users_count' => count($waitingUsers)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $waitingUsers;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('空き待ち者情報取得エラー', [
|
||||||
|
'park_id' => $parkId,
|
||||||
|
'psection_id' => $psectionId,
|
||||||
|
'ptype_id' => $ptypeId,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【処理3】空き待ち者への通知、またはオペレーターキュー追加処理
|
||||||
|
*
|
||||||
|
* 仕様書に基づく分岐処理:
|
||||||
|
* - 手動通知フラグ判定(reserve_manual)
|
||||||
|
* - メール送信成功時のreserve.sent_date更新
|
||||||
|
* - 失敗時のオペレーターキュー追加(最終統計で処理)
|
||||||
|
*
|
||||||
|
* @param array $waitingUsers 空き待ち者リスト
|
||||||
|
* @param object $parkVacancy 駐輪場空き情報
|
||||||
|
* @return array 通知処理結果
|
||||||
|
*/
|
||||||
|
private function processWaitingUsersNotification(array $waitingUsers, object $parkVacancy): array
|
||||||
|
{
|
||||||
|
$notificationSuccessCount = 0;
|
||||||
|
$operatorQueueCount = 0;
|
||||||
|
$errors = [];
|
||||||
|
$queueItems = []; // オペレーターキュー作成用データ収集
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 空きがある分だけ処理(先着順)
|
||||||
|
$availableSpots = min($parkVacancy->vacant_count, count($waitingUsers));
|
||||||
|
|
||||||
|
for ($i = 0; $i < $availableSpots; $i++) {
|
||||||
|
$waitingUserData = $waitingUsers[$i];
|
||||||
|
// 配列をオブジェクトに変換
|
||||||
|
$waitingUser = (object) $waitingUserData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 【仕様判断】手動通知フラグチェック
|
||||||
|
if ($waitingUser->reserve_manual == 1) {
|
||||||
|
// 手動通知 → オペレーターキュー作成データ収集
|
||||||
|
$batchComment = '手動通知フラグ設定のため予約ID:' . $waitingUser->reserve_id;
|
||||||
|
$queueItems[] = [
|
||||||
|
'waiting_user' => $waitingUser,
|
||||||
|
'park_vacancy' => $parkVacancy,
|
||||||
|
'batch_comment' => $batchComment
|
||||||
|
];
|
||||||
|
$operatorQueueCount++;
|
||||||
|
|
||||||
|
Log::info('手動通知フラグによりオペレーターキュー登録予定', [
|
||||||
|
'user_id' => $waitingUser->user_id,
|
||||||
|
'reserve_id' => $waitingUser->reserve_id
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// 自動通知 → メール送信を試行
|
||||||
|
$mailResult = $this->sendVacancyNotificationMail($waitingUser, $parkVacancy);
|
||||||
|
|
||||||
|
if ($mailResult['success']) {
|
||||||
|
// メール送信成功 → reserve.sent_date更新
|
||||||
|
$this->updateReserveSentDate($waitingUser->reserve_id);
|
||||||
|
$notificationSuccessCount++;
|
||||||
|
|
||||||
|
Log::info('空き待ち通知メール送信成功', [
|
||||||
|
'user_id' => $waitingUser->user_id,
|
||||||
|
'reserve_id' => $waitingUser->reserve_id,
|
||||||
|
'park_id' => $parkVacancy->park_id
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// メール送信失敗 → オペレーターキュー作成データ収集
|
||||||
|
$shjSevenError = $mailResult['error'] ?? $mailResult['message'] ?? 'SHJ-7メール送信エラー';
|
||||||
|
$batchComment = $shjSevenError . '予約ID:' . $waitingUser->reserve_id;
|
||||||
|
$queueItems[] = [
|
||||||
|
'waiting_user' => $waitingUser,
|
||||||
|
'park_vacancy' => $parkVacancy,
|
||||||
|
'batch_comment' => $batchComment
|
||||||
|
];
|
||||||
|
$operatorQueueCount++;
|
||||||
|
$errors[] = $shjSevenError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('空き待ち者通知処理エラー', [
|
||||||
|
'user_id' => $waitingUser->user_id,
|
||||||
|
'reserve_id' => $waitingUser->reserve_id,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
|
||||||
|
// エラー発生時もオペレーターキュー作成データ収集
|
||||||
|
$batchComment = 'システムエラー:' . $e->getMessage() . '予約ID:' . $waitingUser->reserve_id;
|
||||||
|
$queueItems[] = [
|
||||||
|
'waiting_user' => $waitingUser,
|
||||||
|
'park_vacancy' => $parkVacancy,
|
||||||
|
'batch_comment' => $batchComment
|
||||||
|
];
|
||||||
|
$operatorQueueCount++;
|
||||||
|
$errors[] = $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'notification_success_count' => $notificationSuccessCount,
|
||||||
|
'operator_queue_count' => $operatorQueueCount,
|
||||||
|
'errors' => $errors,
|
||||||
|
'queue_items' => $queueItems // 後でキュー作成用
|
||||||
|
];
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('空き待ち者通知処理全体エラー', [
|
||||||
|
'park_id' => $parkVacancy->park_id,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空き待ち通知メールを送信
|
||||||
|
*
|
||||||
|
* 仕様書に基づくSHJ-7呼び出し:
|
||||||
|
* - 主メールアドレス・副メールアドレスを正しく渡す
|
||||||
|
* - 必要なパラメータを全て設定
|
||||||
|
*
|
||||||
|
* @param object $waitingUser 空き待ち者情報
|
||||||
|
* @param object $parkVacancy 駐輪場空き情報
|
||||||
|
* @return array 送信結果
|
||||||
|
*/
|
||||||
|
private function sendVacancyNotificationMail(object $waitingUser, object $parkVacancy): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// ShjMailSendServiceを利用してメール送信
|
||||||
|
$mailService = app(ShjMailSendService::class);
|
||||||
|
|
||||||
|
// 空き待ち通知用のメールテンプレートID(予約告知通知)
|
||||||
|
// OperatorQueの定数と合わせて4番を使用
|
||||||
|
$mailTemplateId = 4; // 予約告知通知のテンプレートID
|
||||||
|
|
||||||
|
// 仕様書No1/No2に基づく主メール・副メール設定
|
||||||
|
$mainEmail = $waitingUser->user_primemail ?? '';
|
||||||
|
$subEmail = $waitingUser->user_submail ?? '';
|
||||||
|
|
||||||
|
// メール送信実行(仕様書準拠)
|
||||||
|
$mailResult = $mailService->executeMailSend(
|
||||||
|
$mainEmail,
|
||||||
|
$subEmail,
|
||||||
|
$mailTemplateId
|
||||||
|
);
|
||||||
|
|
||||||
|
Log::info('空き待ち通知メール送信試行完了', [
|
||||||
|
'user_id' => $waitingUser->user_id,
|
||||||
|
'main_email' => $mainEmail,
|
||||||
|
'sub_email' => $subEmail,
|
||||||
|
'mail_template_id' => $mailTemplateId,
|
||||||
|
'result_success' => $mailResult['success'] ?? false
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $mailResult;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('空き待ち通知メール送信エラー', [
|
||||||
|
'user_id' => $waitingUser->user_id,
|
||||||
|
'main_email' => $waitingUser->user_primemail ?? '',
|
||||||
|
'sub_email' => $waitingUser->user_submail ?? '',
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reserve.sent_date及びvalid_flag更新
|
||||||
|
*
|
||||||
|
* 仕様書準拠:メール送信成功時にreserve.sent_dateとvalid_flag=0を同時更新
|
||||||
|
* 重複通知を防ぎ、処理済みマークを設定
|
||||||
|
*
|
||||||
|
* @param int $reserveId 予約ID
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateReserveSentDate(int $reserveId): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DB::table('reserve')
|
||||||
|
->where('reserve_id', $reserveId)
|
||||||
|
->update([
|
||||||
|
'sent_date' => now()->format('Y-m-d H:i:s'),
|
||||||
|
'valid_flag' => 0, // 仕様書:メール送信成功時に0に更新
|
||||||
|
'updated_at' => now()
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::info('reserve.sent_date及びvalid_flag更新完了', [
|
||||||
|
'reserve_id' => $reserveId,
|
||||||
|
'sent_date' => now()->format('Y-m-d H:i:s'),
|
||||||
|
'valid_flag' => 0
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('reserve.sent_date及びvalid_flag更新エラー', [
|
||||||
|
'reserve_id' => $reserveId,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オペレーターキューに追加
|
||||||
|
*
|
||||||
|
* 仕様書に基づくキュー登録:
|
||||||
|
* - que_comment: 空文字列
|
||||||
|
* - que_status_comment: 仕様書完全準拠形式(統計情報含む)
|
||||||
|
* - operator_id: 9999999固定
|
||||||
|
*
|
||||||
|
* @param object $waitingUser 空き待ち者情報
|
||||||
|
* @param object $parkVacancy 駐輪場空き情報
|
||||||
|
* @param string $batchComment 内部変数.バッチコメント
|
||||||
|
* @param int $mailSuccessCount メール正常終了件数
|
||||||
|
* @param int $queueSuccessCount キュー登録正常終了件数
|
||||||
|
* @param int $mailErrorCount メール異常終了件数
|
||||||
|
* @param int $queueErrorCount キュー登録異常終了件数
|
||||||
|
* @return array 追加結果
|
||||||
|
*/
|
||||||
|
private function addToOperatorQueue(object $waitingUser, object $parkVacancy, string $batchComment, int $mailSuccessCount, int $queueSuccessCount, int $mailErrorCount, int $queueErrorCount): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 仕様書完全準拠:駐輪場名/駐輪分類名/車種区分名/空き台数…/対象予約ID…/内部変数.バッチコメント/内部変数.メール正常終了件数…メール異常終了件数…キュー登録正常終了件数…キュー登録異常終了件数…
|
||||||
|
$statusComment = sprintf(
|
||||||
|
'%s/%s/%s/空き台数:%d台/対象予約ID:%d/%s/メール正常終了件数:%d/メール異常終了件数:%d/キュー登録正常終了件数:%d/キュー登録異常終了件数:%d',
|
||||||
|
$waitingUser->park_name ?? '',
|
||||||
|
$waitingUser->ptype_subject ?? '', // 駐輪分類名
|
||||||
|
$waitingUser->psection_subject ?? '', // 車種区分名
|
||||||
|
$parkVacancy->vacant_count ?? 0,
|
||||||
|
$waitingUser->reserve_id ?? 0,
|
||||||
|
$batchComment, // 内部変数.バッチコメント
|
||||||
|
$mailSuccessCount, // 内部変数.メール正常終了件数
|
||||||
|
$mailErrorCount,
|
||||||
|
$queueSuccessCount,
|
||||||
|
$queueErrorCount
|
||||||
|
);
|
||||||
|
|
||||||
|
OperatorQue::create([
|
||||||
|
'que_class' => 4, // 予約告知通知
|
||||||
|
'user_id' => $waitingUser->user_id,
|
||||||
|
'contract_id' => null,
|
||||||
|
'park_id' => $waitingUser->park_id,
|
||||||
|
'que_comment' => '', // 仕様書:空文字列
|
||||||
|
'que_status' => 1, // キュー発生
|
||||||
|
'que_status_comment' => $statusComment, // 仕様書:完全準拠形式
|
||||||
|
'work_instructions' => '空き待ち者への連絡をお願いします。',
|
||||||
|
'operator_id' => 9999999, // 仕様書:固定値9999999
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::info('オペレーターキュー追加成功', [
|
||||||
|
'user_id' => $waitingUser->user_id,
|
||||||
|
'park_id' => $waitingUser->park_id,
|
||||||
|
'reserve_id' => $waitingUser->reserve_id,
|
||||||
|
'que_class' => 4,
|
||||||
|
'operator_id' => 9999999,
|
||||||
|
'batch_comment' => $batchComment,
|
||||||
|
'mail_success_count' => $mailSuccessCount,
|
||||||
|
'mail_error_count' => $mailErrorCount,
|
||||||
|
'queue_success_count' => $queueSuccessCount,
|
||||||
|
'queue_error_count' => $queueErrorCount,
|
||||||
|
'status_comment' => $statusComment
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ['success' => true];
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('オペレーターキュー追加エラー', [
|
||||||
|
'user_id' => $waitingUser->user_id,
|
||||||
|
'park_id' => $waitingUser->park_id,
|
||||||
|
'reserve_id' => $waitingUser->reserve_id ?? null,
|
||||||
|
'batch_comment' => $batchComment,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ use App\Models\RegularContract;
|
|||||||
use App\Models\Park;
|
use App\Models\Park;
|
||||||
use App\Models\PriceA;
|
use App\Models\PriceA;
|
||||||
use App\Models\Batch\BatchLog;
|
use App\Models\Batch\BatchLog;
|
||||||
|
use App\Services\ShjThirteenService;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
@ -170,6 +171,7 @@ class ShjFourBService
|
|||||||
'T1.billing_amount',
|
'T1.billing_amount',
|
||||||
'T4.price_ptypeid as ptype_id',
|
'T4.price_ptypeid as ptype_id',
|
||||||
'T1.psection_id',
|
'T1.psection_id',
|
||||||
|
'T1.zone_id',
|
||||||
'T1.update_flag',
|
'T1.update_flag',
|
||||||
'T1.reserve_id',
|
'T1.reserve_id',
|
||||||
'T1.contract_payment_number',
|
'T1.contract_payment_number',
|
||||||
@ -765,27 +767,57 @@ class ShjFourBService
|
|||||||
/**
|
/**
|
||||||
* SHJ-13実行処理(新規のみ)
|
* SHJ-13実行処理(新規のみ)
|
||||||
*
|
*
|
||||||
|
* ShjThirteenServiceを使用した契約台数追加処理
|
||||||
|
*
|
||||||
* @param object $contract
|
* @param object $contract
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function triggerShjThirteen($contract): array
|
private function triggerShjThirteen($contract): array
|
||||||
{
|
{
|
||||||
// TODO: SHJ-13の具体的な処理を実装
|
|
||||||
// 現在はプレースホルダー
|
|
||||||
|
|
||||||
Log::info('SHJ-4B SHJ-13実行処理', [
|
Log::info('SHJ-4B SHJ-13実行処理', [
|
||||||
'contract_id' => $contract->contract_id,
|
'contract_id' => $contract->contract_id,
|
||||||
'user_id' => $contract->user_id,
|
|
||||||
'park_id' => $contract->park_id,
|
'park_id' => $contract->park_id,
|
||||||
|
'psection_id' => $contract->psection_id,
|
||||||
|
'ptype_id' => $contract->ptype_id,
|
||||||
|
'zone_id' => $contract->zone_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 契約データ準備
|
||||||
|
$contractData = [
|
||||||
|
'contract_id' => $contract->contract_id,
|
||||||
|
'park_id' => $contract->park_id,
|
||||||
|
'psection_id' => $contract->psection_id,
|
||||||
|
'ptype_id' => $contract->ptype_id,
|
||||||
|
'zone_id' => $contract->zone_id,
|
||||||
|
];
|
||||||
|
|
||||||
|
// ShjThirteenService実行
|
||||||
|
$shjThirteenService = app(ShjThirteenService::class);
|
||||||
|
$result = $shjThirteenService->execute($contractData);
|
||||||
|
|
||||||
|
Log::info('SHJ-4B SHJ-13実行完了', [
|
||||||
|
'contract_id' => $contract->contract_id,
|
||||||
|
'result' => $result,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('SHJ-4B SHJ-13実行エラー', [
|
||||||
|
'contract_id' => $contract->contract_id,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'triggered' => true,
|
'result' => 1,
|
||||||
'method' => 'placeholder',
|
'error_code' => $e->getCode() ?: 1999,
|
||||||
'message' => 'SHJ-13処理は実装予定です',
|
'error_message' => $e->getMessage(),
|
||||||
'contract_id' => $contract->contract_id,
|
'stack_trace' => $e->getTraceAsString(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 利用者メール送信処理
|
* 利用者メール送信処理
|
||||||
|
|||||||
346
app/Services/ShjThirteenService.php
Normal file
346
app/Services/ShjThirteenService.php
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use App\Models\Batch\BatchLog;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHJ-13 契約台数追加処理サービス
|
||||||
|
*
|
||||||
|
* 新規契約時の契約台数を park_number・zone テーブルに反映する処理
|
||||||
|
* SHJ-4B の副作用処理として実行される
|
||||||
|
*/
|
||||||
|
class ShjThirteenService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* SHJ-13 契約台数追加処理実行
|
||||||
|
*
|
||||||
|
* 【処理1】契約台数を反映する - park_number・zone テーブルの契約台数を+1更新
|
||||||
|
* 【処理2】バッチ処理ログを作成する - SHJ-8共通仕様でログ登録
|
||||||
|
*
|
||||||
|
* @param array $contractData 契約データ
|
||||||
|
* @return array 処理結果
|
||||||
|
*/
|
||||||
|
public function execute(array $contractData): array
|
||||||
|
{
|
||||||
|
$startTime = now();
|
||||||
|
|
||||||
|
Log::info('SHJ-13 契約台数追加処理開始', [
|
||||||
|
'contract_id' => $contractData['contract_id'] ?? null,
|
||||||
|
'park_id' => $contractData['park_id'] ?? null,
|
||||||
|
'psection_id' => $contractData['psection_id'] ?? null,
|
||||||
|
'ptype_id' => $contractData['ptype_id'] ?? null,
|
||||||
|
'zone_id' => $contractData['zone_id'] ?? null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// パラメータ検証
|
||||||
|
$validationResult = $this->validateParameters($contractData);
|
||||||
|
if (!$validationResult['valid']) {
|
||||||
|
return $this->createErrorResult(
|
||||||
|
1001,
|
||||||
|
'パラメータエラー: ' . $validationResult['message']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【処理1・2】契約台数反映とバッチログ作成を一体で実行
|
||||||
|
$processResult = $this->executeProcessWithLogging($contractData);
|
||||||
|
if (!$processResult['success']) {
|
||||||
|
return $this->createErrorResult(
|
||||||
|
$processResult['error_code'],
|
||||||
|
$processResult['error_message'],
|
||||||
|
$processResult['stack_trace'] ?? ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusComment = $processResult['status_comment'];
|
||||||
|
|
||||||
|
$endTime = now();
|
||||||
|
Log::info('SHJ-13 契約台数追加処理完了', [
|
||||||
|
'contract_id' => $contractData['contract_id'],
|
||||||
|
'execution_time' => $startTime->diffInSeconds($endTime),
|
||||||
|
'updated_count' => $processResult['updated_count'],
|
||||||
|
'status_comment' => $statusComment,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ['result' => 0];
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('SHJ-13 契約台数追加処理例外エラー', [
|
||||||
|
'contract_data' => $contractData,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->createErrorResult(
|
||||||
|
$e->getCode() ?: 1999,
|
||||||
|
$e->getMessage(),
|
||||||
|
$e->getTraceAsString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* パラメータ検証
|
||||||
|
*
|
||||||
|
* @param array $contractData
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function validateParameters(array $contractData): array
|
||||||
|
{
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
if (empty($contractData['park_id'])) {
|
||||||
|
$errors[] = '駐輪場IDが設定されていません';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($contractData['psection_id'])) {
|
||||||
|
$errors[] = '車種区分IDが設定されていません';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($contractData['ptype_id'])) {
|
||||||
|
$errors[] = '駐輪分類IDが設定されていません';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($contractData['zone_id'])) {
|
||||||
|
$errors[] = 'ゾーンIDが設定されていません';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
return [
|
||||||
|
'valid' => false,
|
||||||
|
'message' => implode(', ', $errors),
|
||||||
|
'details' => $errors,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['valid' => true];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 契約台数反映とバッチログ作成の一体処理
|
||||||
|
*
|
||||||
|
* park_number・zone テーブルの契約台数を+1更新し、バッチログを作成
|
||||||
|
* 全てを1つのトランザクション内で実行
|
||||||
|
*
|
||||||
|
* @param array $contractData
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function executeProcessWithLogging(array $contractData): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return DB::transaction(function() use ($contractData) {
|
||||||
|
// 各テーブルの名称取得
|
||||||
|
$names = $this->getTableNames($contractData);
|
||||||
|
if (!$names['success']) {
|
||||||
|
throw new \Exception('名称取得エラー: ' . $names['message'], 1002);
|
||||||
|
}
|
||||||
|
|
||||||
|
// park_number テーブル更新
|
||||||
|
$parkNumberUpdated = DB::table('park_number')
|
||||||
|
->where('park_id', $contractData['park_id'])
|
||||||
|
->where('psection_id', $contractData['psection_id'])
|
||||||
|
->where('ptype_id', $contractData['ptype_id'])
|
||||||
|
->increment('park_number', 1, [
|
||||||
|
'updated_at' => now(),
|
||||||
|
'operator_id' => 'SHJ-13',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($parkNumberUpdated === 0) {
|
||||||
|
throw new \Exception('park_numberテーブルの対象レコードが存在しません', 1011);
|
||||||
|
}
|
||||||
|
|
||||||
|
// zone テーブル更新
|
||||||
|
$zoneUpdated = DB::table('zone')
|
||||||
|
->where('zone_id', $contractData['zone_id'])
|
||||||
|
->increment('zone_number', 1, [
|
||||||
|
'updated_at' => now(),
|
||||||
|
'ope_id' => 'SHJ-13',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($zoneUpdated === 0) {
|
||||||
|
throw new \Exception('zoneテーブルの対象レコードが存在しません', 1012);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新後の契約台数取得
|
||||||
|
$updatedZone = DB::table('zone')
|
||||||
|
->where('zone_id', $contractData['zone_id'])
|
||||||
|
->first(['zone_number']);
|
||||||
|
|
||||||
|
// ステータスコメント構築
|
||||||
|
$statusComment = $this->buildStatusComment($names['names'], $updatedZone->zone_number);
|
||||||
|
|
||||||
|
// バッチ処理ログ作成(同一トランザクション内)
|
||||||
|
$currentDate = now()->format('Y/m/d');
|
||||||
|
|
||||||
|
$batchLog = BatchLog::createBatchLog(
|
||||||
|
'SHJ-13', // process_name
|
||||||
|
BatchLog::STATUS_SUCCESS,
|
||||||
|
[
|
||||||
|
'device_id' => 1,
|
||||||
|
'job_name' => 'SHJ-13',
|
||||||
|
'status' => 'success',
|
||||||
|
'status_comment' => $statusComment,
|
||||||
|
'created_date' => $currentDate,
|
||||||
|
'updated_date' => $currentDate,
|
||||||
|
'contract_id' => $contractData['contract_id'] ?? null,
|
||||||
|
'park_id' => $contractData['park_id'],
|
||||||
|
'psection_id' => $contractData['psection_id'],
|
||||||
|
'ptype_id' => $contractData['ptype_id'],
|
||||||
|
'zone_id' => $contractData['zone_id'],
|
||||||
|
],
|
||||||
|
$statusComment
|
||||||
|
);
|
||||||
|
|
||||||
|
Log::info('SHJ-13 契約台数更新・ログ作成完了', [
|
||||||
|
'contract_id' => $contractData['contract_id'],
|
||||||
|
'park_number_updated' => $parkNumberUpdated,
|
||||||
|
'zone_updated' => $zoneUpdated,
|
||||||
|
'updated_count' => $updatedZone->zone_number,
|
||||||
|
'batch_log_id' => $batchLog->id,
|
||||||
|
'status_comment' => $statusComment,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'updated_count' => $updatedZone->zone_number,
|
||||||
|
'status_comment' => $statusComment,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('SHJ-13 契約台数更新・ログ作成エラー', [
|
||||||
|
'contract_data' => $contractData,
|
||||||
|
'error_code' => $e->getCode(),
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error_code' => $e->getCode() ?: 1010,
|
||||||
|
'error_message' => $e->getMessage(),
|
||||||
|
'stack_trace' => $e->getTraceAsString(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テーブル名称取得
|
||||||
|
*
|
||||||
|
* @param array $contractData
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getTableNames(array $contractData): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 駐輪場名取得
|
||||||
|
$park = DB::table('park')
|
||||||
|
->where('park_id', $contractData['park_id'])
|
||||||
|
->first(['park_name']);
|
||||||
|
|
||||||
|
if (!$park) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => "駐輪場が見つかりません: park_id={$contractData['park_id']}",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 駐輪分類名取得
|
||||||
|
$ptype = DB::table('ptype')
|
||||||
|
->where('ptype_id', $contractData['ptype_id'])
|
||||||
|
->first(['ptype_subject']);
|
||||||
|
|
||||||
|
if (!$ptype) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => "駐輪分類が見つかりません: ptype_id={$contractData['ptype_id']}",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 車種区分名取得
|
||||||
|
$psection = DB::table('psection')
|
||||||
|
->where('psection_id', $contractData['psection_id'])
|
||||||
|
->first(['psection_subject']);
|
||||||
|
|
||||||
|
if (!$psection) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => "車種区分が見つかりません: psection_id={$contractData['psection_id']}",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ゾーン名取得
|
||||||
|
$zone = DB::table('zone')
|
||||||
|
->where('zone_id', $contractData['zone_id'])
|
||||||
|
->first(['zone_name']);
|
||||||
|
|
||||||
|
if (!$zone) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => "ゾーンが見つかりません: zone_id={$contractData['zone_id']}",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'names' => [
|
||||||
|
'park_name' => $park->park_name,
|
||||||
|
'ptype_subject' => $ptype->ptype_subject,
|
||||||
|
'psection_subject' => $psection->psection_subject,
|
||||||
|
'zone_name' => $zone->zone_name,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'テーブル名称取得エラー: ' . $e->getMessage(),
|
||||||
|
'details' => $e->getTraceAsString(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ステータスコメント構築
|
||||||
|
*
|
||||||
|
* 形式:駐輪場名/駐輪分類名/車種区分名/ゾーン名/現在契約台数(更新後):{数値}/
|
||||||
|
*
|
||||||
|
* @param array $names
|
||||||
|
* @param int $updatedCount
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function buildStatusComment(array $names, int $updatedCount): string
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'%s/%s/%s/%s/現在契約台数(更新後):%d/',
|
||||||
|
$names['park_name'],
|
||||||
|
$names['ptype_subject'],
|
||||||
|
$names['psection_subject'],
|
||||||
|
$names['zone_name'],
|
||||||
|
$updatedCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* エラー結果作成
|
||||||
|
*
|
||||||
|
* @param int $errorCode
|
||||||
|
* @param string $errorMessage
|
||||||
|
* @param string $stackTrace
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function createErrorResult(int $errorCode, string $errorMessage, string $stackTrace = ''): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'result' => 1,
|
||||||
|
'error_code' => $errorCode,
|
||||||
|
'error_message' => $errorMessage,
|
||||||
|
'stack_trace' => $stackTrace,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<h3 class="other mt50">開示等の依頼の手続き、使用する様式</h3>
|
<h3 class="other mt50">開示等の依頼の手続き、使用する様式</h3>
|
||||||
<p class="p1">開示等の依頼は、以下の手続き及び様式に則って実施致します。</p>
|
<p class="p1">開示等の依頼は、以下の手続き及び様式に則って実施致します。</p>
|
||||||
<p class="p1">利用目的の通知:本書面の“開示対象個人情報の利用目的”をご覧下さい。</p>
|
<p class="p1">利用目的の通知:本書面の“開示対象個人情報の利用目的”をご覧下さい。</p>
|
||||||
<p class="p1">開示、訂正・削除、利用停止:当社の定める様式にて実施致します。該当の受付け窓口にご連絡頂き、所定の様式『<a href="{{ asset('assets/page-img/privacy_disclosure.pdf') }}" target="_blank">個人情報開示等依頼書(PDF:9KB)</a>』を入手のうえ、手続きをお願い致します。</p>
|
<p class="p1">開示、訂正・削除、利用停止:当社の定める様式にて実施致します。該当の受付け窓口にご連絡頂き、所定の様式『<a href="{{ asset('assets/privacy_disclosure.pdf') }}" target="_blank">個人情報開示等依頼書(PDF:9KB)</a>』を入手のうえ、手続きをお願い致します。</p>
|
||||||
<p class="p1">回答に関しては、記入済み個人情報開示等依頼書を、ご自宅への郵送のみとさせて頂きます。</p>
|
<p class="p1">回答に関しては、記入済み個人情報開示等依頼書を、ご自宅への郵送のみとさせて頂きます。</p>
|
||||||
<h3 class="other mt50">開示対象個人情報の取扱いに関する苦情受付け窓口</h3>
|
<h3 class="other mt50">開示対象個人情報の取扱いに関する苦情受付け窓口</h3>
|
||||||
<p class="p1">開示対象個人情報の取扱いに関する苦情、相談に関しましては、以下の窓口宛てにご連絡ください。</p>
|
<p class="p1">開示対象個人情報の取扱いに関する苦情、相談に関しましては、以下の窓口宛てにご連絡ください。</p>
|
||||||
|
|||||||
@ -65,10 +65,12 @@
|
|||||||
<table id="searchTable" class="tablesorter table table-striped">
|
<table id="searchTable" class="tablesorter table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>駐輪場名</th>
|
<th width="20%">駐輪場名</th>
|
||||||
<th>市町村名</th>
|
<th width="15%">市町村名</th>
|
||||||
<th>駅名</th>
|
<th width="5%">駅名</th>
|
||||||
<th></th>
|
@foreach($psections as $psection)
|
||||||
|
<th width="15%">{{ $psection->psection_subject }}</th>
|
||||||
|
@endforeach
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -82,18 +84,39 @@
|
|||||||
@endphp
|
@endphp
|
||||||
@foreach($pagedData as $data)
|
@foreach($pagedData as $data)
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="#placeModal" data-toggle="modal" data-target="#placeModal">{{ $data['park_name'] }}</a></td>
|
<td>
|
||||||
|
<a href="#placeModal"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#placeModal"
|
||||||
|
data-park_name="{{ $data['park_name'] }}"
|
||||||
|
data-park_adrs="{{ $data['park_adrs'] ?? '' }}"
|
||||||
|
data-price_memo="{{ $data['price_memo'] ?? '' }}"
|
||||||
|
data-park_latitude="{{ $data['park_latitude'] ?? '' }}"
|
||||||
|
data-park_longitude="{{ $data['park_longitude'] ?? '' }}"
|
||||||
|
data-city_name="{{ $data['city_name'] }}"
|
||||||
|
data-station="{{ $data['station_neighbor_station'] }}"
|
||||||
|
data-zone_data='@json($data["zone_data"])'>
|
||||||
|
{{ $data['park_name'] }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
<td>{{ $data['city_name'] }}</td>
|
<td>{{ $data['city_name'] }}</td>
|
||||||
<td>{{ $data['station_neighbor_station'] }}</td>
|
<td>{{ $data['station_neighbor_station'] }}</td>
|
||||||
|
@foreach($psections as $psection)
|
||||||
<td>
|
<td>
|
||||||
@if($data['status'] == 1)
|
@foreach($data['zone_data'] as $zone)
|
||||||
|
@if($zone['psection_subject'] == $psection->psection_subject)
|
||||||
|
@if($zone['status'] == 1)
|
||||||
<a href="{{route('user.info')}}" class="btn btn-block btn-sm btn-outline-success">定期契約</a>
|
<a href="{{route('user.info')}}" class="btn btn-block btn-sm btn-outline-success">定期契約</a>
|
||||||
@elseif($data['status'] == 2)
|
@elseif($zone['status'] == 2)
|
||||||
<a href="{{route('park_waitlist.index')}}" class="btn btn-block btn-sm btn-outline-primary">空き待ち予約</a>
|
<a href="{{route('park_waitlist.index')}}" class="btn btn-block btn-sm btn-outline-primary">空き待ち予約</a>
|
||||||
@elseif($data['status'] == 3)
|
@elseif($zone['status'] == 3)
|
||||||
<a href="{{route('park_waitlist.index')}}" class="btn btn-block btn-sm btn-secondary">販売期間外</a>
|
<a href="{{route('park_waitlist.index')}}" class="btn btn-block btn-sm btn-secondary">販売期間外</a>
|
||||||
@endif
|
@endif
|
||||||
|
@break;
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
</td>
|
</td>
|
||||||
|
@endforeach
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -133,29 +156,86 @@
|
|||||||
<div class="modal fade" id="placeModal" tabindex="-1" role="dialog" aria-hidden="true">
|
<div class="modal fade" id="placeModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="modalParkName">駐輪場名</h5>
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="閉じる"><span aria-hidden="true">×</span></button>
|
|
||||||
</div>
|
|
||||||
<script>
|
<script>
|
||||||
$(function() {
|
|
||||||
$('#placeModal').on('show.bs.modal', function (event) {
|
$('#placeModal').on('show.bs.modal', function (event) {
|
||||||
var button = $(event.relatedTarget);
|
var button = $(event.relatedTarget);
|
||||||
var parkName = button.text();
|
var parkName = button.data('park_name') || '';
|
||||||
$('#modalParkName').text(parkName);
|
var parkAdrs = button.data('park_adrs') || '';
|
||||||
|
var parkMemo = button.data('price_memo') || '';
|
||||||
|
var lat = button.data('park_latitude') || '';
|
||||||
|
var lng = button.data('park_longitude') || '';
|
||||||
|
$('#parkName').text(parkName).attr('data-park_name', parkName);
|
||||||
|
$('#parkAdrs').text('住所:' + parkAdrs).attr('data-park_adrs', parkAdrs);
|
||||||
|
$('#parkmemo').text(parkMemo).attr('data-price_memo', parkMemo);
|
||||||
|
$('#parkMap').attr('src', 'https://www.google.com/maps?q=' + lat + ',' + lng + '&z=15&output=embed');
|
||||||
|
|
||||||
|
// zone_dataはJSON文字列として渡されているのでパース
|
||||||
|
var zoneData = button.data('zone_data') || [];
|
||||||
|
|
||||||
|
// psection_subjectごとの標準収容台数を集計
|
||||||
|
var standardMap = {};
|
||||||
|
if (zoneData && Array.isArray(zoneData)) {
|
||||||
|
zoneData.forEach(function(zone) {
|
||||||
|
if (!standardMap[zone.psection_subject]) {
|
||||||
|
standardMap[zone.psection_subject] = 0;
|
||||||
|
}
|
||||||
|
standardMap[zone.psection_subject] += parseInt(zone.zone_standard, 10) || 0;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
// 表示用文字列を生成
|
||||||
|
var standardText = '';
|
||||||
|
var keys = Object.keys(standardMap);
|
||||||
|
if (keys.length > 0) {
|
||||||
|
standardText = '【標準収容台数】';
|
||||||
|
standardText += keys.map(function(key) {
|
||||||
|
return key + ':' + standardMap[key] + '台';
|
||||||
|
}).join(' / ');
|
||||||
|
}
|
||||||
|
$('#parkStandard').text(standardText);
|
||||||
|
|
||||||
|
// 各ゾーンの空き台数・ボタンを表示
|
||||||
|
var html = '';
|
||||||
|
if (zoneData && Array.isArray(zoneData)) {
|
||||||
|
var grouped = {};
|
||||||
|
zoneData.forEach(function(zone) {
|
||||||
|
if (!grouped[zone.ptype_subject]) grouped[zone.ptype_subject] = [];
|
||||||
|
grouped[zone.ptype_subject].push(zone);
|
||||||
|
});
|
||||||
|
Object.keys(grouped).forEach(function(ptype) {
|
||||||
|
html += '<h4 class="mt-3">' + ptype + '</h4>';
|
||||||
|
grouped[ptype].forEach(function(zone) {
|
||||||
|
html += '<div class="d-flex align-items-center mb-2">';
|
||||||
|
html += '<span>' + zone.psection_subject + ':空き' + zone.zone_vacant + '台</span>';
|
||||||
|
if (zone.status == 1) {
|
||||||
|
html += '<a href="{{route('user.info')}}" class="btn btn-sm btn-outline-success ml-2">定期契約</a>';
|
||||||
|
} else if (zone.status == 2) {
|
||||||
|
html += '<a href="{{route('park_waitlist.index')}}" class="btn btn-sm btn-outline-primary ml-2">空き待ち予約</a>';
|
||||||
|
} else if (zone.status == 3) {
|
||||||
|
html += '<a href="{{route('park_waitlist.index')}}" class="btn btn-sm btn-outline-secondary ml-2">販売期間外</a>';
|
||||||
|
}
|
||||||
|
html += '</div>';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$('#zoneData').html(html);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="parkName" data-park_name=""></h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="閉じる"><span aria-hidden="true">×</span></button>
|
||||||
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3240.722943699139!2d139.75162621525894!3d35.68382338019366!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x60188c0b185b3b75%3A0x3282e79fbc91959c!2z44CSMTAwLTAwMDEg5p2x5Lqs6YO95Y2D5Luj55Sw5Yy65Y2D5Luj55Sw77yR4oiS77yR!5e0!3m2!1sja!2sjp!4v1536695351221" width="100%" height="450" frameborder="0" style="border:0" allowfullscreen></iframe>
|
<iframe id="parkMap" src="" width="100%" height="450" frameborder="0" style="border:0" allowfullscreen></iframe>
|
||||||
<p class="small">〒000-0000 東京都千代田区1-1 <br class="sp">標準収容台数:XXX台</p>
|
<p class="small">
|
||||||
<p class="text-danger">空き台数:XXX台</p>
|
<span id="parkAdrs"> </span>
|
||||||
|
<span id="parkStandard"></span><br />
|
||||||
|
<span id="parkmemo"></span>
|
||||||
|
</p>
|
||||||
|
<span id="zoneData"></span>
|
||||||
|
<div class="text-right"><button type="button" class="btn btn-outline-secondary" data-dismiss="modal">閉じる</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer"></div>
|
||||||
<button type="submit" class="btn btn-success" onClick="location.href='./SWC-08-02.html'">定期契約</button>
|
|
||||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">閉じる</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
Loading…
Reference in New Issue
Block a user