api.so-manager-dev.com/app/Services/ShjNineService.php
unhi.go e1073e2577
All checks were successful
Deploy api / deploy (push) Successful in 24s
SH-6 SHJ-9 実装
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 20:16:47 +08:00

904 lines
35 KiB
PHP
Raw 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 App\Models\Park;
use App\Models\EarningsSummary;
use App\Models\Psection;
use App\Models\OperatorQue;
use App\Models\Device;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
/**
* SHJ-9 売上集計処理サービス
*
* 日次の売上集計処理を実行するビジネスロジック
* バッチ処理「SHJ-9売上集計日次」の核となる処理を担当
*/
class ShjNineService
{
/**
* バッチ実行者の固定オペレータID
*/
const BATCH_OPERATOR_ID = 9999999;
/**
* 日次集計の summary_type 値
*/
const SUMMARY_TYPE_DAILY = 3;
/**
* 情報不備なしの固定文字列(全角スペース付き)
*/
const DATA_INTEGRITY_NONE = ' 情報不備:なし';
/**
* ShjEightService インスタンス
*
* @var ShjEightService
*/
protected $shjEightService;
/**
* コンストラクタ
*
* @param ShjEightService $shjEightService
*/
public function __construct(ShjEightService $shjEightService)
{
$this->shjEightService = $shjEightService;
}
/**
* SHJ-9 売上集計処理メイン実行(日次のみ)
*
* 処理フロー (todo/SHJ-9/SHJ-9.txt):
* 【処理1】集計対象を設定する
* 【処理2】駐輪場マスタを取得する
* 【判断1】取得件数判定
* 【処理3】車種区分毎に算出する
* 【判断2】取得判定
* 【処理4】売上集計結果を削除→登録する
* 【処理5】オペレータキュー作成およびバッチ処理ログを作成する
*
* @param string $type 集計種別daily 固定)
* @param string $aggregationDate 集計対象日YYYY-MM-DD
* @return array 処理結果
*/
public function executeEarningsAggregation(string $type, string $aggregationDate): array
{
$statusComments = []; // 内部変数.ステータスコメント
$allDataIntegrity = []; // 内部変数.情報不備(なし含む全件)
$dataIntegrityIssues = []; // オペレータキュー作成用NULL以外
try {
// 【処理1】集計対象を設定する
// パラメーター検証(日付形式チェック)
if (!$this->isValidDateFormat($aggregationDate)) {
// 日付形式エラー時は【処理】へ仕様JOB1ステータスコメント設定→JOB5
$statusComment = "売上集計(日次):パラメーターが不正です。(日付形式ではありません)";
// 仕様JOB5情報不備がNULL→オペレータキュー作成しない日付エラーは情報不備ではない
// SHJ-8 バッチ処理ログ作成仕様JOB5ステータスは常にsuccess
$shjEightResult = $this->callShjEight('SHJ-9売上集計日次', 'success', $statusComment);
$this->evaluateShjEightResult($shjEightResult);
return [
'success' => true, // 仕様上はwarningで成功扱い
'message' => $statusComment,
'processed_parks' => 0,
'summary_records' => 0
];
}
$targetDate = Carbon::parse($aggregationDate)->format('Y-m-d');
Log::info('SHJ-9 売上集計処理開始', [
'type' => $type,
'target_date' => $targetDate
]);
// 【処理2】駐輪場マスタを取得する
$parkInfo = $this->getParkInformation();
// 【判断1】取得件数判定
if (empty($parkInfo)) {
$statusComment = '売上集計(日次):駐輪場マスタが存在していません。';
// 仕様JOB2-STEP1情報不備がNULL→オペレータキュー作成しない
// SHJ-8 バッチ処理ログ作成仕様JOB5ステータスは常にsuccess
$shjEightResult = $this->callShjEight('SHJ-9売上集計日次', 'success', $statusComment);
$this->evaluateShjEightResult($shjEightResult);
return [
'success' => true,
'message' => $statusComment,
'processed_parks' => 0,
'summary_records' => 0
];
}
// 仕様JOB3/JOB4車種区分毎に算出→売上集計結果を登録
$summaryRecords = 0;
$processedParks = 0;
foreach ($parkInfo as $park) {
$result = $this->processEarningsForPark($park, $targetDate);
$processedParks++;
$summaryRecords += $result['summary_records'];
// 仕様JOB4レコード毎のステータスコメントを収集
if (!empty($result['status_comments'])) {
$statusComments = array_merge($statusComments, $result['status_comments']);
}
// 仕様JOB3-STEP1対象データなしの場合のステータスコメント
if (!empty($result['no_data_message'])) {
$statusComments[] = $result['no_data_message'];
}
// 仕様JOB5全parkの情報不備を収集なし含む
$allDataIntegrity[] = $result['data_integrity_issue'];
// 仕様JOB5NULL以外の場合にオペレータキューを登録
if ($result['data_integrity_issue'] !== null) {
$dataIntegrityIssues[] = [
'park_id' => $park->park_id,
'message' => $result['data_integrity_issue']
];
}
}
// 仕様JOB5情報不備がNULL以外の場合、オペレータキューを登録
foreach ($dataIntegrityIssues as $issue) {
$this->createOperatorQueue($issue['message'], $issue['park_id']);
}
if (!empty($dataIntegrityIssues)) {
Log::warning('SHJ-9 情報不備検出', [
'issues' => $dataIntegrityIssues
]);
}
// 仕様JOB5ステータスコメント = 内部変数.ステータスコメント + 内部変数.情報不備(なし含む全件)
$allParts = array_merge($statusComments, $allDataIntegrity);
$finalStatusComment = implode("\n", $allParts);
Log::info('SHJ-9 完全ステータスコメント', ['status_comment' => $finalStatusComment]);
// 仕様JOB5SHJ-8 バッチ処理ログ作成ステータスは常にsuccess
$shjEightResult = $this->callShjEight('SHJ-9売上集計日次', 'success', $finalStatusComment);
$this->evaluateShjEightResult($shjEightResult);
Log::info('SHJ-9 売上集計処理完了', [
'processed_parks' => $processedParks,
'summary_records' => $summaryRecords,
'data_integrity_issues' => count($dataIntegrityIssues)
]);
return [
'success' => true,
'message' => 'SHJ-9 売上集計処理が正常に完了しました',
'processed_parks' => $processedParks,
'summary_records' => $summaryRecords,
'data_integrity_issues' => count($dataIntegrityIssues)
];
} catch (\Exception $e) {
$errorMessage = '売上集計(日次):エラー発生 - ' . $e->getMessage();
// SHJ-8 バッチ処理ログ作成(エラー時も作成)
try {
$shjEightResult = $this->callShjEight('SHJ-9売上集計日次', 'success', $errorMessage);
$this->evaluateShjEightResult($shjEightResult);
} catch (\Exception $shjException) {
Log::error('SHJ-8呼び出しエラー', ['error' => $shjException->getMessage()]);
}
Log::error('SHJ-9 売上集計処理エラー', [
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => $errorMessage,
'details' => $e->getMessage()
];
}
}
/**
* 日付形式の検証厳格YYYY-MM-DD形式のみ許可
*
* @param string $date 日付文字列
* @return bool 有効な日付形式かどうか
*/
private function isValidDateFormat(string $date): bool
{
// YYYY-MM-DD形式の正規表現チェック
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
return false;
}
// 実際の日付として有効かチェック
$dateParts = explode('-', $date);
return checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0]);
}
/**
* 【処理2】駐輪場マスタを取得する
*
* @return array 駐輪場情報
*/
private function getParkInformation(): array
{
try {
$parkInfo = DB::table('park')
->select(['park_id', 'park_name'])
->where('park_close_flag', '<>', 1)
->orderBy('park_ruby')
->get()
->toArray();
Log::info('駐輪場マスタ取得完了', [
'park_count' => count($parkInfo)
]);
return $parkInfo;
} catch (\Exception $e) {
Log::error('駐輪場マスタ取得エラー', [
'error' => $e->getMessage()
]);
throw $e;
}
}
/**
* 駐輪場毎の売上集計処理
*
* @param object $park 駐輪場情報
* @param string $targetDate 集計対象日YYYY-MM-DD
* @return array 処理結果
*/
private function processEarningsForPark($park, string $targetDate): array
{
try {
// 仕様JOB3-0情報不備チェックSQL-2
$dataIntegrityIssue = $this->checkDataIntegrity($park->park_id, $targetDate);
// 仕様JOB3-①:定期契約データ取得(車種区分・分類名1・定期有効月数毎)
$regularData = $this->calculateRegularEarnings($park->park_id, $targetDate);
// 仕様JOB3-②:一時金データ取得(車種毎)
$lumpsumData = $this->calculateLumpsumEarnings($park->park_id, $targetDate);
// 仕様JOB3-③:解約返戻金データ取得(車種区分毎)
$refundData = $this->calculateRefundEarnings($park->park_id, $targetDate);
// 仕様JOB3-④:再発行データ取得(車種区分毎)
$reissueData = $this->calculateReissueCount($park->park_id, $targetDate);
// 仕様JOB3-STEP1①②③④のデータがいずれかあれば→JOB4へ
if (empty($regularData) && empty($lumpsumData) && empty($refundData) && empty($reissueData)) {
// 仕様JOB3-STEP1対象データなし
$noDataMessage = "売上集計(日次):対象日:{$targetDate}/駐輪場:{$park->park_name}:売上データが存在しません。";
return [
'summary_records' => 0,
'data_integrity_issue' => $dataIntegrityIssue,
'no_data_message' => $noDataMessage,
'status_comments' => []
];
}
// 仕様JOB4売上集計結果を登録同一キーの既存レコードは各insert前に削除
$summaryRecords = 0;
$statusComments = [];
// ① 定期契約データ同じ組合せpsection×usertype×monthsを統合
$mergedRegularData = $this->mergeRegularDataByGroup($regularData);
foreach ($mergedRegularData as $key => $mergedRow) {
$sc = $this->createEarningsSummary($park, $mergedRow, $targetDate, 'regular', $dataIntegrityIssue);
$statusComments[] = $sc;
$summaryRecords++;
}
// ②③④ 一時金・解約・再発行データ(車種区分毎に集約)
$otherDataByPsection = $this->mergeOtherEarningsData($lumpsumData, $refundData, $reissueData);
foreach ($otherDataByPsection as $psectionId => $data) {
$sc = $this->createEarningsSummary($park, $data, $targetDate, 'other', null);
$statusComments[] = $sc;
$summaryRecords++;
}
Log::info('駐輪場売上集計完了', [
'park_id' => $park->park_id,
'park_name' => $park->park_name,
'summary_records' => $summaryRecords,
'data_integrity_issue' => $dataIntegrityIssue
]);
return [
'summary_records' => $summaryRecords,
'data_integrity_issue' => $dataIntegrityIssue,
'no_data_message' => null,
'status_comments' => $statusComments
];
} catch (\Exception $e) {
Log::error('駐輪場売上集計エラー', [
'park_id' => $park->park_id,
'error' => $e->getMessage()
]);
throw $e;
}
}
/**
* 0. 情報不備チェック
*
* 仕様 todo/SHJ-9/SHJ-9.txt:44-68
*
* @param int $parkId 駐輪場ID
* @param string $targetDate 集計対象日YYYY-MM-DD
* @return string 情報不備メッセージ(仕様フォーマット:"情報不備xxx" or "情報不備:なし"
*/
private function checkDataIntegrity(int $parkId, string $targetDate): string
{
// 仕様SQL-2情報不備チェックログ吐き出し
$incompleteContracts = DB::table('regular_contract')
->select(['contract_id', 'contract_reduction', 'update_flag', 'psection_id', 'enable_months'])
->where('park_id', $parkId)
->where('contract_flag', 1)
->whereDate('contract_payment_day', '=', $targetDate)
->where(function($query) {
$query->whereNull('update_flag')
->orWhereNull('psection_id')
->orWhereNull('enable_months');
})
->get()
->toArray();
if (empty($incompleteContracts)) {
return self::DATA_INTEGRITY_NONE;
}
// 仕様SQL-2ログ吐き出し詳細情報を出力
Log::warning('SHJ-9 情報不備契約詳細', [
'park_id' => $parkId,
'target_date' => $targetDate,
'contracts' => $incompleteContracts
]);
// 仕様フォーマット:" 情報不備:" + 契約IDカンマ区切り全角スペース付き
$contractIds = array_map(function($c) {
return is_object($c) ? $c->contract_id : $c['contract_id'];
}, $incompleteContracts);
return ' 情報不備:' . implode(',', $contractIds);
}
/**
* ① 定期契約データ取得(車種区分・分類名1・定期有効月数毎)
*
* 仕様 todo/SHJ-9/SHJ-9.txt:70-95
* SQL定義減免措置・継続フラグ・車種区分・分類名・有効月数でグループ化し、
* 授受金額の合計と件数を算出する
*
* @param int $parkId 駐輪場ID
* @param string $targetDate 集計対象日YYYY-MM-DD
* @return array
*/
private function calculateRegularEarnings(int $parkId, string $targetDate): array
{
$results = DB::table('regular_contract as T1')
->join('usertype as T3', 'T1.user_categoryid', '=', 'T3.user_categoryid')
->select([
DB::raw('IFNULL(T1.contract_reduction, 0) as contract_reduction'),
'T1.update_flag',
'T1.psection_id',
'T3.usertype_subject1',
'T1.enable_months',
DB::raw('SUM(T1.contract_money) as total_amount'), // 仕様line 77: 授受金額の合計
DB::raw('COUNT(T1.contract_money) as contract_count') // 仕様line 78: 授受件数
])
->where('T1.park_id', $parkId)
->where('T1.contract_flag', 1)
->whereDate('T1.contract_payment_day', '=', $targetDate)
->whereNotNull('T1.update_flag')
->whereNotNull('T1.psection_id')
->whereNotNull('T1.enable_months')
->groupBy([
DB::raw('IFNULL(T1.contract_reduction, 0)'),
'T1.update_flag',
'T1.psection_id',
'T3.usertype_subject1',
'T1.enable_months'
])
->get();
return $results->toArray();
}
/**
* ② 一時金データ取得(車種毎)
*
* 仕様 todo/SHJ-9/SHJ-9.txt:114-125
*
* @param int $parkId 駐輪場ID
* @param string $targetDate 集計対象日YYYY-MM-DD
* @return array
*/
private function calculateLumpsumEarnings(int $parkId, string $targetDate): array
{
$results = DB::table('lumpsum_transaction')
->select([
'type_class as psection_id',
DB::raw('COUNT(*) as lumpsum_count'),
DB::raw('COALESCE(SUM(deposit_amount), 0) as lumpsum')
])
->where('park_id', $parkId)
->whereDate('pay_date', '=', $targetDate)
->groupBy('type_class')
->get();
return $results->toArray();
}
/**
* ③ 解約返戻金データ取得(車種区分毎)
*
* 仕様 todo/SHJ-9/SHJ-9.txt:126-137
*
* @param int $parkId 駐輪場ID
* @param string $targetDate 集計対象日YYYY-MM-DD
* @return array
*/
private function calculateRefundEarnings(int $parkId, string $targetDate): array
{
$results = DB::table('regular_contract')
->select([
'psection_id',
DB::raw('COALESCE(SUM(refunds), 0) as refunds')
])
->where('park_id', $parkId)
->where('contract_cancel_flag', 1)
->whereDate('repayment_at', '=', $targetDate)
->groupBy('psection_id')
->get();
return $results->toArray();
}
/**
* ④ 再発行データ取得(車種区分毎)
*
* 仕様 todo/SHJ-9/SHJ-9.txt:138-149
*
* @param int $parkId 駐輪場ID
* @param string $targetDate 集計対象日YYYY-MM-DD
* @return array
*/
private function calculateReissueCount(int $parkId, string $targetDate): array
{
$results = DB::table('seal')
->select([
'psection_id',
DB::raw('COUNT(contract_id) as reissue_count')
])
->where('park_id', $parkId)
->where('contract_seal_issue', '>=', 2)
->whereDate('seal_day', '=', $targetDate)
->groupBy('psection_id')
->get();
return $results->toArray();
}
/**
* 定期契約データを組合せ毎に統合
*
* SQLは contract_reduction × update_flag で分組しているため、
* 同じ psection × usertype × months の組合せで複数行が返る場合がある。
* ここで park_id + psection_id + usertype_subject1 + enable_months をキーに統合し、
* 新規/更新 × 減免/通常 の各件数・金額を1つのオブジェクトに集約する。
*
* 仕様 todo/SHJ-9/SHJ-9.txt:96-113
*
* @param array $regularData calculateRegularEarnings()の結果
* @return array キーpsection_id|usertype|months、値統合されたデータオブジェクト
*/
private function mergeRegularDataByGroup(array $regularData): array
{
$merged = [];
foreach ($regularData as $row) {
// 統合キーpsection_id|usertype_subject1|enable_months
$key = $row->psection_id . '|' . $row->usertype_subject1 . '|' . $row->enable_months;
// 初回作成
if (!isset($merged[$key])) {
$merged[$key] = (object)[
'psection_id' => $row->psection_id,
'usertype_subject1' => $row->usertype_subject1,
'enable_months' => $row->enable_months,
// 新規・通常
'regular_new_count' => 0,
'regular_new_amount' => 0,
// 新規・減免
'regular_new_reduction_count' => 0,
'regular_new_reduction_amount' => 0,
// 更新・通常
'regular_update_count' => 0,
'regular_update_amount' => 0,
// 更新・減免
'regular_update_reduction_count' => 0,
'regular_update_reduction_amount' => 0
];
}
// 区分判定
$isNew = in_array($row->update_flag, [2, null]); // 新規
$isReduction = ($row->contract_reduction == 1); // 減免
$count = $row->contract_count ?? 0;
$amount = $row->total_amount ?? 0;
// 対応するフィールドに累加
if ($isNew && !$isReduction) {
// 新規・通常
$merged[$key]->regular_new_count += $count;
$merged[$key]->regular_new_amount += $amount;
} elseif ($isNew && $isReduction) {
// 新規・減免
$merged[$key]->regular_new_reduction_count += $count;
$merged[$key]->regular_new_reduction_amount += $amount;
} elseif (!$isNew && !$isReduction) {
// 更新・通常
$merged[$key]->regular_update_count += $count;
$merged[$key]->regular_update_amount += $amount;
} elseif (!$isNew && $isReduction) {
// 更新・減免
$merged[$key]->regular_update_reduction_count += $count;
$merged[$key]->regular_update_reduction_amount += $amount;
}
}
return $merged;
}
/**
* 一時金・解約・再発行データを車種区分毎に統合
*
* @param array $lumpsumData
* @param array $refundData
* @param array $reissueData
* @return array
*/
private function mergeOtherEarningsData(array $lumpsumData, array $refundData, array $reissueData): array
{
$merged = [];
// 一時金
foreach ($lumpsumData as $row) {
$psectionId = $row->psection_id;
if (!isset($merged[$psectionId])) {
$merged[$psectionId] = (object)[
'psection_id' => $psectionId,
'usertype_subject1' => null,
'enable_months' => 0,
'lumpsum_count' => 0,
'lumpsum' => 0,
'refunds' => 0,
'reissue_count' => 0
];
}
$merged[$psectionId]->lumpsum_count = $row->lumpsum_count;
$merged[$psectionId]->lumpsum = $row->lumpsum;
}
// 解約返戻金
foreach ($refundData as $row) {
$psectionId = $row->psection_id;
if (!isset($merged[$psectionId])) {
$merged[$psectionId] = (object)[
'psection_id' => $psectionId,
'usertype_subject1' => null,
'enable_months' => 0,
'lumpsum_count' => 0,
'lumpsum' => 0,
'refunds' => 0,
'reissue_count' => 0
];
}
$merged[$psectionId]->refunds = $row->refunds;
}
// 再発行
foreach ($reissueData as $row) {
$psectionId = $row->psection_id;
if (!isset($merged[$psectionId])) {
$merged[$psectionId] = (object)[
'psection_id' => $psectionId,
'usertype_subject1' => null,
'enable_months' => 0,
'lumpsum_count' => 0,
'lumpsum' => 0,
'refunds' => 0,
'reissue_count' => 0
];
}
$merged[$psectionId]->reissue_count = $row->reissue_count;
}
return $merged;
}
/**
* 車種区分名を取得
*
* @param int|null $psectionId 車種区分ID
* @return string 車種区分名
*/
private function getPsectionName(?int $psectionId): string
{
if ($psectionId === null) {
return '';
}
$name = DB::table('psection')
->where('psection_id', $psectionId)
->value('psection_subject');
return $name ?? '';
}
/**
* 仕様JOB4売上集計結果を削除→登録
*
* 同一キー駐車場ID,集計種別,集計開始日,集計終了日,売上日付,車種区分,分類名1,定期有効月数)
* が既に登録済みの場合削除した上で、新規レコードを登録する。
*
* @param object $park 駐輪場情報
* @param object $data 売上データ
* @param string $targetDate 集計対象日YYYY-MM-DD
* @param string $dataType データ種別regular or other
* @param string|null $dataIntegrityIssue 情報不備メッセージregularのみ使用
* @return string 仕様JOB4形式のステータスコメント
*/
private function createEarningsSummary($park, $data, string $targetDate, string $dataType, ?string $dataIntegrityIssue = null): string
{
$usertypeSubject1 = $data->usertype_subject1 ?? null;
$enableMonths = $data->enable_months ?? 0;
// 仕様JOB4同一キーの既存レコードを削除
$deleteQuery = DB::table('earnings_summary')
->where('park_id', $park->park_id)
->where('summary_type', self::SUMMARY_TYPE_DAILY)
->whereNull('summary_start_date')
->whereNull('summary_end_date')
->where('earnings_date', $targetDate);
// NULL値対応psection_idがnullの場合はwhereNull
if ($data->psection_id === null) {
$deleteQuery->whereNull('psection_id');
} else {
$deleteQuery->where('psection_id', $data->psection_id);
}
// NULL値対応usertype_subject1がnullの場合はwhereNull
if ($usertypeSubject1 === null) {
$deleteQuery->whereNull('usertype_subject1');
} else {
$deleteQuery->where('usertype_subject1', $usertypeSubject1);
}
$deleteQuery->where('enable_months', $enableMonths);
$deleteQuery->delete();
// 仕様:集計備考の生成
if ($dataType === 'regular' && $dataIntegrityIssue !== null) {
// 仕様JOB3-①定期データのsummary_noteには情報不備を含める
$summaryNote = "SHJ-9{$targetDate}{$dataIntegrityIssue}";
} else {
// 仕様JOB3-②③④otherデータのsummary_note
$summaryNote = "SHJ-9{$targetDate}";
}
$insertData = [
'park_id' => $park->park_id,
'summary_type' => self::SUMMARY_TYPE_DAILY,
'summary_start_date' => null,
'summary_end_date' => null,
'earnings_date' => $targetDate,
'psection_id' => $data->psection_id,
'usertype_subject1' => $usertypeSubject1,
'enable_months' => $enableMonths,
'summary_note' => $summaryNote,
'created_at' => now(),
'updated_at' => now(),
'operator_id' => self::BATCH_OPERATOR_ID
];
if ($dataType === 'regular') {
// 仕様JOB3-①:定期契約データ
$insertData = array_merge($insertData, [
'regular_new_count' => $data->regular_new_count ?? 0,
'regular_new_amount' => $data->regular_new_amount ?? 0,
'regular_new_reduction_count' => $data->regular_new_reduction_count ?? 0,
'regular_new_reduction_amount' => $data->regular_new_reduction_amount ?? 0,
'regular_update_count' => $data->regular_update_count ?? 0,
'regular_update_amount' => $data->regular_update_amount ?? 0,
'regular_update_reduction_count' => $data->regular_update_reduction_count ?? 0,
'regular_update_reduction_amount' => $data->regular_update_reduction_amount ?? 0,
'lumpsum_count' => 0,
'lumpsum' => 0,
'refunds' => 0,
'other_income' => 0,
'other_spending' => 0,
'reissue_count' => 0,
'reissue_amount' => 0
]);
} else {
// 仕様JOB3-②③④一時金・解約・再発行データ定期フィールドは0固定
$insertData = array_merge($insertData, [
'regular_new_count' => 0,
'regular_new_amount' => 0,
'regular_new_reduction_count' => 0,
'regular_new_reduction_amount' => 0,
'regular_update_count' => 0,
'regular_update_amount' => 0,
'regular_update_reduction_count' => 0,
'regular_update_reduction_amount' => 0,
'lumpsum_count' => $data->lumpsum_count ?? 0,
'lumpsum' => $data->lumpsum ?? 0,
'refunds' => $data->refunds ?? 0,
'other_income' => 0,
'other_spending' => 0,
'reissue_count' => $data->reissue_count ?? 0,
'reissue_amount' => 0
]);
}
DB::table('earnings_summary')->insert($insertData);
// 仕様JOB4レコード毎のステータスコメント生成全データ型共通フォーマット
$psectionName = $this->getPsectionName($data->psection_id);
$displayUsertype = $usertypeSubject1 ?? '';
$statusComment = "売上集計(日次):対象日:{$targetDate}/駐輪場:{$park->park_name}/車種区分:{$psectionName}/分類名1:{$displayUsertype}/定期有効月数:{$enableMonths}";
Log::debug('売上集計結果登録', [
'park_id' => $park->park_id,
'psection_id' => $data->psection_id,
'data_type' => $dataType,
'target_date' => $targetDate
]);
return $statusComment;
}
/**
* 【処理5】オペレータキュー作成駐輪場単位・情報不備がある場合のみ
*
* 仕様 todo/SHJ-9/SHJ-9.txt:253-280
* - que_class: 14集計対象エラー
* - que_comment: 空文字(""
* - que_status: 1キュー発生
* - que_status_comment: 空文字(""
* - work_instructions: 情報不備メッセージ
* - park_id: 駐輪場ID仕様 "処理1.駐輪場ID"、パラメータエラー時はnull
* - operator_id: 9999999バッチ処理固定値
*
* @param string $message 情報不備メッセージ
* @param int|null $parkId 駐輪場IDパラメータエラー時はnull
* @return void
*/
private function createOperatorQueue(string $message, ?int $parkId = null): void
{
try {
DB::table('operator_que')->insert([
'que_class' => 14, // 集計対象エラー
'user_id' => null,
'contract_id' => null,
'park_id' => $parkId, // 仕様処理1.駐輪場ID
'que_comment' => '', // 仕様line 260: ""
'que_status' => 1, // キュー発生
'que_status_comment' => '', // 仕様line 262: ""
'work_instructions' => $message, // 仕様line 263: 情報不備
'operator_id' => self::BATCH_OPERATOR_ID, // 9999999
'created_at' => now(),
'updated_at' => now()
]);
Log::info('オペレータキュー作成完了', [
'park_id' => $parkId,
'que_class' => 14,
'que_status' => 1,
'operator_id' => self::BATCH_OPERATOR_ID,
'work_instructions' => $message
]);
} catch (\Exception $e) {
Log::error('オペレータキュー作成エラー', [
'park_id' => $parkId,
'error' => $e->getMessage()
]);
}
}
/**
* SHJ-8 バッチ処理ログ作成
*
* 仕様 todo/SHJ-9/SHJ-9.txt:289-300
* 共通処理「SHJ-8 バッチ処理ログ作成」を呼び出す
*
* @param string $jobName ジョブ名
* @param string $status ステータス (success/error)
* @param string $statusComment 業務固有のステータスコメント
* @return array SHJ-8実行結果
*/
private function callShjEight(string $jobName, string $status, string $statusComment): array
{
try {
$device = Device::orderBy('device_id')->first();
$deviceId = $device ? $device->device_id : 1;
$today = now()->format('Y/m/d');
$result = $this->shjEightService->execute(
$deviceId,
'SHJ-9',
$jobName,
$status,
$statusComment,
$today,
$today
);
Log::info('SHJ-8 バッチ処理ログ作成完了', [
'job_name' => $jobName,
'status' => $status,
'shj8_result' => $result['result'] ?? null
]);
return $result;
} catch (\Exception $e) {
Log::error('SHJ-8 バッチ処理ログ作成エラー', [
'error' => $e->getMessage(),
'job_name' => $jobName,
'status_comment' => $statusComment
]);
throw $e;
}
}
/**
* SHJ-8実行結果の判定
*
* 仕様JOB5の判定処理結果=0/その他)をログで明示する。
*
* @param array $shjEightResult SHJ-8返却値
* @return void
*/
private function evaluateShjEightResult(array $shjEightResult): void
{
if ((int)($shjEightResult['result'] ?? 1) === 0) {
Log::info('SHJ-9 SHJ-8処理結果判定: 0正常');
return;
}
Log::warning('SHJ-9 SHJ-8処理結果判定: 0以外異常', [
'shj8_result' => $shjEightResult
]);
}
}