feat: SHJ-11とSHJ-12バッチ処理コマンドを追加
All checks were successful
Deploy preview (main_xxx) / Deploy ${{ github.ref_name }} (push) Successful in 13s

- SHJ-11: 現在契約台数集計処理を実装
- SHJ-12: 未払い者通知処理を実装
This commit is contained in:
Your Name 2025-08-29 12:04:27 +09:00
parent bb3f85e10f
commit 50bb8543a5
7 changed files with 1297 additions and 4 deletions

9
.env
View File

@ -2,7 +2,8 @@ APP_NAME=so-manager
APP_ENV=local APP_ENV=local
APP_KEY=base64:ejLwJbt2bEXY9emPUmsurG+X1hzkjTxQQvq2/FO14RY= APP_KEY=base64:ejLwJbt2bEXY9emPUmsurG+X1hzkjTxQQvq2/FO14RY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=https://krgm.so-manager-dev.com/ APP_URL=http://somanager-local.localhost:81/
APP_LOCALE=ja APP_LOCALE=ja
APP_FALLBACK_LOCALE=ja APP_FALLBACK_LOCALE=ja
APP_FAKER_LOCALE=ja_JP APP_FAKER_LOCALE=ja_JP
@ -22,9 +23,9 @@ LOG_LEVEL=debug
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=localhost DB_HOST=localhost
DB_PORT=3306 DB_PORT=3306
DB_DATABASE=krgm DB_DATABASE=somanager_admin
DB_USERNAME=krgm_user DB_USERNAME=root
DB_PASSWORD=StrongDbP@ss2 DB_PASSWORD=
SESSION_DRIVER=database SESSION_DRIVER=database
SESSION_LIFETIME=120 SESSION_LIFETIME=120

64
.env.main Normal file
View File

@ -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}"

View File

@ -0,0 +1,182 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use App\Services\ShjElevenService;
/**
* SHJ-11 現在契約台数集計コマンド
*
* 集計単位每个の契約台数を算出し、ゾーンマスタとの管理処理を実行する
* バックグラウンドで実行される定期バッチ処理
*/
class ShjElevenCommand extends Command
{
/**
* コンソールコマンドの名前とシグネチャ
*
* 引数: なし
* オプション: なし
*
* @var string
*/
protected $signature = 'shj:11';
/**
* コンソールコマンドの説明
*
* @var string
*/
protected $description = 'SHJ-11 現在契約台数集計 - 集計単位每个契約台数を算出しゾーンマスタ管理を実行';
/**
* SHJ-11サービスクラス
*
* @var ShjElevenService
*/
protected $shjElevenService;
/**
* コンストラクタ
*
* @param ShjElevenService $shjElevenService
*/
public function __construct(ShjElevenService $shjElevenService)
{
parent::__construct();
$this->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;
}
}
}

View File

@ -0,0 +1,177 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use App\Services\ShjTwelveService;
/**
* SHJ-12 未払い者通知処理コマンド
*
* 定期契約マスタより未払い者を取得し、通知処理またはオペレーターキュー追加を実行する
* バックグラウンドで実行される定期バッチ処理
*/
class ShjTwelveCommand extends Command
{
/**
* コンソールコマンドの名前とシグネチャ
*
* 引数: なし
* オプション: なし
*
* @var string
*/
protected $signature = 'shj:12';
/**
* コンソールコマンドの説明
*
* @var string
*/
protected $description = 'SHJ-12 未払い者通知処理 - 定期契約マスタより未払い者を取得し通知処理を実行';
/**
* SHJ-12サービスクラス
*
* @var ShjTwelveService
*/
protected $shjTwelveService;
/**
* コンストラクタ
*
* @param ShjTwelveService $shjTwelveService
*/
public function __construct(ShjTwelveService $shjTwelveService)
{
parent::__construct();
$this->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;
}
}
}

View File

@ -0,0 +1,432 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Models\Batch\BatchLog;
use App\Models\RegularContract;
use App\Models\Park;
use App\Models\Psection;
use App\Models\Ptype;
use Carbon\Carbon;
/**
* SHJ-11 現在契約台数集計サービス
*
* 集計単位每个の契約台数を算出し、ゾーンマスタとの管理処理を実行
*/
class ShjElevenService
{
/**
* 【処理1】集計単位每个の契約台数を算出する
*
* 集計単位: 駐輪場ID + 車種区分ID + 駐輪分類ID + ゾーンID
*
* SQL仕様に基づく複雑なJOIN処理:
* - regular_contract (T1)
* - park (T2)
* - psection (T4)
* - price_a (T5)
* - ptype (T3)
*
* @return array 契約台数集計結果
*/
public function calculateContractCounts(): array
{
try {
$query = DB::table('regular_contract as T1')
->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
]);
}
}
}

View File

@ -440,4 +440,106 @@ class ShjMailSendService
'message' => $message '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
];
}
}
} }

View File

@ -0,0 +1,335 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Models\Batch\BatchLog;
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;
/**
* コンストラクタ
*
* @param ShjMailSendService $shjMailSendService
*/
public function __construct(ShjMailSendService $shjMailSendService)
{
$this->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
]);
}
}
}