so-manager-dev.com/app/Services/ShjTwelveService.php
Your Name 10a917b556
All checks were successful
Deploy so-manager (auto) / deploy (push) Successful in 24s
【更新】SHJ関連の修正
2025-10-10 19:55:46 +09:00

524 lines
20 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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
]);
}
}
}