All checks were successful
Deploy api / deploy (push) Successful in 23s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1374 lines
55 KiB
PHP
1374 lines
55 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\SettlementTransaction;
|
||
use App\Models\RegularContract;
|
||
use App\Models\Park;
|
||
use App\Models\PriceA;
|
||
use App\Models\BatJobLog;
|
||
use App\Models\Device;
|
||
use App\Models\User;
|
||
use App\Services\ShjFourCService;
|
||
use App\Services\ShjThirteenService;
|
||
use App\Services\ShjEightService;
|
||
use App\Services\ShjMailSendService;
|
||
use Illuminate\Support\Facades\Log;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\Storage;
|
||
use Carbon\Carbon;
|
||
|
||
/**
|
||
* SHJ-4B 定期契約更新処理サービス
|
||
*
|
||
* SHJ-4Aで登録された決済情報を基に定期契約の更新処理を実行する
|
||
* ウェルネットのPUSH通知を契機とした決済トランザクション処理
|
||
*/
|
||
class ShjFourBService
|
||
{
|
||
/**
|
||
* 金額比較結果の定数
|
||
*/
|
||
const AMOUNT_MATCH = 'match'; // 金額一致
|
||
const AMOUNT_SHORTAGE = 'shortage'; // 授受過少
|
||
const AMOUNT_EXCESS = 'excess'; // 授受超過
|
||
|
||
/**
|
||
* 契約フラグの状態定数
|
||
*/
|
||
const CONTRACT_FLAG_PROCESSING = 0; // 処理中
|
||
const CONTRACT_FLAG_UPDATED = 1; // 更新済
|
||
const CONTRACT_FLAG_ERROR = 2; // エラー状態
|
||
|
||
/**
|
||
* SHJ-8 バッチ処理ログ作成サービス
|
||
*
|
||
* @var ShjEightService
|
||
*/
|
||
protected $shjEightService;
|
||
|
||
/**
|
||
* SHJ-4C 車室割り当てサービス
|
||
*
|
||
* @var ShjFourCService
|
||
*/
|
||
protected $shjFourCService;
|
||
|
||
/**
|
||
* SHJ-7 メール送信サービス
|
||
*
|
||
* @var ShjMailSendService
|
||
*/
|
||
protected $mailSendService;
|
||
|
||
/**
|
||
* コンストラクタ
|
||
*
|
||
* @param ShjEightService $shjEightService
|
||
* @param ShjMailSendService $mailSendService
|
||
* @param ShjFourCService $shjFourCService
|
||
*/
|
||
public function __construct(
|
||
ShjEightService $shjEightService,
|
||
ShjMailSendService $mailSendService,
|
||
ShjFourCService $shjFourCService
|
||
) {
|
||
$this->shjEightService = $shjEightService;
|
||
$this->mailSendService = $mailSendService;
|
||
$this->shjFourCService = $shjFourCService;
|
||
}
|
||
|
||
/**
|
||
* 決済トランザクション処理メイン実行
|
||
*
|
||
* SHJ-4Bの3段階判断処理を実装:
|
||
* 【判断0】対象契約取得判定
|
||
* 【判断1】授受状態チェック
|
||
* 【判断2】金額チェック
|
||
*
|
||
* @param int $settlementTransactionId 決済トランザクションID
|
||
* @param array $context 追加のコンテキスト情報
|
||
* @return array 処理結果
|
||
*/
|
||
public function processSettlementTransaction(int $settlementTransactionId, array $context = []): array
|
||
{
|
||
$startTime = now();
|
||
|
||
Log::info('SHJ-4B 決済トランザクション処理開始', [
|
||
'settlement_transaction_id' => $settlementTransactionId,
|
||
'context' => $context,
|
||
'start_time' => $startTime,
|
||
]);
|
||
|
||
try {
|
||
// 【前処理】決済トランザクション取得
|
||
$settlement = $this->getSettlementTransaction($settlementTransactionId);
|
||
|
||
// 【処理1】定期契約マスタの対象レコード取得
|
||
// 【判断0】取得判定(登録済み判定を含む)
|
||
$contractResult = $this->judgeTargetContract($settlement);
|
||
|
||
if (!$contractResult['found']) {
|
||
// 対象レコードなしの場合
|
||
$result = $this->handleNoTargetRecord($settlement, $contractResult);
|
||
$this->createBatchLog($settlement, null, null, true, null, null, null, $result['message']);
|
||
return $result;
|
||
}
|
||
|
||
if ($contractResult['already_processed']) {
|
||
// 登録済みの場合
|
||
$result = $this->handleAlreadyProcessed($settlement, $contractResult);
|
||
$contract = $contractResult['contract'];
|
||
$this->createBatchLog($settlement, $contract, null, true, null, null, null, $result['message']);
|
||
return $result;
|
||
}
|
||
|
||
$contract = $contractResult['contract'];
|
||
|
||
// 【判断1】授受状態チェック
|
||
$statusResult = $this->judgeReceiptStatus($settlement, $contract);
|
||
|
||
if (!$statusResult['valid']) {
|
||
// 授受済みの場合(仕様: JOB5メール送信 → JOB6バッチログ → 終了)
|
||
$result = $this->handleInvalidStatus($settlement, $contract, $statusResult);
|
||
// 【JOB5】メール送信(授受済みでも送信)
|
||
$mailResult = $this->sendUserNotificationMail($settlement, $contract);
|
||
$mailCommentSuffix = $mailResult['batch_comment_suffix'] ?? null;
|
||
// 【JOB6】バッチログ(授受済みメッセージを直接statusCommentとして渡す)
|
||
$this->createBatchLog(
|
||
$settlement, $contract, null, true, null, $mailCommentSuffix,
|
||
null, $result['message']
|
||
);
|
||
return $result;
|
||
}
|
||
|
||
// 【JOB3-0】SHJ-4C 車室割り当て(新規のみ)
|
||
// 仕様:定期契約継続フラグ(update_flag) = 2(新規)の場合に呼び出す
|
||
// ※異常があったとしても、処理は継続する
|
||
$shjFourCResult = null;
|
||
if ((int) $contract->update_flag === 2) {
|
||
$shjFourCResult = $this->triggerShjFourC($contract);
|
||
}
|
||
|
||
// 【JOB3-1】契約更新処理実行(SHJ-4C結果を含む)
|
||
// 仕様フロー順: JOB3-0 → JOB3-1/3-4 → JOB3-STEP1(金額チェック)
|
||
$updateResult = $this->executeContractUpdate($settlement, $contract, $shjFourCResult);
|
||
|
||
// 【判断2】金額チェック(仕様のJOB3-STEP1)
|
||
$amountResult = $this->judgeAmountComparison($settlement, $contract);
|
||
|
||
// 副作用処理実行(SHJ-4C結果をSHJ-13に渡す)
|
||
$sideEffectResult = $this->executeSideEffects($settlement, $contract, $amountResult, $updateResult, $shjFourCResult);
|
||
|
||
$result = [
|
||
'success' => true,
|
||
'settlement_transaction_id' => $settlementTransactionId,
|
||
'contract_id' => $contract->contract_id,
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'amount_comparison' => $amountResult['comparison'],
|
||
'contract_updated' => $updateResult['updated'],
|
||
'side_effects' => $sideEffectResult,
|
||
'execution_time' => now()->diffInSeconds($startTime),
|
||
];
|
||
|
||
Log::info('SHJ-4B 決済トランザクション処理完了', $result);
|
||
|
||
// 【処理6】バッチ処理ログ作成(SHJ-8呼び出し)
|
||
$mailCommentSuffix = $sideEffectResult['user_mail']['batch_comment_suffix'] ?? null;
|
||
$photoDeletionResult = $sideEffectResult['photo_deletion'] ?? null;
|
||
$this->createBatchLog($settlement, $contract, $amountResult, true, null, $mailCommentSuffix, $photoDeletionResult);
|
||
|
||
return $result;
|
||
|
||
} catch (\Throwable $e) {
|
||
Log::error('SHJ-4B 決済トランザクション処理失敗', [
|
||
'settlement_transaction_id' => $settlementTransactionId,
|
||
'context' => $context,
|
||
'error' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString(),
|
||
]);
|
||
|
||
// エラー時のバッチログ作成
|
||
try {
|
||
$settlement = SettlementTransaction::find($settlementTransactionId);
|
||
if ($settlement) {
|
||
$this->createBatchLog($settlement, null, null, false, $e->getMessage());
|
||
}
|
||
} catch (\Throwable $logError) {
|
||
Log::error('SHJ-4B バッチログ作成エラー', ['error' => $logError->getMessage()]);
|
||
}
|
||
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ウェルネットPUSHデータ処理入口
|
||
*
|
||
* 仕様順序: JOB1(SQL-1 契約検索) → JOB2(SQL-2 重複チェック → SQL-3 INSERT) → 既存処理
|
||
* 既存の processSettlementTransaction(id) は再処理・後方互換の入口として残す
|
||
*
|
||
* @param array $wellnetData ウェルネットからの生データ
|
||
* @return array 処理結果
|
||
*/
|
||
public function processWellnetPush(array $wellnetData): array
|
||
{
|
||
$contractPaymentNumber = $wellnetData['contract_payment_number'];
|
||
|
||
Log::info('SHJ-4B ウェルネットPUSHデータ処理開始', [
|
||
'contract_payment_number' => $contractPaymentNumber,
|
||
]);
|
||
|
||
// 【JOB1】SQL-1: 定期契約マスタの対象レコード取得(judgeTargetContractと同一条件)
|
||
$contract = DB::table('regular_contract as T1')
|
||
->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
|
||
->join('price_a as T4', function($join) {
|
||
$join->on('T1.price_parkplaceid', '=', 'T4.price_parkplaceid')
|
||
->on('T1.park_id', '=', 'T4.park_id');
|
||
})
|
||
->where('T1.contract_payment_number', $contractPaymentNumber)
|
||
->where('T1.contract_cancel_flag', '!=', 1)
|
||
->whereNotNull('T1.contract_flag')
|
||
->select('T1.contract_id')
|
||
->first();
|
||
|
||
// JOB1-STEP1: 対象レコード判定
|
||
if (!$contract) {
|
||
Log::warning('SHJ-4B JOB1: 受付番号不正(対象契約なし)', [
|
||
'contract_payment_number' => $contractPaymentNumber,
|
||
]);
|
||
// 仕様: JOB6 バッチログ → 終了(settlement_transaction未作成)
|
||
$statusComment = "受付番号が不正です。(受付番号:{$contractPaymentNumber})";
|
||
$this->createWellnetPushBatchLog($statusComment);
|
||
return [
|
||
'success' => true,
|
||
'result' => 'no_target',
|
||
'message' => $statusComment,
|
||
];
|
||
}
|
||
|
||
// 【JOB2】SQL-2: 重複チェック
|
||
// 補足: 受付番号は contract_payment_number(rno)で照合する
|
||
$existCount = DB::table('settlement_transaction')
|
||
->where('contract_payment_number', $contractPaymentNumber)
|
||
->count();
|
||
|
||
if ($existCount >= 1) {
|
||
Log::info('SHJ-4B SQL-2: 既に登録済み', [
|
||
'contract_payment_number' => $contractPaymentNumber,
|
||
]);
|
||
return [
|
||
'success' => true,
|
||
'result' => 'already_registered',
|
||
'message' => '決済トランザクションは既に登録済みです',
|
||
];
|
||
}
|
||
|
||
// 【SQL-3】決済トランザクション挿入(<JOB1>.定期契約ID を使用)
|
||
$settlementId = DB::table('settlement_transaction')->insertGetId([
|
||
'contract_id' => $contract->contract_id,
|
||
'status' => $wellnetData['status'] ?? null,
|
||
'pay_code' => $wellnetData['pay_code'] ?? null,
|
||
'contract_payment_number' => $contractPaymentNumber,
|
||
'corp_code' => $wellnetData['corp_code'] ?? null,
|
||
'mms_date' => $wellnetData['mms_date'] ?? null,
|
||
'cvs_code' => $wellnetData['cvs_code'] ?? null,
|
||
'shop_code' => $wellnetData['shop_code'] ?? null,
|
||
'pay_date' => $wellnetData['pay_date'] ?? null,
|
||
'settlement_amount' => $wellnetData['settlement_amount'] ?? null,
|
||
'stamp_flag' => $wellnetData['stamp_flag'] ?? null,
|
||
'md5_string' => $wellnetData['md5_string'] ?? null,
|
||
'created_at' => now(),
|
||
'updated_at' => now(),
|
||
]);
|
||
|
||
Log::info('SHJ-4B SQL-3: 決済トランザクション登録', [
|
||
'settlement_transaction_id' => $settlementId,
|
||
'contract_payment_number' => $contractPaymentNumber,
|
||
'contract_id' => $contract->contract_id,
|
||
]);
|
||
|
||
// JOB2-STEP2以降: 既存フローへ
|
||
return $this->processSettlementTransaction($settlementId);
|
||
}
|
||
|
||
/**
|
||
* 決済トランザクション取得
|
||
*
|
||
* @param int $settlementTransactionId
|
||
* @return SettlementTransaction
|
||
* @throws \RuntimeException
|
||
*/
|
||
private function getSettlementTransaction(int $settlementTransactionId): SettlementTransaction
|
||
{
|
||
$settlement = SettlementTransaction::find($settlementTransactionId);
|
||
|
||
if (!$settlement) {
|
||
throw new \RuntimeException("SettlementTransaction not found: {$settlementTransactionId}");
|
||
}
|
||
|
||
Log::info('SHJ-4B 決済トランザクション取得成功', [
|
||
'settlement_transaction_id' => $settlementTransactionId,
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'settlement_amount' => $settlement->settlement_amount,
|
||
'pay_date' => $settlement->pay_date,
|
||
]);
|
||
|
||
return $settlement;
|
||
}
|
||
|
||
/**
|
||
* 【処理1】定期契約マスタの対象レコード取得
|
||
* 【判断0】取得判定(登録済み判定を含む)
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @return array
|
||
*/
|
||
private function judgeTargetContract(SettlementTransaction $settlement): array
|
||
{
|
||
Log::info('SHJ-4B 処理1: 定期契約マスタの対象レコード取得開始', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
]);
|
||
|
||
// 仕様書要件のSQL構造に基づく対象レコード取得
|
||
// regular_contract T1 inner join park T2 inner join price_a T4
|
||
$contractQuery = DB::table('regular_contract as T1')
|
||
->select([
|
||
'T1.contract_id',
|
||
'T1.old_contract_id',
|
||
'T1.park_id',
|
||
'T1.user_id',
|
||
'T1.contract_flag',
|
||
'T1.billing_amount',
|
||
'T4.price_ptypeid as ptype_id',
|
||
'T1.psection_id',
|
||
'T1.zone_id',
|
||
'T1.update_flag',
|
||
'T1.reserve_id',
|
||
'T1.contract_payment_number',
|
||
'T1.contract_payment_day',
|
||
'T1.contract_periods',
|
||
'T1.contract_periode',
|
||
'T1.contract_created_at',
|
||
'T1.contract_cancel_flag',
|
||
'T2.park_name',
|
||
'T2.immediate_use_permit',
|
||
'T2.update_grace_period_start_date',
|
||
'T2.update_grace_period_end_date',
|
||
'T2.parking_start_grace_period',
|
||
'T4.price_month',
|
||
'T4.price'
|
||
])
|
||
->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
|
||
->join('price_a as T4', function($join) {
|
||
$join->on('T1.price_parkplaceid', '=', 'T4.price_parkplaceid')
|
||
->on('T1.park_id', '=', 'T4.park_id'); // 仕様書要件の第2条件
|
||
})
|
||
->where('T1.contract_payment_number', $settlement->contract_payment_number)
|
||
->where('T1.contract_cancel_flag', '!=', 1) // 解約されていない
|
||
->whereNotNull('T1.contract_flag') // 状態が設定済み
|
||
->first();
|
||
|
||
if (!$contractQuery) {
|
||
Log::warning('SHJ-4B 判断0: 対象レコードなし', [
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
]);
|
||
|
||
return [
|
||
'found' => false,
|
||
'contract' => null,
|
||
'reason' => '対象レコードなし',
|
||
'message' => "受付番号が不正です。(受付番号:{$settlement->contract_payment_number})",
|
||
];
|
||
}
|
||
|
||
// 登録済み判定
|
||
$isAlreadyProcessed = $this->checkAlreadyProcessed($contractQuery, $settlement);
|
||
|
||
if ($isAlreadyProcessed['processed']) {
|
||
Log::info('SHJ-4B 判断0: 登録済み検出', [
|
||
'contract_id' => $contractQuery->contract_id,
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'reason' => $isAlreadyProcessed['reason'],
|
||
]);
|
||
|
||
return [
|
||
'found' => true,
|
||
'contract' => $contractQuery,
|
||
'already_processed' => true,
|
||
'reason' => '登録済み',
|
||
'message' => "この決済は既に処理済みです: " . $isAlreadyProcessed['reason'],
|
||
];
|
||
}
|
||
|
||
Log::info('SHJ-4B 判断0: 対象契約取得成功', [
|
||
'contract_id' => $contractQuery->contract_id,
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'billing_amount' => $contractQuery->billing_amount,
|
||
'contract_flag' => $contractQuery->contract_flag,
|
||
'park_name' => $contractQuery->park_name,
|
||
'price_month' => $contractQuery->price_month,
|
||
]);
|
||
|
||
return [
|
||
'found' => true,
|
||
'contract' => $contractQuery,
|
||
'already_processed' => false,
|
||
'reason' => '対象契約取得成功',
|
||
'message' => "契約ID {$contractQuery->contract_id} を取得しました",
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 登録済み判定
|
||
*
|
||
* 複数の条件で既に処理済みかを判定
|
||
*
|
||
* @param object $contract
|
||
* @param SettlementTransaction $settlement
|
||
* @return array
|
||
*/
|
||
private function checkAlreadyProcessed($contract, SettlementTransaction $settlement): array
|
||
{
|
||
// 条件1: contract_payment_dayが既に設定済みで、今回の支払日以降
|
||
if (!empty($contract->contract_payment_day)) {
|
||
$existingPaymentDate = Carbon::parse($contract->contract_payment_day);
|
||
$currentPaymentDate = Carbon::parse($settlement->pay_date);
|
||
|
||
if ($existingPaymentDate->gte($currentPaymentDate)) {
|
||
return [
|
||
'processed' => true,
|
||
'reason' => "既に支払日 {$existingPaymentDate->format('Y-m-d')} が設定済み",
|
||
];
|
||
}
|
||
}
|
||
|
||
// 条件2: 同一の決済条件(contract_payment_number + pay_date + settlement_amount)が
|
||
// 既に他のsettlement_transactionで処理済み
|
||
$existingTransaction = SettlementTransaction::where('contract_payment_number', $settlement->contract_payment_number)
|
||
->where('pay_date', $settlement->pay_date)
|
||
->where('settlement_amount', $settlement->settlement_amount)
|
||
->where('settlement_transaction_id', '!=', $settlement->settlement_transaction_id)
|
||
->first();
|
||
|
||
if ($existingTransaction) {
|
||
return [
|
||
'processed' => true,
|
||
'reason' => "同一条件の決済トランザクション {$existingTransaction->settlement_transaction_id} が既に存在",
|
||
];
|
||
}
|
||
|
||
// 条件3: bat_job_logで同一決済の処理完了記録があるか
|
||
// status_commentに決済トランザクションIDが含まれているかチェック
|
||
$existingBatchLog = BatJobLog::where('process_name', 'SHJ-4B')
|
||
->where('status', 'success')
|
||
->where('status_comment', 'like', '%settlement_transaction_id:' . $settlement->settlement_transaction_id . '%')
|
||
->exists();
|
||
|
||
if ($existingBatchLog) {
|
||
return [
|
||
'processed' => true,
|
||
'reason' => "bat_job_logに処理完了記録が存在",
|
||
];
|
||
}
|
||
|
||
return [
|
||
'processed' => false,
|
||
'reason' => '未処理',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 【判断1】授受状態チェック
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param object $contract
|
||
* @return array
|
||
*/
|
||
private function judgeReceiptStatus(SettlementTransaction $settlement, $contract): array
|
||
{
|
||
// 仕様JOB2-STEP2: 授受フラグ = 0 の場合、処理続行
|
||
if ((int) $contract->contract_flag === 0) {
|
||
Log::info('SHJ-4B 判断1: 授受フラグ=0 処理続行', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contract->contract_id,
|
||
]);
|
||
|
||
return [
|
||
'valid' => true,
|
||
'reason' => '授受フラグ未処理',
|
||
'message' => '授受フラグチェックに合格しました',
|
||
];
|
||
}
|
||
|
||
// その他の場合(授受済み)
|
||
$message = sprintf(
|
||
'支払いステータスチェック:授受済みです。(定期契約ID:%s)',
|
||
$contract->contract_id
|
||
);
|
||
|
||
Log::warning('SHJ-4B 判断1: 授受済み', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contract->contract_id,
|
||
'contract_flag' => $contract->contract_flag,
|
||
]);
|
||
|
||
return [
|
||
'valid' => false,
|
||
'reason' => '授受済み',
|
||
'message' => $message,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 【判断2】金額チェック
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param object $contract
|
||
* @return array
|
||
*/
|
||
private function judgeAmountComparison(SettlementTransaction $settlement, $contract): array
|
||
{
|
||
// 仕様書要件: 請求額=授受額の厳密比較
|
||
$billingAmount = (int) $contract->billing_amount; // 整数として比較
|
||
$settlementAmount = (int) $settlement->settlement_amount; // 整数として比較
|
||
|
||
$difference = $settlementAmount - $billingAmount;
|
||
|
||
if ($difference === 0) {
|
||
$comparison = self::AMOUNT_MATCH;
|
||
$result = '正常(金額一致)';
|
||
} elseif ($difference < 0) {
|
||
$comparison = self::AMOUNT_SHORTAGE;
|
||
$result = '授受過少';
|
||
} else {
|
||
$comparison = self::AMOUNT_EXCESS;
|
||
$result = '授受超過';
|
||
}
|
||
|
||
Log::info('SHJ-4B 判断2: 金額チェック完了', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contract->contract_id,
|
||
'billing_amount' => $billingAmount,
|
||
'settlement_amount' => $settlementAmount,
|
||
'difference' => $difference,
|
||
'comparison' => $comparison,
|
||
'result' => $result,
|
||
]);
|
||
|
||
return [
|
||
'comparison' => $comparison,
|
||
'result' => $result,
|
||
'billing_amount' => $billingAmount,
|
||
'settlement_amount' => $settlementAmount,
|
||
'difference' => $difference,
|
||
'message' => "請求額: {$billingAmount}円, 授受額: {$settlementAmount}円, 結果: {$result}",
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 登録済み処理
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param array $contractResult
|
||
* @return array
|
||
*/
|
||
private function handleAlreadyProcessed(SettlementTransaction $settlement, array $contractResult): array
|
||
{
|
||
Log::info('SHJ-4B 登録済み処理', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contractResult['contract']->contract_id,
|
||
'reason' => $contractResult['reason'],
|
||
]);
|
||
|
||
return [
|
||
'success' => true,
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contractResult['contract']->contract_id,
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'result' => 'already_processed',
|
||
'reason' => $contractResult['reason'],
|
||
'message' => $contractResult['message'],
|
||
'skipped' => true,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 【SQL-4】シール印刷可能日を計算(仕様パターンA/B準拠)
|
||
*
|
||
* パターン判定:
|
||
* - 即利用許可=1 → 入金日の年月日
|
||
* - 更新期間開始日 <= 更新期間終了日(パターンA):
|
||
* - A-1: 駐輪開始猶予期間 > 本日の日 → 入金日の年月 + 猶予期間日
|
||
* - A-2: それ以外 → 入金日の年月日
|
||
* - 更新期間開始日 > 更新期間終了日(パターンB)→ 入金日の年月日
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param object $contract
|
||
* @return string Y-m-d形式の日付
|
||
*/
|
||
private function calculatePrintableDate(SettlementTransaction $settlement, $contract): string
|
||
{
|
||
$payDate = Carbon::parse($settlement->pay_date);
|
||
|
||
// 契約後即利用許可 = 1
|
||
if ((int) ($contract->immediate_use_permit ?? 0) === 1) {
|
||
return $payDate->format('Y-m-d');
|
||
}
|
||
|
||
$startDate = (int) ($contract->update_grace_period_start_date ?? 0);
|
||
$endDate = (int) ($contract->update_grace_period_end_date ?? 0);
|
||
$gracePeriod = (int) ($contract->parking_start_grace_period ?? 0);
|
||
$todayDay = (int) now()->format('d');
|
||
|
||
// パターンA: 更新期間開始日 <= 更新期間終了日
|
||
if ($startDate <= $endDate) {
|
||
if ($gracePeriod > $todayDay) {
|
||
// A-1: 入金日の年月 + 猶予期間日
|
||
return $payDate->format('Y-m') . '-' . str_pad($gracePeriod, 2, '0', STR_PAD_LEFT);
|
||
}
|
||
// A-2: 入金日の年月日
|
||
return $payDate->format('Y-m-d');
|
||
}
|
||
|
||
// パターンB: 全サブケース同一 → 入金日の年月日
|
||
return $payDate->format('Y-m-d');
|
||
}
|
||
|
||
/**
|
||
* 新規契約判定
|
||
*
|
||
* @param object $contract
|
||
* @return bool
|
||
*/
|
||
private function isNewContract($contract): bool
|
||
{
|
||
if (empty($contract->contract_created_at)) {
|
||
return false;
|
||
}
|
||
|
||
$createdAt = Carbon::parse($contract->contract_created_at);
|
||
$thirtyDaysAgo = Carbon::now()->subDays(30);
|
||
|
||
// 作成から30日以内を新規とみなす(調整可能)
|
||
$isNew = $createdAt->gte($thirtyDaysAgo);
|
||
|
||
Log::info('SHJ-4B 新規契約判定', [
|
||
'contract_id' => $contract->contract_id,
|
||
'contract_created_at' => $createdAt->format('Y-m-d H:i:s'),
|
||
'is_new' => $isNew,
|
||
'days_since_created' => $createdAt->diffInDays(Carbon::now()),
|
||
]);
|
||
|
||
return $isNew;
|
||
}
|
||
|
||
/**
|
||
* 【JOB3-1】決済授受および写真削除 + 定期契約マスタ、定期予約マスタ更新
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param object $contract
|
||
* @param array|null $shjFourCResult SHJ-4C車室割り当て結果(新規のみ)
|
||
* @return array
|
||
*/
|
||
private function executeContractUpdate(
|
||
SettlementTransaction $settlement,
|
||
$contract,
|
||
?array $shjFourCResult = null
|
||
): array {
|
||
$updateData = [];
|
||
$updated = false;
|
||
|
||
try {
|
||
DB::transaction(function() use ($settlement, $contract, $shjFourCResult, &$updateData, &$updated) {
|
||
// 【SQL-4】基本更新項目(仕様準拠)
|
||
$updateData = [
|
||
'contract_payment_day' => Carbon::parse($settlement->pay_date)->format('Y-m-d H:i:s'),
|
||
'contract_money' => $settlement->settlement_amount,
|
||
'contact_shop_code' => $settlement->shop_code,
|
||
'contract_cvs_class' => $settlement->cvs_code,
|
||
'contract_flag' => 1,
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_permission' => 1,
|
||
'printable_date' => $this->calculatePrintableDate($settlement, $contract),
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'updated_at' => now(),
|
||
];
|
||
|
||
// 【新規】SHJ-4Cの結果を反映(仕様SQL-4準拠)
|
||
if ((int) $contract->update_flag === 2 && $shjFourCResult
|
||
&& ($shjFourCResult['success'] ?? false)
|
||
&& !empty($shjFourCResult['zone_id']) && !empty($shjFourCResult['pplace_no'])) {
|
||
$updateData['zone_id'] = $shjFourCResult['zone_id'];
|
||
$updateData['pplace_no'] = $shjFourCResult['pplace_no'];
|
||
}
|
||
|
||
// 【SQL-4】定期契約マスタ更新
|
||
$affectedRows = DB::table('regular_contract')
|
||
->where('contract_id', $contract->contract_id)
|
||
->update($updateData);
|
||
|
||
$updated = $affectedRows > 0;
|
||
|
||
// 【SQL-5】契約元の定期契約マスタを更新(旧契約の更新済フラグ)
|
||
if (!empty($contract->old_contract_id)) {
|
||
DB::table('regular_contract')
|
||
->where('contract_id', $contract->old_contract_id)
|
||
->update(['contract_renewal' => 1, 'updated_at' => now()]);
|
||
|
||
Log::info('SHJ-4B SQL-5 旧契約更新済フラグ更新', [
|
||
'old_contract_id' => $contract->old_contract_id,
|
||
'contract_id' => $contract->contract_id,
|
||
]);
|
||
}
|
||
|
||
// 【SQL-8】定期予約マスタ更新(reserve_idがnull以外の場合)
|
||
if (!empty($contract->reserve_id)) {
|
||
DB::table('reserve')
|
||
->where('reserve_id', $contract->reserve_id)
|
||
->update([
|
||
'contract_id' => $contract->contract_id,
|
||
'contract_created_at' => now(),
|
||
'valid_flag' => 0,
|
||
'updated_at' => now(),
|
||
]);
|
||
|
||
Log::info('SHJ-4B 定期予約マスタ更新完了', [
|
||
'reserve_id' => $contract->reserve_id,
|
||
'contract_id' => $contract->contract_id,
|
||
]);
|
||
}
|
||
|
||
Log::info('SHJ-4B 定期契約マスタ更新完了', [
|
||
'contract_id' => $contract->contract_id,
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'update_data' => $updateData,
|
||
'affected_rows' => $affectedRows,
|
||
]);
|
||
});
|
||
|
||
return [
|
||
'updated' => $updated,
|
||
'update_data' => $updateData,
|
||
'message' => $updated ? '契約更新に成功しました' : '契約更新対象が見つかりませんでした',
|
||
];
|
||
|
||
} catch (\Throwable $e) {
|
||
Log::error('SHJ-4B 契約更新処理失敗', [
|
||
'contract_id' => $contract->contract_id,
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'error' => $e->getMessage(),
|
||
]);
|
||
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 副作用処理実行
|
||
*
|
||
* 決済授受および写真削除、新規連動等の処理
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param object $contract
|
||
* @param array $amountResult
|
||
* @param array $updateResult
|
||
* @param array|null $shjFourCResult SHJ-4C車室割り当て結果(新規のみ)
|
||
* @return array
|
||
*/
|
||
private function executeSideEffects(
|
||
SettlementTransaction $settlement,
|
||
$contract,
|
||
array $amountResult,
|
||
array $updateResult,
|
||
?array $shjFourCResult = null
|
||
): array {
|
||
$sideEffects = [];
|
||
|
||
try {
|
||
// 【JOB3-2】写真削除処理(SQL-4/SQL-5の後、金額比較結果に関わらず実行)
|
||
$sideEffects['photo_deletion'] = $this->executePhotoDeletion($contract);
|
||
|
||
// 【JOB3-3】写真削除バッチログ(独立SHJ-8呼出し)
|
||
$this->createPhotoDeletionBatchLog($sideEffects['photo_deletion']);
|
||
|
||
// 【JOB3-STEP1】SHJ-13契約台数追加(新規 かつ shortage以外)
|
||
// 仕様:shortage → SHJ-13呼出なし、match/excess → SHJ-13呼出
|
||
if ((int) $contract->update_flag === 2
|
||
&& $amountResult['comparison'] !== self::AMOUNT_SHORTAGE) {
|
||
$sideEffects['shj13_trigger'] = $this->triggerShjThirteen($contract, $shjFourCResult);
|
||
}
|
||
|
||
// 【処理4】異常時のオペレーターキュー登録処理
|
||
if ($amountResult['comparison'] !== self::AMOUNT_MATCH) {
|
||
$sideEffects['operator_queue'] = $this->registerToOperatorQueue($settlement, $contract, $amountResult);
|
||
}
|
||
|
||
// 【JOB5】利用者メール送信処理(仕様: フロー到達時は一律送信)
|
||
$sideEffects['user_mail'] = $this->sendUserNotificationMail($settlement, $contract, $amountResult);
|
||
|
||
Log::info('SHJ-4B 副作用処理完了', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contract->contract_id,
|
||
'side_effects' => array_keys($sideEffects),
|
||
]);
|
||
|
||
return $sideEffects;
|
||
|
||
} catch (\Throwable $e) {
|
||
Log::error('SHJ-4B 副作用処理失敗', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contract->contract_id,
|
||
'error' => $e->getMessage(),
|
||
]);
|
||
|
||
// 副作用処理の失敗はメイン処理を止めない
|
||
return ['error' => $e->getMessage()];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 対象レコードなしの場合の処理
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param array $contractResult
|
||
* @return array
|
||
*/
|
||
private function handleNoTargetRecord(SettlementTransaction $settlement, array $contractResult): array
|
||
{
|
||
Log::warning('SHJ-4B 対象レコードなし処理', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'reason' => $contractResult['reason'],
|
||
]);
|
||
|
||
// TODO: 必要に応じて管理者通知やオペレーターキューへの登録
|
||
|
||
return [
|
||
'success' => true, // エラーではなく、正常な結果として扱う
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_payment_number' => $settlement->contract_payment_number,
|
||
'result' => 'no_target',
|
||
'reason' => $contractResult['reason'],
|
||
'message' => $contractResult['message'],
|
||
'action_required' => '管理者による手動確認が必要です',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 授受状態異常の場合の処理
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param RegularContract $contract
|
||
* @param array $statusResult
|
||
* @return array
|
||
*/
|
||
private function handleInvalidStatus(
|
||
SettlementTransaction $settlement,
|
||
$contract,
|
||
array $statusResult
|
||
): array {
|
||
Log::warning('SHJ-4B 授受状態異常処理', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contract->contract_id,
|
||
'reason' => $statusResult['reason'],
|
||
]);
|
||
|
||
return [
|
||
'success' => true, // 授受済みは仕様上エラーではなく正常分岐
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'contract_id' => $contract->contract_id,
|
||
'result' => 'already_received',
|
||
'reason' => $statusResult['reason'],
|
||
'message' => $statusResult['message'],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 写真削除処理
|
||
*
|
||
* @param object $contract
|
||
* @return array
|
||
*/
|
||
private function executePhotoDeletion($contract): array
|
||
{
|
||
// 【SQL-6】利用者の写真ファイル名取得
|
||
$userData = DB::table('regular_contract as T1')
|
||
->join('user as T2', 'T1.user_id', '=', 'T2.user_seq')
|
||
->where('T1.contract_payment_number', $contract->contract_payment_number)
|
||
->where('T1.contract_flag', 1)
|
||
->select('T2.user_id', 'T2.user_seq', 'T2.user_name', 'T2.photo_filename1', 'T2.photo_filename2')
|
||
->first();
|
||
|
||
if (!$userData) {
|
||
Log::info('SHJ-4B 写真削除: 対象利用者なし', [
|
||
'contract_id' => $contract->contract_id,
|
||
]);
|
||
return ['executed' => false, 'message' => '対象利用者なし'];
|
||
}
|
||
|
||
$deletedFiles = [];
|
||
|
||
// ファイル削除
|
||
foreach (['photo_filename1', 'photo_filename2'] as $field) {
|
||
if (!empty($userData->$field)) {
|
||
$path = 'photo/' . $userData->$field;
|
||
if (Storage::disk('public')->exists($path)) {
|
||
Storage::disk('public')->delete($path);
|
||
$deletedFiles[] = $userData->$field;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 【SQL-7】利用者の写真ファイル名をNULLに更新
|
||
$userUpdateQuery = DB::table('user');
|
||
if (!empty($userData->user_id)) {
|
||
// 仕様書上の利用者ID(user.user_id)で更新
|
||
$userUpdateQuery->where('user_id', $userData->user_id);
|
||
} else {
|
||
// user_id未設定データの後方互換
|
||
$userUpdateQuery->where('user_seq', $userData->user_seq);
|
||
}
|
||
$userUpdateQuery->update([
|
||
'photo_filename1' => null,
|
||
'photo_filename2' => null,
|
||
'updated_at' => now(),
|
||
]);
|
||
|
||
Log::info('SHJ-4B 写真削除処理完了', [
|
||
'user_seq' => $userData->user_seq,
|
||
'deleted_files' => $deletedFiles,
|
||
]);
|
||
|
||
return [
|
||
'executed' => true,
|
||
'user_id' => $userData->user_id,
|
||
'user_seq' => $userData->user_seq,
|
||
'user_name' => $userData->user_name,
|
||
'photo_filename1' => $userData->photo_filename1,
|
||
'photo_filename2' => $userData->photo_filename2,
|
||
'deleted_files' => $deletedFiles,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* ウェルネットPUSH処理用バッチログ作成(settlement_transaction未作成時)
|
||
*
|
||
* processWellnetPush内でJOB1不合格(受付番号不正)の場合に使用
|
||
*
|
||
* @param string $statusComment ステータスコメント
|
||
* @return void
|
||
*/
|
||
private function createWellnetPushBatchLog(string $statusComment): void
|
||
{
|
||
try {
|
||
$device = Device::orderBy('device_id')->first();
|
||
$deviceId = $device ? $device->device_id : 1;
|
||
$today = now()->format('Y/m/d');
|
||
|
||
$this->shjEightService->execute(
|
||
$deviceId,
|
||
'SHJ-4B',
|
||
'SHJ-4支払いステータスチェック',
|
||
'success',
|
||
$statusComment,
|
||
$today,
|
||
$today
|
||
);
|
||
} catch (\Exception $e) {
|
||
Log::error('SHJ-4B ウェルネットPUSHバッチログ作成エラー', [
|
||
'error' => $e->getMessage(),
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 【JOB3-3】写真削除バッチログ作成(独立SHJ-8呼出し)
|
||
*
|
||
* @param array $photoDeletionResult executePhotoDeletion()の戻り値
|
||
* @return void
|
||
*/
|
||
private function createPhotoDeletionBatchLog(array $photoDeletionResult): void
|
||
{
|
||
try {
|
||
$device = Device::orderBy('device_id')->first();
|
||
$deviceId = $device ? $device->device_id : 1;
|
||
$today = now()->format('Y/m/d');
|
||
|
||
if ($photoDeletionResult['executed'] ?? false) {
|
||
$status = 'success';
|
||
$userId = $photoDeletionResult['user_id'] ?? '';
|
||
$userName = $photoDeletionResult['user_name'] ?? '';
|
||
$file1 = $photoDeletionResult['photo_filename1'] ?? '';
|
||
$file2 = $photoDeletionResult['photo_filename2'] ?? '';
|
||
$statusComment = "利用者ID:{$userId}、利用者名:{$userName}、ファイル名:{$file1},{$file2}";
|
||
} else {
|
||
$status = 'error';
|
||
$statusComment = $photoDeletionResult['message'] ?? '写真削除対象なし';
|
||
}
|
||
|
||
$this->shjEightService->execute(
|
||
$deviceId,
|
||
'SHJ-4B',
|
||
'SHJ-4B本人確認写真削除',
|
||
$status,
|
||
$statusComment,
|
||
$today,
|
||
$today
|
||
);
|
||
} catch (\Exception $e) {
|
||
Log::error('SHJ-4B JOB3-3 写真削除バッチログ作成エラー', [
|
||
'error' => $e->getMessage(),
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 【JOB3-0】SHJ-4C 車室割り当て処理(新規のみ)
|
||
*
|
||
* 仕様:定期契約継続フラグ = 2(新規)の場合に呼び出す
|
||
* ※異常があったとしても、処理は継続する
|
||
*
|
||
* @param object $contract 契約データ
|
||
* @return array|null 処理結果(異常時はnull)
|
||
*/
|
||
private function triggerShjFourC($contract): ?array
|
||
{
|
||
Log::info('SHJ-4B SHJ-4C車室割り当て処理実行', [
|
||
'contract_id' => $contract->contract_id,
|
||
'park_id' => $contract->park_id,
|
||
'ptype_id' => $contract->ptype_id,
|
||
'psection_id' => $contract->psection_id,
|
||
]);
|
||
|
||
try {
|
||
// SHJ-4Cサービス実行
|
||
$result = $this->shjFourCService->executeRoomAllocation(
|
||
(int) $contract->park_id,
|
||
(int) $contract->ptype_id,
|
||
(int) $contract->psection_id
|
||
);
|
||
|
||
Log::info('SHJ-4B SHJ-4C車室割り当て処理完了', [
|
||
'contract_id' => $contract->contract_id,
|
||
'result' => $result,
|
||
]);
|
||
|
||
return $result;
|
||
|
||
} catch (\Throwable $e) {
|
||
// ※異常があったとしても、処理は継続する
|
||
Log::error('SHJ-4B SHJ-4C車室割り当て処理エラー', [
|
||
'contract_id' => $contract->contract_id,
|
||
'error' => $e->getMessage(),
|
||
]);
|
||
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* SHJ-13実行処理(新規のみ)
|
||
*
|
||
* ShjThirteenServiceを使用した契約台数追加処理
|
||
* 仕様:パラメーター4 ゾーンID = 【SHJ-4C.ゾーンID】
|
||
*
|
||
* @param object $contract
|
||
* @param array|null $shjFourCResult SHJ-4C車室割り当て結果
|
||
* @return array
|
||
*/
|
||
private function triggerShjThirteen($contract, ?array $shjFourCResult = null): array
|
||
{
|
||
// 仕様準拠:ゾーンID = SHJ-4C.ゾーンID
|
||
$zoneId = ($shjFourCResult && !empty($shjFourCResult['zone_id']))
|
||
? $shjFourCResult['zone_id']
|
||
: $contract->zone_id;
|
||
|
||
Log::info('SHJ-4B SHJ-13実行処理', [
|
||
'contract_id' => $contract->contract_id,
|
||
'park_id' => $contract->park_id,
|
||
'psection_id' => $contract->psection_id,
|
||
'ptype_id' => $contract->ptype_id,
|
||
'zone_id' => $zoneId,
|
||
]);
|
||
|
||
try {
|
||
// 契約データ準備(仕様:パラメーター4 ゾーンID = SHJ-4C.ゾーンID)
|
||
$contractData = [
|
||
'contract_id' => $contract->contract_id,
|
||
'park_id' => $contract->park_id,
|
||
'psection_id' => $contract->psection_id,
|
||
'ptype_id' => $contract->ptype_id,
|
||
'zone_id' => $zoneId,
|
||
];
|
||
|
||
// 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 [
|
||
'result' => 1,
|
||
'error_code' => $e->getCode() ?: 1999,
|
||
'error_message' => $e->getMessage(),
|
||
'stack_trace' => $e->getTraceAsString(),
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 【処理5】利用者メール送信処理
|
||
*
|
||
* SHJ-4B仕様準拠:
|
||
* 1. 利用者マスタよりメールアドレス、予備メールアドレスを取得
|
||
* 2. SHJ-7メール送信を呼び出し(使用プログラムID: 205)
|
||
* 3. 処理結果判定:
|
||
* - result = 0 (正常): バッチコメントに "/メール正常終了件数:1" を追加
|
||
* - その他: バッチコメントに "/メール異常終了件数:1、" + error_info を追加
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param object $contract
|
||
* @param array $amountResult
|
||
* @return array 処理結果 ['success' => bool, 'mail_status' => string, 'batch_comment_suffix' => string]
|
||
*/
|
||
private function sendUserNotificationMail(SettlementTransaction $settlement, $contract, ?array $amountResult = null): array
|
||
{
|
||
try {
|
||
// 【処理5】利用者マスタよりメールアドレス、予備メールアドレスを取得する
|
||
$user = User::select('user_name', 'user_primemail', 'user_submail')
|
||
->where('user_seq', $contract->user_id)
|
||
->first();
|
||
|
||
if (!$user) {
|
||
Log::error('SHJ-4B 利用者メール送信処理: 利用者情報取得失敗', [
|
||
'user_id' => $contract->user_id,
|
||
'contract_id' => $contract->contract_id,
|
||
]);
|
||
|
||
return [
|
||
'success' => false,
|
||
'mail_status' => 'user_not_found',
|
||
'batch_comment_suffix' => '/メール異常終了件数:1、利用者情報取得失敗'
|
||
];
|
||
}
|
||
|
||
$mailAddress = $user->user_primemail ?? '';
|
||
$backupMailAddress = $user->user_submail ?? '';
|
||
|
||
Log::info('SHJ-4B 利用者メール送信処理開始', [
|
||
'contract_id' => $contract->contract_id,
|
||
'user_id' => $contract->user_id,
|
||
'user_name' => $user->user_name,
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'mail_address' => $mailAddress,
|
||
'amount_comparison' => $amountResult['comparison'] ?? 'already_received',
|
||
]);
|
||
|
||
// 共通処理「SHJ-7メール送信」を呼び出し
|
||
// 使用プログラムID: 205(仕様書準拠)
|
||
$mailResult = $this->mailSendService->executeMailSend(
|
||
$mailAddress,
|
||
$backupMailAddress,
|
||
205 // SHJ-4B仕様: 使用プログラムID = 205
|
||
);
|
||
|
||
// SHJ-7仕様準拠: result === 0 が正常、それ以外は異常
|
||
if (($mailResult['result'] ?? 1) === 0) {
|
||
// 【正常終了】バッチコメントに "/メール正常終了件数:1" を設定
|
||
Log::info('SHJ-4B 利用者メール送信成功', [
|
||
'contract_id' => $contract->contract_id,
|
||
'user_id' => $contract->user_id,
|
||
'mail_address' => $mailAddress,
|
||
]);
|
||
|
||
return [
|
||
'success' => true,
|
||
'mail_status' => 'sent',
|
||
'batch_comment_suffix' => '/メール正常終了件数:1'
|
||
];
|
||
} else {
|
||
// 【異常終了】バッチコメントに "/メール異常終了件数:1、" + error_info を設定
|
||
$errorInfo = $mailResult['error_info'] ?? 'メール送信失敗';
|
||
|
||
Log::error('SHJ-4B 利用者メール送信失敗', [
|
||
'contract_id' => $contract->contract_id,
|
||
'user_id' => $contract->user_id,
|
||
'mail_address' => $mailAddress,
|
||
'error_info' => $errorInfo,
|
||
]);
|
||
|
||
return [
|
||
'success' => false,
|
||
'mail_status' => 'failed',
|
||
'batch_comment_suffix' => "/メール異常終了件数:1、{$errorInfo}"
|
||
];
|
||
}
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('SHJ-4B 利用者メール送信処理例外エラー', [
|
||
'contract_id' => $contract->contract_id,
|
||
'user_id' => $contract->user_id,
|
||
'error' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString(),
|
||
]);
|
||
|
||
return [
|
||
'success' => false,
|
||
'mail_status' => 'exception',
|
||
'batch_comment_suffix' => '/メール異常終了件数:1、システムエラー: ' . $e->getMessage()
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* オペレーターキューへの登録
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param object $contract
|
||
* @param array $amountResult
|
||
* @return array
|
||
*/
|
||
private function registerToOperatorQueue(
|
||
SettlementTransaction $settlement,
|
||
$contract,
|
||
array $amountResult
|
||
): array {
|
||
// 【SQL-9】キュー種別: shortage=8(支払い催促), excess=6(返金処理)
|
||
$queClass = $amountResult['comparison'] === self::AMOUNT_SHORTAGE ? 8 : 6;
|
||
|
||
$queId = DB::table('operator_que')->insertGetId([
|
||
'que_class' => $queClass,
|
||
'user_id' => $contract->user_id,
|
||
'contract_id' => $contract->contract_id,
|
||
'park_id' => $contract->park_id,
|
||
'que_comment' => '収納請求金額照合エラー',
|
||
'que_status' => 1,
|
||
'que_status_comment' => '',
|
||
'work_instructions' => '',
|
||
'operator_id' => 'SHJ-4',
|
||
'created_at' => now(),
|
||
'updated_at' => now(),
|
||
]);
|
||
|
||
Log::info('SHJ-4B オペレーターキュー登録完了', [
|
||
'que_id' => $queId,
|
||
'que_class' => $queClass,
|
||
'contract_id' => $contract->contract_id,
|
||
]);
|
||
|
||
return [
|
||
'registered' => true,
|
||
'que_id' => $queId,
|
||
'que_class' => $queClass,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 【処理6】バッチ処理ログ作成
|
||
*
|
||
* SHJ-8サービスを呼び出してbat_job_logに記録
|
||
*
|
||
* @param SettlementTransaction $settlement
|
||
* @param object|null $contract
|
||
* @param array|null $amountResult
|
||
* @param bool $isSuccess
|
||
* @param string|null $errorMessage
|
||
* @param string|null $mailCommentSuffix メール送信結果コメント
|
||
* @param array|null $photoDeletionResult 写真削除結果(SQL-6情報含む)
|
||
* @return void
|
||
*/
|
||
private function createBatchLog(
|
||
SettlementTransaction $settlement,
|
||
$contract = null,
|
||
?array $amountResult = null,
|
||
bool $isSuccess = true,
|
||
?string $errorMessage = null,
|
||
?string $mailCommentSuffix = null,
|
||
?array $photoDeletionResult = null,
|
||
?string $statusCommentOverride = null
|
||
): void {
|
||
try {
|
||
$device = Device::orderBy('device_id')->first();
|
||
$deviceId = $device ? $device->device_id : 1;
|
||
$today = now()->format('Y/m/d');
|
||
|
||
// ステータスコメント生成(内部変数.バッチコメント)
|
||
if ($statusCommentOverride) {
|
||
// 直接指定(授受済み等)
|
||
$statusComment = $statusCommentOverride;
|
||
} elseif ($errorMessage) {
|
||
// エラー時
|
||
$statusComment = "支払いステータスチェック:エラー(決済トランザクションID:{$settlement->settlement_transaction_id}) - {$errorMessage}";
|
||
} elseif ($amountResult) {
|
||
// 正常処理時
|
||
switch ($amountResult['comparison']) {
|
||
case self::AMOUNT_MATCH:
|
||
// 仕様: OK + 利用者情報 + 写真ファイル名
|
||
$userId = $photoDeletionResult['user_id'] ?? '';
|
||
$userName = $photoDeletionResult['user_name'] ?? '';
|
||
$file1 = $photoDeletionResult['photo_filename1'] ?? '';
|
||
$file2 = $photoDeletionResult['photo_filename2'] ?? '';
|
||
$statusComment = "支払いステータスチェック:OK、(決済トランザクションID:{$settlement->settlement_transaction_id}、利用者ID:{$userId}、利用者名:{$userName}、ファイル名:{$file1},{$file2})";
|
||
break;
|
||
case self::AMOUNT_SHORTAGE:
|
||
$statusComment = "支払いステータスチェック:請求金額より授受金額が少ないです。(決済トランザクションID:{$settlement->settlement_transaction_id})";
|
||
break;
|
||
case self::AMOUNT_EXCESS:
|
||
$statusComment = "支払いステータスチェック:請求金額より授受金額が多いです。(決済トランザクションID:{$settlement->settlement_transaction_id})";
|
||
break;
|
||
default:
|
||
$statusComment = "支払いステータスチェック:処理完了(決済トランザクションID:{$settlement->settlement_transaction_id})";
|
||
}
|
||
} else {
|
||
// その他のケース(対象なし、登録済み等)
|
||
$statusComment = "支払いステータスチェック:処理完了(決済トランザクションID:{$settlement->settlement_transaction_id})";
|
||
}
|
||
|
||
// メール送信結果をバッチコメントに追加
|
||
if ($mailCommentSuffix) {
|
||
$statusComment .= $mailCommentSuffix;
|
||
}
|
||
|
||
// 重複判定用マーカー追加(checkAlreadyProcessed/ShjFourBCheckCommand互換)
|
||
$statusComment .= " settlement_transaction_id:{$settlement->settlement_transaction_id}";
|
||
|
||
// SHJ-8サービス呼び出し(仕様JOB6: success/error動的判定)
|
||
$this->shjEightService->execute(
|
||
$deviceId,
|
||
'SHJ-4B',
|
||
'SHJ-4支払いステータスチェック',
|
||
$isSuccess ? 'success' : 'error',
|
||
$statusComment,
|
||
$today,
|
||
$today
|
||
);
|
||
|
||
Log::info('SHJ-4B バッチ処理ログ作成完了', [
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
'status_comment' => $statusComment,
|
||
]);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('SHJ-4B バッチ処理ログ作成エラー', [
|
||
'error' => $e->getMessage(),
|
||
'settlement_transaction_id' => $settlement->settlement_transaction_id,
|
||
]);
|
||
}
|
||
}
|
||
}
|