From 50bb8543a54104e0a10cc68d45dd30a67c994028 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 29 Aug 2025 12:04:27 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20SHJ-11=E3=81=A8SHJ-12=E3=83=90=E3=83=83?= =?UTF-8?q?=E3=83=81=E5=87=A6=E7=90=86=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20-=20SHJ-11:=20=E7=8F=BE=E5=9C=A8?= =?UTF-8?q?=E5=A5=91=E7=B4=84=E5=8F=B0=E6=95=B0=E9=9B=86=E8=A8=88=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=82=92=E5=AE=9F=E8=A3=85=20-=20SHJ-12:=20=E6=9C=AA?= =?UTF-8?q?=E6=89=95=E3=81=84=E8=80=85=E9=80=9A=E7=9F=A5=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 9 +- .env.main | 64 ++++ app/Console/Commands/ShjElevenCommand.php | 182 +++++++++ app/Console/Commands/ShjTwelveCommand.php | 177 +++++++++ app/Services/ShjElevenService.php | 432 ++++++++++++++++++++++ app/Services/ShjMailSendService.php | 102 +++++ app/Services/ShjTwelveService.php | 335 +++++++++++++++++ 7 files changed, 1297 insertions(+), 4 deletions(-) create mode 100644 .env.main create mode 100644 app/Console/Commands/ShjElevenCommand.php create mode 100644 app/Console/Commands/ShjTwelveCommand.php create mode 100644 app/Services/ShjElevenService.php create mode 100644 app/Services/ShjTwelveService.php diff --git a/.env b/.env index 1b72b13..a9fc570 100644 --- a/.env +++ b/.env @@ -2,7 +2,8 @@ APP_NAME=so-manager APP_ENV=local APP_KEY=base64:ejLwJbt2bEXY9emPUmsurG+X1hzkjTxQQvq2/FO14RY= APP_DEBUG=true -APP_URL=https://krgm.so-manager-dev.com/ +APP_URL=http://somanager-local.localhost:81/ + APP_LOCALE=ja APP_FALLBACK_LOCALE=ja APP_FAKER_LOCALE=ja_JP @@ -22,9 +23,9 @@ LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 -DB_DATABASE=krgm -DB_USERNAME=krgm_user -DB_PASSWORD=StrongDbP@ss2 +DB_DATABASE=somanager_admin +DB_USERNAME=root +DB_PASSWORD= SESSION_DRIVER=database SESSION_LIFETIME=120 diff --git a/.env.main b/.env.main new file mode 100644 index 0000000..1b72b13 --- /dev/null +++ b/.env.main @@ -0,0 +1,64 @@ +APP_NAME=so-manager +APP_ENV=local +APP_KEY=base64:ejLwJbt2bEXY9emPUmsurG+X1hzkjTxQQvq2/FO14RY= +APP_DEBUG=true +APP_URL=https://krgm.so-manager-dev.com/ +APP_LOCALE=ja +APP_FALLBACK_LOCALE=ja +APP_FAKER_LOCALE=ja_JP + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=localhost +DB_PORT=3306 +DB_DATABASE=krgm +DB_USERNAME=krgm_user +DB_PASSWORD=StrongDbP@ss2 + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="hello@so-manager-dev.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" diff --git a/app/Console/Commands/ShjElevenCommand.php b/app/Console/Commands/ShjElevenCommand.php new file mode 100644 index 0000000..2e739f2 --- /dev/null +++ b/app/Console/Commands/ShjElevenCommand.php @@ -0,0 +1,182 @@ +shjElevenService = $shjElevenService; + } + + /** + * コンソールコマンドを実行 + * + * 処理フロー: + * 1. 集計単位每个の契約台数を算出する + * 2. 取得件数判定 + * 3. ゾーンマスタを取得する + * 4. 取得判定とゾーンマスタ登録 + * 5. 契約台数チェック(限界台数超過判定) + * 6. 契約台数を反映する + * 7. バッチ処理ログを作成する + * + * @return int + */ + public function handle() + { + try { + // 開始ログ出力 + $startTime = now(); + $this->info('SHJ-11 現在契約台数集計を開始します。'); + Log::info('SHJ-11 現在契約台数集計開始', [ + 'start_time' => $startTime + ]); + + // 【処理1】集計単位每个の契約台数を算出する + $this->info('【処理1】集計単位每个の契約台数を算出しています...'); + $contractCounts = $this->shjElevenService->calculateContractCounts(); + + // 【判断1】取得件数判定 + $countResults = count($contractCounts); + $this->info("取得件数: {$countResults}件"); + + if ($countResults === 0) { + // 対象なしの結果を設定する + $this->info('契約台数算出対象なしのため処理を終了します。'); + + // バッチ処理ログを作成 + $this->shjElevenService->createBatchLog( + 'success', + [], + '契約台数算出対象なし', + 0, + 0, + 0 + ); + + Log::info('SHJ-11 現在契約台数集計完了(対象なし)', [ + 'end_time' => now(), + 'duration_seconds' => $startTime->diffInSeconds(now()) + ]); + + return self::SUCCESS; + } + + // 【処理2・3】ゾーンマスタ処理(取得・登録・更新) + $this->info('【処理2】ゾーンマスタ処理を実行しています...'); + $processResult = $this->shjElevenService->processZoneManagement($contractCounts); + + // 処理結果確認 + if ($processResult['success']) { + $endTime = now(); + $this->info('SHJ-11 現在契約台数集計が正常に完了しました。'); + $this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒"); + $this->info("処理対象件数: {$countResults}件"); + $this->info("ゾーン新規作成件数: {$processResult['created_zones']}件"); + $this->info("ゾーン更新件数: {$processResult['updated_zones']}件"); + $this->info("限界台数超過件数: {$processResult['over_capacity_count']}件"); + + // 【処理4】バッチ処理ログを作成する + $this->shjElevenService->createBatchLog( + 'success', + $processResult['parameters'], + '現在契約台数集計処理完了', + $countResults, + $processResult['created_zones'] + $processResult['updated_zones'], + 0 + ); + + Log::info('SHJ-11 現在契約台数集計完了', [ + 'end_time' => $endTime, + 'duration_seconds' => $startTime->diffInSeconds($endTime), + 'processed_count' => $countResults, + 'created_zones' => $processResult['created_zones'], + 'updated_zones' => $processResult['updated_zones'], + 'over_capacity_count' => $processResult['over_capacity_count'] + ]); + + return self::SUCCESS; + } else { + $this->error('SHJ-11 現在契約台数集計でエラーが発生しました: ' . $processResult['message']); + + // エラー時のバッチログ作成 + $this->shjElevenService->createBatchLog( + 'error', + $processResult['parameters'] ?? [], + $processResult['message'], + $countResults, + $processResult['created_zones'] ?? 0, + 1 + ); + + Log::error('SHJ-11 現在契約台数集計エラー', [ + 'error' => $processResult['message'], + 'details' => $processResult['details'] ?? null + ]); + + return self::FAILURE; + } + + } catch (\Exception $e) { + $this->error('SHJ-11 現在契約台数集計で予期しないエラーが発生しました: ' . $e->getMessage()); + + // 例外時のバッチログ作成 + $this->shjElevenService->createBatchLog( + 'error', + [], + 'システムエラー: ' . $e->getMessage(), + 0, + 0, + 1 + ); + + Log::error('SHJ-11 現在契約台数集計例外エラー', [ + 'exception' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return self::FAILURE; + } + } +} diff --git a/app/Console/Commands/ShjTwelveCommand.php b/app/Console/Commands/ShjTwelveCommand.php new file mode 100644 index 0000000..10b0705 --- /dev/null +++ b/app/Console/Commands/ShjTwelveCommand.php @@ -0,0 +1,177 @@ +shjTwelveService = $shjTwelveService; + } + + /** + * コンソールコマンドを実行 + * + * 処理フロー: + * 1. 定期契約マスタより未払い者を取得する + * 2. 取得件数判定 + * 3. 未払い者への通知、またはオペレーターキュー追加処理 + * 4. バッチ処理ログを作成する + * + * @return int + */ + public function handle() + { + try { + // 開始ログ出力 + $startTime = now(); + $this->info('SHJ-12 未払い者通知処理を開始します。'); + Log::info('SHJ-12 未払い者通知処理開始', [ + 'start_time' => $startTime + ]); + + // 【処理1】定期契約マスタより未払い者を取得する + $this->info('【処理1】定期契約マスタより未払い者を取得しています...'); + $unpaidUsers = $this->shjTwelveService->getUnpaidUsers(); + + // 【判断1】取得件数判定 + $unpaidCount = count($unpaidUsers); + $this->info("取得件数: {$unpaidCount}件"); + + if ($unpaidCount === 0) { + // 未払い者対象なしの結果を設定する + $this->info('未払い者対象なしのため処理を終了します。'); + + // バッチ処理ログを作成 + $this->shjTwelveService->createBatchLog( + 'success', + [], + '未払い者対象なし', + 0, + 0, + 0 + ); + + Log::info('SHJ-12 未払い者通知処理完了(対象なし)', [ + 'end_time' => now(), + 'duration_seconds' => $startTime->diffInSeconds(now()) + ]); + + return self::SUCCESS; + } + + // 【処理2】未払い者への通知、またはオペレーターキュー追加処理 + $this->info('【処理2】未払い者への通知処理を実行しています...'); + $processResult = $this->shjTwelveService->processUnpaidUserNotifications($unpaidUsers); + + // 処理結果確認 + if ($processResult['success']) { + $endTime = now(); + $this->info('SHJ-12 未払い者通知処理が正常に完了しました。'); + $this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒"); + $this->info("処理対象件数: {$unpaidCount}件"); + $this->info("通知送信件数: {$processResult['notification_count']}件"); + $this->info("オペレーターキュー追加件数: {$processResult['queue_count']}件"); + + // 【処理3】バッチ処理ログを作成する + $this->shjTwelveService->createBatchLog( + 'success', + $processResult['parameters'], + '未払い者通知処理完了', + $unpaidCount, + $processResult['notification_count'] + $processResult['queue_count'], + 0 + ); + + Log::info('SHJ-12 未払い者通知処理完了', [ + 'end_time' => $endTime, + 'duration_seconds' => $startTime->diffInSeconds($endTime), + 'processed_count' => $unpaidCount, + 'notification_count' => $processResult['notification_count'], + 'queue_count' => $processResult['queue_count'] + ]); + + return self::SUCCESS; + } else { + $this->error('SHJ-12 未払い者通知処理でエラーが発生しました: ' . $processResult['message']); + + // エラー時のバッチログ作成 + $this->shjTwelveService->createBatchLog( + 'error', + $processResult['parameters'] ?? [], + $processResult['message'], + $unpaidCount, + $processResult['notification_count'] ?? 0, + 1 + ); + + Log::error('SHJ-12 未払い者通知処理エラー', [ + 'error' => $processResult['message'], + 'details' => $processResult['details'] ?? null + ]); + + return self::FAILURE; + } + + } catch (\Exception $e) { + $this->error('SHJ-12 未払い者通知処理で予期しないエラーが発生しました: ' . $e->getMessage()); + + // 例外時のバッチログ作成 + $this->shjTwelveService->createBatchLog( + 'error', + [], + 'システムエラー: ' . $e->getMessage(), + 0, + 0, + 1 + ); + + Log::error('SHJ-12 未払い者通知処理例外エラー', [ + 'exception' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return self::FAILURE; + } + } +} diff --git a/app/Services/ShjElevenService.php b/app/Services/ShjElevenService.php new file mode 100644 index 0000000..fe0bb23 --- /dev/null +++ b/app/Services/ShjElevenService.php @@ -0,0 +1,432 @@ +select([ + 'T1.park_id', // 駐輪場ID + 'T2.park_name', // 駐輪場名 + 'T5.psection_id', // 車種区分ID + 'T4.psection_subject', // 車種区分名 + 'T5.ptype_id', // 駐輪分類ID + 'T3.ptype_subject', // 駐輪分類名 + 'T1.zone_id', // ゾーンID + DB::raw('count(T1.contract_id) as cnt') // 契約台数 + ]) + // park テーブルとの JOIN + ->join('park as T2', 'T1.park_id', '=', 'T2.park_id') + // psection テーブルとの JOIN + ->join('psection as T4', 'T1.psection_id', '=', 'T4.psection_id') + // price_a テーブルとの複合条件 JOIN + ->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'); + }) + // ptype テーブルとの JOIN + ->join('ptype as T3', 'T5.ptype_id', '=', 'T3.ptype_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', + 'T2.park_name', + 'T5.psection_id', + 'T4.psection_subject', + 'T5.ptype_id', + 'T3.ptype_subject', + 'T1.zone_id' + ]) + ->get(); + + Log::info('SHJ-11 契約台数算出完了', [ + 'count' => $query->count(), + 'sql_conditions' => [ + 'contract_flag' => 1, + 'park_close_flag' => 0, + 'contract_period_check' => 'BETWEEN contract_periods AND contract_periode' + ] + ]); + + return $query->toArray(); + + } catch (\Exception $e) { + Log::error('SHJ-11 契約台数算出エラー', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + throw $e; + } + } + + /** + * 【処理2・3】ゾーンマスタ管理処理 + * + * 処理フロー: + * 1. ゾーンマスタを取得する + * 2. 取得判定 → 存在しない場合は新規登録 + * 3. 契約台数チェック(限界台数超過判定) + * 4. 契約台数を反映する(ゾーンマスタ更新) + * + * @param array $contractCounts 契約台数集計結果 + * @return array 処理結果 + */ + public function processZoneManagement(array $contractCounts): array + { + $createdZones = 0; + $updatedZones = 0; + $overCapacityCount = 0; + $errors = []; + $processParameters = []; + + try { + DB::beginTransaction(); + + foreach ($contractCounts as $contractData) { + try { + // 【処理2】ゾーンマスタを取得する + $zoneData = $this->getZoneData($contractData); + + if (!$zoneData) { + // 【判断2】ゾーンマスタが存在しない場合 → 新規登録 + $createResult = $this->createZoneData($contractData); + if ($createResult['success']) { + $createdZones++; + } + $zoneData = $createResult['zone_data']; + } + + // 【判断3】契約台数チェック(限界台数超過判定) + $isOverCapacity = $this->checkCapacityLimit($contractData, $zoneData); + if ($isOverCapacity) { + $overCapacityCount++; + Log::warning('SHJ-11 限界台数超過検出', [ + 'park_id' => $contractData->park_id, + 'zone_id' => $contractData->zone_id, + 'current_count' => $contractData->cnt, + 'limit_capacity' => $zoneData->zone_tolerance ?? 0 + ]); + } + + // 【処理3】契約台数を反映する(ゾーンマスタ更新) + $updateResult = $this->updateZoneContractCount($contractData); + if ($updateResult['success']) { + $updatedZones++; + } + + // 処理パラメータ記録 + $processParameters[] = [ + 'park_id' => $contractData->park_id, + 'psection_id' => $contractData->psection_id, + 'ptype_id' => $contractData->ptype_id, + 'zone_id' => $contractData->zone_id, + 'contract_count' => $contractData->cnt, + 'is_over_capacity' => $isOverCapacity, + 'zone_created' => !$zoneData && $createResult['success'] ?? false, + 'zone_updated' => $updateResult['success'] ?? false + ]; + + } catch (\Exception $e) { + $errors[] = [ + 'park_id' => $contractData->park_id ?? null, + 'zone_id' => $contractData->zone_id ?? null, + 'error' => $e->getMessage() + ]; + + Log::warning('SHJ-11 個別処理エラー', [ + 'park_id' => $contractData->park_id ?? null, + 'zone_id' => $contractData->zone_id ?? null, + 'error' => $e->getMessage() + ]); + } + } + + DB::commit(); + + return [ + 'success' => true, + 'created_zones' => $createdZones, + 'updated_zones' => $updatedZones, + 'over_capacity_count' => $overCapacityCount, + 'parameters' => $processParameters, + 'errors' => $errors, + 'message' => '現在契約台数集計処理完了' + ]; + + } catch (\Exception $e) { + DB::rollBack(); + + Log::error('SHJ-11 ゾーンマスタ管理処理全体エラー', [ + 'error' => $e->getMessage(), + 'processed_count' => count($processParameters) + ]); + + return [ + 'success' => false, + 'created_zones' => $createdZones, + 'updated_zones' => $updatedZones, + 'over_capacity_count' => $overCapacityCount, + 'parameters' => $processParameters, + 'errors' => $errors, + 'message' => 'ゾーンマスタ管理処理エラー: ' . $e->getMessage(), + 'details' => $e->getTraceAsString() + ]; + } + } + + /** + * ゾーンマスタデータ取得 + * + * 集計単位に対応するゾーンマスタを取得 + * + * @param object $contractData 契約台数集計データ + * @return object|null ゾーンマスタデータ + */ + private function getZoneData($contractData) + { + try { + return DB::table('zone') + ->select([ + 'zone_id', + 'park_id', + 'ptype_id', + 'psection_id', + 'zone_name', + 'zone_number', // 現在契約台数 + 'zone_standard', // 標準収容台数 + 'zone_tolerance' // 限界収容台数 + ]) + ->where([ + ['park_id', '=', $contractData->park_id], + ['psection_id', '=', $contractData->psection_id], + ['ptype_id', '=', $contractData->ptype_id], + ['zone_id', '=', $contractData->zone_id] + ]) + ->first(); + + } catch (\Exception $e) { + Log::error('SHJ-11 ゾーンマスタ取得エラー', [ + 'park_id' => $contractData->park_id, + 'zone_id' => $contractData->zone_id, + 'error' => $e->getMessage() + ]); + throw $e; + } + } + + /** + * ゾーンマスタ新規作成 + * + * 仕様書に基づくINSERT処理 + * + * @param object $contractData 契約台数集計データ + * @return array 作成結果 + */ + private function createZoneData($contractData): array + { + try { + // 新規ゾーンマスタのデフォルト値設定 + $newZoneData = [ + 'zone_id' => $contractData->zone_id, + 'park_id' => $contractData->park_id, + 'ptype_id' => $contractData->ptype_id, + 'psection_id' => $contractData->psection_id, + 'zone_name' => null, // デフォルトnull + 'zone_number' => $contractData->cnt, // 算出した契約台数 + 'zone_standard' => null, // デフォルトnull + 'zone_tolerance' => null, // デフォルトnull + 'zone_sort' => null, // デフォルトnull + 'delete_flag' => 0, // 削除フラグOFF + 'created_at' => now(), + 'updated_at' => now(), + 'ope_id' => 9999999 // 仕様書指定値 + ]; + + DB::table('zone')->insert($newZoneData); + + Log::info('SHJ-11 ゾーンマスタ新規作成完了', [ + 'zone_id' => $contractData->zone_id, + 'park_id' => $contractData->park_id, + 'contract_count' => $contractData->cnt + ]); + + return [ + 'success' => true, + 'zone_data' => (object) $newZoneData, + 'message' => 'ゾーンマスタを新規作成しました' + ]; + + } catch (\Exception $e) { + Log::error('SHJ-11 ゾーンマスタ新規作成エラー', [ + 'zone_id' => $contractData->zone_id, + 'park_id' => $contractData->park_id, + 'error' => $e->getMessage() + ]); + + return [ + 'success' => false, + 'zone_data' => null, + 'message' => 'ゾーンマスタ新規作成エラー: ' . $e->getMessage() + ]; + } + } + + /** + * 契約台数限界チェック + * + * 【判断3】契約台数 > 限界収容台数の判定 + * + * @param object $contractData 契約台数集計データ + * @param object|null $zoneData ゾーンマスタデータ + * @return bool 限界台数超過フラグ + */ + private function checkCapacityLimit($contractData, $zoneData = null): bool + { + if (!$zoneData || is_null($zoneData->zone_tolerance)) { + // ゾーンデータが存在しないか、限界収容台数が未設定の場合は超過判定しない + return false; + } + + // 契約台数 > 限界収容台数の判定 + return $contractData->cnt > $zoneData->zone_tolerance; + } + + /** + * ゾーンマスタ契約台数更新 + * + * 【処理3】現在契約台数をゾーンマスタに反映 + * + * @param object $contractData 契約台数集計データ + * @return array 更新結果 + */ + private function updateZoneContractCount($contractData): array + { + try { + $updateData = [ + 'zone_number' => $contractData->cnt, // 現在契約台数を更新 + 'updated_at' => now(), // 更新日時 + 'ope_id' => 'SHJ-11' // 更新オペレータID + ]; + + $updated = DB::table('zone') + ->where([ + ['park_id', '=', $contractData->park_id], + ['psection_id', '=', $contractData->psection_id], + ['ptype_id', '=', $contractData->ptype_id], + ['zone_id', '=', $contractData->zone_id] + ]) + ->update($updateData); + + if ($updated > 0) { + Log::info('SHJ-11 ゾーンマスタ更新完了', [ + 'zone_id' => $contractData->zone_id, + 'park_id' => $contractData->park_id, + 'new_contract_count' => $contractData->cnt + ]); + + return [ + 'success' => true, + 'message' => 'ゾーンマスタ契約台数を更新しました' + ]; + } else { + return [ + 'success' => false, + 'message' => 'ゾーンマスタ更新対象が見つかりません' + ]; + } + + } catch (\Exception $e) { + Log::error('SHJ-11 ゾーンマスタ更新エラー', [ + 'zone_id' => $contractData->zone_id, + 'park_id' => $contractData->park_id, + 'error' => $e->getMessage() + ]); + + return [ + 'success' => false, + 'message' => 'ゾーンマスタ更新エラー: ' . $e->getMessage() + ]; + } + } + + /** + * 【処理4】バッチ処理ログを作成する + * + * 統一BatchLogシステムを使用してSHJ-11の実行ログを記録 + * + * @param string $status ステータス + * @param array $parameters パラメータ + * @param string $message メッセージ + * @param int $executionCount 実行回数 + * @param int $successCount 成功回数 + * @param int $errorCount エラー回数 + * @return void + */ + public function createBatchLog( + string $status, + array $parameters, + string $message, + int $executionCount = 0, + int $successCount = 0, + int $errorCount = 0 + ): void { + try { + BatchLog::createBatchLog( + 'SHJ-11', + $status, + $parameters, + $message, + [ + 'execution_count' => $executionCount, + 'success_count' => $successCount, + 'error_count' => $errorCount, + 'process_type' => '現在契約台数集計', + 'executed_at' => now()->toISOString() + ] + ); + + } catch (\Exception $e) { + Log::error('SHJ-11 バッチログ作成エラー', [ + 'error' => $e->getMessage(), + 'status' => $status, + 'message' => $message + ]); + } + } +} diff --git a/app/Services/ShjMailSendService.php b/app/Services/ShjMailSendService.php index fca1779..64d9513 100644 --- a/app/Services/ShjMailSendService.php +++ b/app/Services/ShjMailSendService.php @@ -440,4 +440,106 @@ class ShjMailSendService 'message' => $message ]); } + + /** + * SHJ-12 未払い者通知メール送信 + * + * SHJ-12から呼び出される専用メール送信メソッド + * 未払い者への通知メールを送信する + * + * @param array $mailParams メール送信パラメータ + * @return array 送信結果 + */ + public function sendUnpaidNotificationMail(array $mailParams): array + { + try { + // パラメータ展開 + $toEmail = $mailParams['to_email'] ?? ''; + $userName = $mailParams['user_name'] ?? ''; + $parkName = $mailParams['park_name'] ?? ''; + $billingAmount = $mailParams['billing_amount'] ?? 0; + $contractId = $mailParams['contract_id'] ?? ''; + + // 未払い者通知専用のメールテンプレートID(仮定: 1) + // 実際の運用では設定ファイルまたはデータベースから取得 + $mailTemplateId = 1; + + // メールテンプレート情報取得 + $templateInfo = $this->getMailTemplateInfo($mailTemplateId); + + if (!$templateInfo) { + return [ + 'success' => false, + 'message' => '未払い者通知用メールテンプレートが見つかりません' + ]; + } + + // メール件名とメッセージのカスタマイズ + $subject = str_replace( + ['{park_name}', '{user_name}'], + [$parkName, $userName], + $templateInfo->getSubject() + ); + + $message = str_replace( + ['{user_name}', '{park_name}', '{billing_amount}', '{contract_id}'], + [$userName, $parkName, number_format($billingAmount), $contractId], + $templateInfo->getText() + ); + + // 追加ヘッダ設定 + $headers = $this->buildMailHeaders($templateInfo); + + Log::info('SHJ-12 未払い者通知メール送信準備完了', [ + 'to_email' => $toEmail, + 'user_name' => $userName, + 'billing_amount' => $billingAmount, + 'contract_id' => $contractId + ]); + + // メール送信実行 + $sendResult = mb_send_mail( + $toEmail, // 送信先 + $subject, // 件名 + $message, // 本文 + $headers // 追加ヘッダ + ); + + if ($sendResult) { + Log::info('SHJ-12 未払い者通知メール送信成功', [ + 'to_email' => $toEmail, + 'contract_id' => $contractId + ]); + + return [ + 'success' => true, + 'message' => '未払い者通知メール送信完了' + ]; + } else { + $errorMessage = '未払い者通知メール送信に失敗しました'; + Log::error('SHJ-12 未払い者通知メール送信失敗', [ + 'to_email' => $toEmail, + 'contract_id' => $contractId, + 'error' => $errorMessage + ]); + + return [ + 'success' => false, + 'message' => $errorMessage + ]; + } + + } catch (\Exception $e) { + $errorMessage = 'SHJ-12 未払い者通知メール送信エラー: ' . $e->getMessage(); + Log::error('SHJ-12 未払い者通知メール送信エラー', [ + 'error' => $e->getMessage(), + 'mail_params' => $mailParams + ]); + + return [ + 'success' => false, + 'message' => $errorMessage + ]; + } + } } \ No newline at end of file diff --git a/app/Services/ShjTwelveService.php b/app/Services/ShjTwelveService.php new file mode 100644 index 0000000..3380c4c --- /dev/null +++ b/app/Services/ShjTwelveService.php @@ -0,0 +1,335 @@ +shjMailSendService = $shjMailSendService; + } + + /** + * 【処理1】定期契約マスタより未払い者を取得する + * + * SQL条件: + * - 解約フラグ = 0 (未解約) + * - 授受フラグ = 0 (授受フラグOFF) + * - 請求金額 > 0 (請求金額あり) + * + * @return array 未払い者リスト + */ + public function getUnpaidUsers(): array + { + try { + $query = DB::table('regular_contract as T1') + ->select([ + 'T1.contract_id', // 定期契約ID + 'T2.user_seq', // 利用者ID + 'T2.user_name', // 利用者名 + 'T2.user_manual_flag', // 手動登録フラグ + 'T2.user_mail', // メールアドレス + 'T2.user_mail_sub', // 予備メールアドレス + 'T2.park_id', // 駐輪場ID (userテーブルから) + 'T3.park_name', // 駐輪場名 + 'T1.billing_amount' // 請求金額 + ]) + ->join('user as T2', function($join) { + $join->on('T1.user_seq', '=', 'T2.user_seq'); + }) + ->join('park as T3', 'T1.park_id', '=', 'T3.park_id') + ->where([ + ['T1.contract_cancel_flag', '=', 0], // 解約フラグ = 0 + ['T1.contract_flag', '=', 0], // 授受フラグ = 0 + ]) + ->where('T1.billing_amount', '>', 0) // 請求金額 > 0 + ->get(); + + Log::info('SHJ-12 未払い者取得完了', [ + 'count' => $query->count(), + 'sql_conditions' => [ + 'contract_cancel_flag' => 0, + 'contract_flag' => 0, + 'billing_amount' => '> 0' + ] + ]); + + return $query->toArray(); + + } catch (\Exception $e) { + Log::error('SHJ-12 未払い者取得エラー', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + throw $e; + } + } + + /** + * 【処理2】未払い者への通知、またはオペレーターキュー追加処理 + * + * 各未払い者に対して以下の処理を実行: + * 1. メール通知の実行 (SHJ-7連携) + * 2. オペレーターキューへの追加 (opeテーブル) + * + * @param array $unpaidUsers 未払い者リスト + * @return array 処理結果 + */ + public function processUnpaidUserNotifications(array $unpaidUsers): array + { + $notificationCount = 0; + $queueCount = 0; + $errors = []; + $processParameters = []; + + try { + DB::beginTransaction(); + + foreach ($unpaidUsers as $user) { + try { + // メール通知処理 + $mailResult = $this->sendNotificationMail($user); + if ($mailResult['success']) { + $notificationCount++; + } + + // オペレーターキュー追加処理 + $queueResult = $this->addToOperatorQueue($user); + if ($queueResult['success']) { + $queueCount++; + } + + // 処理パラメータ記録 + $processParameters[] = [ + 'contract_id' => $user->contract_id, + 'user_seq' => $user->user_seq, + 'billing_amount' => $user->billing_amount, + 'mail_sent' => $mailResult['success'], + 'queue_added' => $queueResult['success'] + ]; + + } catch (\Exception $e) { + $errors[] = [ + 'contract_id' => $user->contract_id, + 'error' => $e->getMessage() + ]; + + Log::warning('SHJ-12 個別処理エラー', [ + 'contract_id' => $user->contract_id, + 'user_seq' => $user->user_seq, + 'error' => $e->getMessage() + ]); + } + } + + DB::commit(); + + return [ + 'success' => true, + 'notification_count' => $notificationCount, + 'queue_count' => $queueCount, + 'parameters' => $processParameters, + 'errors' => $errors, + 'message' => '未払い者通知処理完了' + ]; + + } catch (\Exception $e) { + DB::rollBack(); + + Log::error('SHJ-12 通知処理全体エラー', [ + 'error' => $e->getMessage(), + 'processed_count' => count($processParameters) + ]); + + return [ + 'success' => false, + 'notification_count' => $notificationCount, + 'queue_count' => $queueCount, + 'parameters' => $processParameters, + 'errors' => $errors, + 'message' => '通知処理エラー: ' . $e->getMessage(), + 'details' => $e->getTraceAsString() + ]; + } + } + + /** + * 未払い者へのメール通知送信 + * + * SHJ-7 メール送信サービスを使用してメール通知を実行 + * + * @param object $user 未払い者情報 + * @return array 送信結果 + */ + private function sendNotificationMail($user): array + { + try { + // メールアドレスの確認 + $emailAddress = $user->user_mail ?: $user->user_mail_sub; + + if (empty($emailAddress)) { + return [ + 'success' => false, + 'message' => 'メールアドレスが設定されていません' + ]; + } + + // SHJ-7 メール送信サービス呼び出し + $mailParams = [ + 'to_email' => $emailAddress, + 'user_name' => $user->user_name, + 'park_name' => $user->park_name, + 'billing_amount' => $user->billing_amount, + 'contract_id' => $user->contract_id + ]; + + $result = $this->shjMailSendService->sendUnpaidNotificationMail($mailParams); + + Log::info('SHJ-12 メール送信結果', [ + 'contract_id' => $user->contract_id, + 'email' => $emailAddress, + 'success' => $result['success'] + ]); + + return $result; + + } catch (\Exception $e) { + Log::error('SHJ-12 メール送信エラー', [ + 'contract_id' => $user->contract_id, + 'error' => $e->getMessage() + ]); + + return [ + 'success' => false, + 'message' => 'メール送信エラー: ' . $e->getMessage() + ]; + } + } + + /** + * オペレーターキューへの追加 + * + * opeテーブルにオペレーター処理キューとして登録 + * + * @param object $user 未払い者情報 + * @return array 追加結果 + */ + private function addToOperatorQueue($user): array + { + try { + $queueData = [ + 'ope_device_id' => null, // デバイスID (未設定) + 'ope_process_name' => 'SHJ-12', // プロセス名 + 'ope_job_name' => '未払い者通知', // ジョブ名 + 'ope_status' => 'pending', // ステータス + 'ope_comment' => sprintf( + '契約ID:%s ユーザー:%s 金額:%s円', + $user->contract_id, + $user->user_name, + number_format($user->billing_amount) + ), + 'ope_target_user_id' => $user->user_seq, // 対象ユーザーID + 'ope_target_contract_id' => $user->contract_id, // 対象契約ID + 'ope_billing_amount' => $user->billing_amount, // 請求金額 + 'created_at' => now(), + 'updated_at' => now() + ]; + + // opeテーブルに挿入 + DB::table('ope')->insert($queueData); + + Log::info('SHJ-12 オペレーターキュー追加完了', [ + 'contract_id' => $user->contract_id, + 'user_seq' => $user->user_seq, + 'billing_amount' => $user->billing_amount + ]); + + return [ + 'success' => true, + 'message' => 'オペレーターキューに追加しました' + ]; + + } catch (\Exception $e) { + Log::error('SHJ-12 オペレーターキュー追加エラー', [ + 'contract_id' => $user->contract_id, + 'error' => $e->getMessage() + ]); + + return [ + 'success' => false, + 'message' => 'オペレーターキュー追加エラー: ' . $e->getMessage() + ]; + } + } + + /** + * 【処理3】バッチ処理ログを作成する + * + * 統一BatchLogシステムを使用してSHJ-12の実行ログを記録 + * + * @param string $status ステータス + * @param array $parameters パラメータ + * @param string $message メッセージ + * @param int $executionCount 実行回数 + * @param int $successCount 成功回数 + * @param int $errorCount エラー回数 + * @return void + */ + public function createBatchLog( + string $status, + array $parameters, + string $message, + int $executionCount = 0, + int $successCount = 0, + int $errorCount = 0 + ): void { + try { + BatchLog::createBatchLog( + 'SHJ-12', + $status, + $parameters, + $message, + [ + 'execution_count' => $executionCount, + 'success_count' => $successCount, + 'error_count' => $errorCount, + 'process_type' => '未払い者通知処理', + 'executed_at' => now()->toISOString() + ] + ); + + } catch (\Exception $e) { + Log::error('SHJ-12 バッチログ作成エラー', [ + 'error' => $e->getMessage(), + 'status' => $status, + 'message' => $message + ]); + } + } +}