524 lines
20 KiB
PHP
524 lines
20 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\Log;
|
||
use App\Models\Device;
|
||
use App\Models\RegularContract;
|
||
use App\Models\User;
|
||
use App\Models\Park;
|
||
use App\Models\Ope;
|
||
use App\Services\ShjMailSendService;
|
||
|
||
/**
|
||
* SHJ-12 未払い者通知処理サービス
|
||
*
|
||
* 定期契約マスタより未払い者を取得し、通知処理またはオペレーターキュー追加を実行
|
||
*/
|
||
class ShjTwelveService
|
||
{
|
||
/**
|
||
* SHJメール送信サービス
|
||
*
|
||
* @var ShjMailSendService
|
||
*/
|
||
protected $shjMailSendService;
|
||
|
||
/**
|
||
* ShjEightService
|
||
*
|
||
* @var ShjEightService
|
||
*/
|
||
protected $shjEightService;
|
||
|
||
/**
|
||
* コンストラクタ
|
||
*
|
||
* @param ShjMailSendService $shjMailSendService
|
||
* @param ShjEightService $shjEightService
|
||
*/
|
||
public function __construct(
|
||
ShjMailSendService $shjMailSendService,
|
||
ShjEightService $shjEightService
|
||
) {
|
||
$this->shjMailSendService = $shjMailSendService;
|
||
$this->shjEightService = $shjEightService;
|
||
}
|
||
|
||
/**
|
||
* 【処理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_regist_flag', // 手動登録フラグ
|
||
'T2.user_primemail', // メールアドレス
|
||
'T2.user_submail', // 予備メールアドレス
|
||
'T1.park_id', // 駐輪場ID (regular_contractテーブルから)
|
||
'T3.park_name', // 駐輪場名
|
||
'T1.billing_amount' // 請求金額
|
||
])
|
||
->join('user as T2', 'T1.user_id', '=', '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】未払い者への通知、またはオペレーターキュー追加処理
|
||
*
|
||
* 仕様:
|
||
* - 手動登録フラグ = 0 (Web申込) → SHJ-7メール送信のみ
|
||
* - 手動登録フラグ ≠ 0 (その他) → オペレーターキュー追加のみ
|
||
*
|
||
* @param array $unpaidUsers 未払い者リスト
|
||
* @return array 処理結果
|
||
*/
|
||
public function processUnpaidUserNotifications(array $unpaidUsers): array
|
||
{
|
||
// 内部変数(カウンタ)初期化
|
||
$mailSuccessCount = 0;
|
||
$mailErrorCount = 0;
|
||
$queueSuccessCount = 0;
|
||
$queueErrorCount = 0;
|
||
$batchComments = []; // バッチコメント(エラー情報)累積用
|
||
$processParameters = [];
|
||
|
||
try {
|
||
DB::beginTransaction();
|
||
|
||
// 処理1の取得レコード数分繰り返し
|
||
foreach ($unpaidUsers as $user) {
|
||
try {
|
||
// 【判定】手動登録フラグによる分岐
|
||
if ($user->user_manual_regist_flag == 0) {
|
||
// Web申込 → SHJ-7メール送信
|
||
$mailResult = $this->sendNotificationMailViaShj7($user);
|
||
|
||
// 処理結果判定: result === 0 → 正常
|
||
if (($mailResult['result'] ?? 1) === 0) {
|
||
$mailSuccessCount++;
|
||
} else {
|
||
$mailErrorCount++;
|
||
// バッチコメントにSHJ-7異常情報を設定(後ろに足す)
|
||
$batchComments[] = sprintf(
|
||
'SHJ-7メール送信: 契約ID:%s メール送信失敗: %s',
|
||
$user->contract_id,
|
||
$mailResult['error_info'] ?? 'Unknown error'
|
||
);
|
||
}
|
||
|
||
// 処理パラメータ記録
|
||
$processParameters[] = [
|
||
'contract_id' => $user->contract_id,
|
||
'user_seq' => $user->user_seq,
|
||
'billing_amount' => $user->billing_amount,
|
||
'process_type' => 'mail',
|
||
'result' => ($mailResult['result'] ?? 1) === 0 ? 'success' : 'error'
|
||
];
|
||
|
||
} else {
|
||
// その他(手動登録)→ オペレーターキュー追加
|
||
$queueResult = $this->addToOperatorQueue($user);
|
||
|
||
if ($queueResult['success']) {
|
||
$queueSuccessCount++;
|
||
} else {
|
||
$queueErrorCount++;
|
||
// バッチコメントに異常情報を設定(後ろに足す)
|
||
$batchComments[] = sprintf(
|
||
'キュー登録: 契約ID:%s キュー登録失敗: %s',
|
||
$user->contract_id,
|
||
$queueResult['message'] ?? 'Unknown error'
|
||
);
|
||
}
|
||
|
||
// 処理パラメータ記録
|
||
$processParameters[] = [
|
||
'contract_id' => $user->contract_id,
|
||
'user_seq' => $user->user_seq,
|
||
'billing_amount' => $user->billing_amount,
|
||
'process_type' => 'queue',
|
||
'result' => $queueResult['success'] ? 'success' : 'error'
|
||
];
|
||
}
|
||
|
||
} catch (\Exception $e) {
|
||
// 個別エラーを記録して次の繰り返し処理へ
|
||
$batchComments[] = sprintf(
|
||
'契約ID:%s 処理エラー: %s',
|
||
$user->contract_id,
|
||
$e->getMessage()
|
||
);
|
||
|
||
Log::warning('SHJ-12 個別処理エラー', [
|
||
'contract_id' => $user->contract_id,
|
||
'user_seq' => $user->user_seq,
|
||
'error' => $e->getMessage()
|
||
]);
|
||
}
|
||
}
|
||
|
||
DB::commit();
|
||
|
||
// ステータスコメント構築
|
||
$statusComment = $this->buildStatusComment(
|
||
$mailSuccessCount,
|
||
$mailErrorCount,
|
||
$queueSuccessCount,
|
||
$queueErrorCount
|
||
);
|
||
|
||
// バッチコメント整形(最大100件、超過分は省略)
|
||
$formattedBatchComments = $this->formatBatchComments($batchComments);
|
||
|
||
return [
|
||
'success' => true,
|
||
'mail_success_count' => $mailSuccessCount,
|
||
'mail_error_count' => $mailErrorCount,
|
||
'queue_success_count' => $queueSuccessCount,
|
||
'queue_error_count' => $queueErrorCount,
|
||
'status_comment' => $statusComment,
|
||
'batch_comments' => $formattedBatchComments,
|
||
'parameters' => $processParameters,
|
||
'message' => '未払い者通知処理完了'
|
||
];
|
||
|
||
} catch (\Exception $e) {
|
||
DB::rollBack();
|
||
|
||
Log::error('SHJ-12 通知処理全体エラー', [
|
||
'error' => $e->getMessage(),
|
||
'processed_count' => count($processParameters)
|
||
]);
|
||
|
||
return [
|
||
'success' => false,
|
||
'mail_success_count' => $mailSuccessCount,
|
||
'mail_error_count' => $mailErrorCount,
|
||
'queue_success_count' => $queueSuccessCount,
|
||
'queue_error_count' => $queueErrorCount,
|
||
'status_comment' => 'システムエラー',
|
||
'batch_comments' => $batchComments,
|
||
'parameters' => $processParameters,
|
||
'message' => '通知処理エラー: ' . $e->getMessage(),
|
||
'details' => $e->getTraceAsString()
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* SHJ-7 メール送信サービス経由でメール通知送信
|
||
*
|
||
* 仕様書パラメータ:
|
||
* - メールアドレス: 処理1.メールアドレス
|
||
* - 予備メールアドレス: 処理1.予備メールアドレス
|
||
* - 使用プログラムID: 204
|
||
*
|
||
* @param object $user 未払い者情報
|
||
* @return array SHJ-7の処理結果 (result_code: 0=正常, 1=異常)
|
||
*/
|
||
private function sendNotificationMailViaShj7($user): array
|
||
{
|
||
try {
|
||
// SHJ-7 executeMailSend 呼び出し
|
||
$result = $this->shjMailSendService->executeMailSend(
|
||
(string)($user->user_primemail ?? ''), // メールアドレス
|
||
(string)($user->user_submail ?? ''), // 予備メールアドレス
|
||
204 // 使用プログラムID
|
||
);
|
||
|
||
Log::info('SHJ-12 SHJ-7メール送信結果', [
|
||
'contract_id' => $user->contract_id,
|
||
'user_seq' => $user->user_seq,
|
||
'result' => $result['result'] ?? 1,
|
||
'error_info' => $result['error_info'] ?? ''
|
||
]);
|
||
|
||
return $result;
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('SHJ-12 SHJ-7メール送信エラー', [
|
||
'contract_id' => $user->contract_id,
|
||
'error' => $e->getMessage()
|
||
]);
|
||
|
||
return [
|
||
'result' => 1, // 異常終了
|
||
'error_info' => 'SHJ-7メール送信エラー: ' . $e->getMessage()
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* オペレーターキューへの追加
|
||
*
|
||
* 仕様書SQL定義に基づき operator_que テーブルに登録
|
||
* - キュー種別ID: 8 (支払い催促)
|
||
* - キューステータスID: 1 (キュー発生)
|
||
* - que_id: MAX+1方式で生成(db_now.sqlは非AUTO_INCREMENT)
|
||
* - 主キー衝突時に1回リトライ
|
||
*
|
||
* @param object $user 未払い者情報
|
||
* @return array 追加結果
|
||
*/
|
||
private function addToOperatorQueue($user): array
|
||
{
|
||
$maxRetries = 2; // 最大2回試行(初回 + リトライ1回)
|
||
$lastException = null;
|
||
|
||
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
|
||
try {
|
||
// オペレーターキューデータ構築
|
||
// 注: que_idはAUTO_INCREMENTのため、DBに委任(手動採番しない)
|
||
$queueData = [
|
||
// 'que_id' は削除(AUTO_INCREMENTに委任)
|
||
'que_class' => 8, // キュー種別ID: 8=支払い催促
|
||
'user_id' => $user->user_seq, // 利用者ID
|
||
'contract_id' => $user->contract_id, // 定期契約ID
|
||
'park_id' => $user->park_id, // 駐輪場ID
|
||
'que_comment' => '', // キューコメント (空文字)
|
||
'que_status' => 1, // キューステータスID: 1=キュー発生
|
||
'que_status_comment' => '', // キューステータスコメント (空文字)
|
||
'work_instructions' => '', // 業務指示コメント (空文字)
|
||
'created_at' => now(), // 登録日時
|
||
'updated_at' => now(), // 更新日時
|
||
'operator_id' => null // 更新オペレータID (NULL: システム自動)
|
||
];
|
||
|
||
// operator_queテーブルに挿入(que_idはDBが自動採番)
|
||
$newQueId = DB::table('operator_que')->insertGetId($queueData);
|
||
|
||
Log::info('SHJ-12 オペレーターキュー追加完了', [
|
||
'que_id' => $newQueId,
|
||
'contract_id' => $user->contract_id,
|
||
'user_seq' => $user->user_seq,
|
||
'park_id' => $user->park_id,
|
||
'que_class' => 8,
|
||
'que_status' => 1,
|
||
'attempt' => $attempt
|
||
]);
|
||
|
||
return [
|
||
'success' => true,
|
||
'message' => 'オペレーターキュー追加成功',
|
||
'que_id' => $newQueId
|
||
];
|
||
|
||
} catch (\Illuminate\Database\QueryException $e) {
|
||
$lastException = $e;
|
||
|
||
// 主キー重複エラー(Duplicate entry)の場合のみリトライ
|
||
// SQLSTATEコード 23000 は整合性制約違反を示す
|
||
if ($e->getCode() == 23000 && $attempt < $maxRetries) {
|
||
Log::warning('SHJ-12 que_id重複検出、リトライします', [
|
||
'attempt' => $attempt,
|
||
'max_retries' => $maxRetries,
|
||
'que_id' => $newQueId ?? null,
|
||
'contract_id' => $user->contract_id,
|
||
'error_code' => $e->getCode()
|
||
]);
|
||
|
||
// 短時間待機後に再試行(100ms)
|
||
usleep(100000);
|
||
continue;
|
||
}
|
||
|
||
// その他のエラー、または最終試行での失敗時は例外をスロー
|
||
throw $e;
|
||
|
||
} catch (\Exception $e) {
|
||
// QueryException以外の例外は即座にスロー
|
||
$lastException = $e;
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
// ループを抜けた場合(通常は到達しない)
|
||
Log::error('SHJ-12 オペレーターキュー追加エラー(最大リトライ回数超過)', [
|
||
'contract_id' => $user->contract_id,
|
||
'user_seq' => $user->user_seq,
|
||
'max_retries' => $maxRetries,
|
||
'error' => $lastException ? $lastException->getMessage() : 'Unknown error',
|
||
'trace' => $lastException ? $lastException->getTraceAsString() : ''
|
||
]);
|
||
|
||
return [
|
||
'success' => false,
|
||
'message' => 'オペレーターキュー追加エラー(リトライ失敗): ' .
|
||
($lastException ? $lastException->getMessage() : 'Unknown error')
|
||
];
|
||
}
|
||
|
||
/**
|
||
* ステータスコメント構築
|
||
*
|
||
* 仕様書に基づくステータスコメント形式:
|
||
* - エラーなし: "メール送信成功:X件 / キュー登録成功:Y件"
|
||
* - エラーあり: "メール送信成功:X件 / メール送信失敗:A件 / キュー登録成功:Y件 / キュー登録失敗:B件"
|
||
*
|
||
* @param int $mailSuccessCount メール正常終了件数
|
||
* @param int $mailErrorCount メール異常終了件数
|
||
* @param int $queueSuccessCount キュー登録正常終了件数
|
||
* @param int $queueErrorCount キュー登録異常終了件数
|
||
* @return string ステータスコメント
|
||
*/
|
||
private function buildStatusComment(
|
||
int $mailSuccessCount,
|
||
int $mailErrorCount,
|
||
int $queueSuccessCount,
|
||
int $queueErrorCount
|
||
): string {
|
||
$parts = [];
|
||
|
||
// メール送信結果
|
||
if ($mailSuccessCount > 0 || $mailErrorCount > 0) {
|
||
if ($mailErrorCount > 0) {
|
||
$parts[] = "メール送信成功:{$mailSuccessCount}件";
|
||
$parts[] = "メール送信失敗:{$mailErrorCount}件";
|
||
} else {
|
||
$parts[] = "メール送信成功:{$mailSuccessCount}件";
|
||
}
|
||
}
|
||
|
||
// キュー登録結果
|
||
if ($queueSuccessCount > 0 || $queueErrorCount > 0) {
|
||
if ($queueErrorCount > 0) {
|
||
$parts[] = "キュー登録成功:{$queueSuccessCount}件";
|
||
$parts[] = "キュー登録失敗:{$queueErrorCount}件";
|
||
} else {
|
||
$parts[] = "キュー登録成功:{$queueSuccessCount}件";
|
||
}
|
||
}
|
||
|
||
return implode(' / ', $parts);
|
||
}
|
||
|
||
/**
|
||
* バッチコメント整形
|
||
*
|
||
* エラー情報を最大100件に制限し、超過分は省略表記
|
||
*
|
||
* @param array $batchComments エラー情報配列
|
||
* @return string 整形済みバッチコメント
|
||
*/
|
||
private function formatBatchComments(array $batchComments): string
|
||
{
|
||
if (empty($batchComments)) {
|
||
return '';
|
||
}
|
||
|
||
// 最大100件に制限
|
||
$limitedComments = array_slice($batchComments, 0, 100);
|
||
|
||
// 超過分の件数を追加
|
||
if (count($batchComments) > 100) {
|
||
$limitedComments[] = sprintf(
|
||
'...他%d件省略',
|
||
count($batchComments) - 100
|
||
);
|
||
}
|
||
|
||
return implode("\n", $limitedComments);
|
||
}
|
||
|
||
/**
|
||
* 【処理3】バッチ処理ログを作成する
|
||
*
|
||
* SHJ-8サービスを呼び出してbat_job_logテーブルに登録(業務固有のstatus_comment記録)
|
||
*
|
||
* @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 {
|
||
// デバイスIDを取得
|
||
$device = Device::orderBy('device_id')->first();
|
||
$deviceId = $device ? $device->device_id : 1;
|
||
|
||
// status_commentを構築(業務情報を含む)
|
||
$statusComment = sprintf(
|
||
'%s (実行:%d/成功:%d/エラー:%d)',
|
||
$message,
|
||
$executionCount,
|
||
$successCount,
|
||
$errorCount
|
||
);
|
||
|
||
$today = now()->format('Y/m/d');
|
||
|
||
Log::info('SHJ-8 バッチ処理ログ作成', [
|
||
'device_id' => $deviceId,
|
||
'status' => $status,
|
||
'status_comment' => $statusComment
|
||
]);
|
||
|
||
// SHJ-8サービスを呼び出し
|
||
$this->shjEightService->execute(
|
||
$deviceId,
|
||
'SHJ-12',
|
||
'SHJ-12未払い者通知',
|
||
$status,
|
||
$statusComment,
|
||
$today,
|
||
$today
|
||
);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('SHJ-8 バッチログ作成エラー', [
|
||
'error' => $e->getMessage(),
|
||
'status' => $status,
|
||
'message' => $message
|
||
]);
|
||
}
|
||
}
|
||
}
|