| @@ -56,3 +56,9 @@ VITE_PUSHER_HOST="${PUSHER_HOST}" | |||
| VITE_PUSHER_PORT="${PUSHER_PORT}" | |||
| VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" | |||
| VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" | |||
| SAIL_XDEBUG_MODE=debug,develop | |||
| SMS_PROVIDER=log | |||
| SMS_FOURS_MESSAGE_USER=null | |||
| SMS_FOURS_MESSAGE_PASSWORD=null | |||
| @@ -0,0 +1,64 @@ | |||
| APP_NAME=Laravel | |||
| APP_ENV=local | |||
| APP_KEY=base64:7/KotwfYt4yVnc1Euu18lGSzo10TMPvW3JMv7LWdo8Y= | |||
| APP_DEBUG=true | |||
| APP_URL=http://localhost | |||
| LOG_CHANNEL=web | |||
| LOG_DEPRECATIONS_CHANNEL=null | |||
| LOG_LEVEL=debug | |||
| DB_CONNECTION=pgsql | |||
| DB_HOST=pgsql | |||
| DB_PORT=5432 | |||
| DB_DATABASE=testing | |||
| DB_USERNAME=sail | |||
| DB_PASSWORD=password | |||
| BROADCAST_DRIVER=log | |||
| CACHE_DRIVER=file | |||
| FILESYSTEM_DISK=local | |||
| QUEUE_CONNECTION=sync | |||
| SESSION_DRIVER=file | |||
| SESSION_LIFETIME=120 | |||
| MEMCACHED_HOST=127.0.0.1 | |||
| REDIS_HOST=127.0.0.1 | |||
| REDIS_PASSWORD=null | |||
| REDIS_PORT=6379 | |||
| MAIL_MAILER=smtp | |||
| MAIL_HOST=mailpit | |||
| MAIL_PORT=1025 | |||
| MAIL_USERNAME=null | |||
| MAIL_PASSWORD=null | |||
| MAIL_ENCRYPTION=null | |||
| MAIL_FROM_ADDRESS="hello@example.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 | |||
| PUSHER_APP_ID= | |||
| PUSHER_APP_KEY= | |||
| PUSHER_APP_SECRET= | |||
| PUSHER_HOST= | |||
| PUSHER_PORT=443 | |||
| PUSHER_SCHEME=https | |||
| PUSHER_APP_CLUSTER=mt1 | |||
| VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" | |||
| VITE_PUSHER_HOST="${PUSHER_HOST}" | |||
| VITE_PUSHER_PORT="${PUSHER_PORT}" | |||
| VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" | |||
| VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" | |||
| SAIL_XDEBUG_MODE=debug,develop | |||
| SMS_PROVIDER=log | |||
| SMS_FOURS_MESSAGE_USER=T8VWXuqlV2Z6 | |||
| SMS_FOURS_MESSAGE_PASSWORD=0Vjnak9FFPTH | |||
| @@ -0,0 +1,40 @@ | |||
| <?php | |||
| namespace App\Codes\FourSMessage; | |||
| enum APIErrorCode: string | |||
| { | |||
| case CODE_1001 = '1001'; // 認証失敗。 | |||
| case CODE_1002 = '1002'; // 認証されたユーザーにはこの操作を行う権限が無い。 | |||
| case CODE_2002 = '2002'; // 送信実行予定時刻範囲エラー。(現在〜31 日後の範囲外) | |||
| case CODE_2003 = '2003'; // 日付範囲が 1 ヶ月を超えている。 | |||
| case CODE_2004 = '2004'; // 指定された SMS 送信要求はキャンセルできない。(送信済み) | |||
| case CODE_3002 = '3002'; // キャリアコードが指定されていない。 | |||
| case CODE_3003 = '3003'; // キャリアコードの値が不正。 | |||
| case CODE_3004 = '3004'; // 送信先電話番号が指定されていない。 | |||
| case CODE_3005 = '3005'; // 送信先電話番号の形式が不正。 | |||
| case CODE_3006 = '3006'; // メッセージが指定されていない。 | |||
| case CODE_3007 = '3007'; // メッセージのデコードエラー。 | |||
| case CODE_3011 = '3011'; // メッセージに「¦」文字が含まれている。 | |||
| case CODE_3012 = '3012'; // 送信実行予定時刻の形式が不正。 | |||
| case CODE_3013 = '3013'; // 範囲指定の日付時刻が指定されていない。(開始時刻) | |||
| case CODE_3014 = '3014'; // 範囲指定の日付時刻が指定されていない。(終了時刻) | |||
| case CODE_3015 = '3015'; // 範囲指定の日付形式が不正。(開始時刻) | |||
| case CODE_3016 = '3016'; // 範囲指定の日付形式が不正。(終了時刻) | |||
| case CODE_3017 = '3017'; // 日付範囲が不正。 | |||
| case CODE_3018 = '3018'; // SMS 送信要求ID が指定されていない。 | |||
| case CODE_3019 = '3019'; // 指定された SMS 送信要求ID が不正。 | |||
| case CODE_3037 = '3037'; // パスワードが指定されていない。 | |||
| case CODE_3038 = '3038'; // 指定されたパスワードが不正。(base64 エンコード) | |||
| case CODE_3039 = '3039'; // 指定されたパスワードが不正。(使用文字) | |||
| case CODE_3040 = '3040'; // 指定されたパスワードが不正。(セキュリティー要件) | |||
| case CODE_3055 = '3055'; // 説明文字列を base64 でデコードできない。 | |||
| case CODE_3067 = '3067'; // 送信オプションの形式が不正 | |||
| case CODE_3068 = '3068'; // コールバックフラグの形式が不正 | |||
| case CODE_3069 = '3069'; // コールバック URL が指定されていない | |||
| case CODE_3070 = '3070'; // コールバック URL の形式が不正 | |||
| case CODE_3073 = '3073'; // 送信除外時間extime_from の内容が正しくない | |||
| case CODE_3074 = '3074'; // 送信除外時間extime_to の内容が正しくない | |||
| case CODE_9001 = '9001'; // 各種エラー | |||
| case CODE_1000 = '1000'; //1 送信要求時の各種エラー (エラー内容はレスポンス参照) | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| <?php | |||
| namespace App\Codes\FourSMessage; | |||
| /** | |||
| * 受付状態 | |||
| */ | |||
| enum RequestStatus: string | |||
| { | |||
| case NONE = ''; | |||
| case DONE = '00'; // 受付済 | |||
| case CANCELED = '00'; // 受付取消 | |||
| case FAILED = '00'; // 受付失敗 | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| <?php | |||
| namespace App\Codes\FourSMessage; | |||
| /** | |||
| * 送達状態 | |||
| */ | |||
| enum ResultStatus: string | |||
| { | |||
| case NONE = ''; | |||
| case SUCCESS_00 = '00'; // 正常 | |||
| case OUT_OF_SERVIFCE_21 = '21'; // 圏外等 | |||
| case NETWORK_ERROR_22 = '22'; // キャリア側NW 障害 | |||
| case MOBILE_ERROR_23 = '23'; // 移動機起因エラー(SMS 拒否設定等) | |||
| case OUT_OF_SERVIFCE_31 = '31'; // 圏外・電源オフ | |||
| case MOBILE_ERROR_32 = '32'; // 移動機起因エラー | |||
| case OTHER_ERROR_33 = '33'; // その他エラー | |||
| case CARRIER_ERROR_33 = '33'; // キャリア側エラー | |||
| case NOT_CARRIERS_USER_34 = '34'; // 自社キャリアのユーザではない | |||
| case DONE_40 = '40'; // 送達済 | |||
| case DELIVERD = 'DELIVERD'; // 正常 | |||
| case FAILED = 'FAILED'; // 送信エラー | |||
| case EXPIRED = 'EXPIRED'; // 圏外等 | |||
| case DELETED = 'DELETED'; // 圏外等 | |||
| case UNDELIV = 'UNDELIV'; // キャリア側NW 障害 | |||
| case REJECTD = 'REJECTD'; // 移動機起因エラー(SMS 拒否設定等) | |||
| case UNKNOWN = 'UNKNOWN'; // 想定外のエラー | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| <?php | |||
| namespace App\Codes\FourSMessage; | |||
| /** | |||
| * 送信状態 | |||
| */ | |||
| enum SendingStatus: string | |||
| { | |||
| case NONE = ''; | |||
| case SENDING = '10'; // 送信中 | |||
| case DONE = '20'; // 送信済 | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| <?php | |||
| namespace App\Codes; | |||
| enum HTTPResultCode: int | |||
| { | |||
| case SECCESS = 0; | |||
| case FAILED = 1; | |||
| case UNAUTHORIZED = 2; | |||
| case EXCLUSIVE_ERROR = 3; | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| <?php | |||
| namespace App\Codes; | |||
| enum QueueName: string | |||
| { | |||
| case EMAIL = 'email'; | |||
| case SMS = 'sms'; | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| <?php | |||
| namespace App\Codes; | |||
| /** | |||
| * 領収証発行依頼ステータス | |||
| */ | |||
| enum ReceiptIssuingOrderStatus: int | |||
| { | |||
| case NONE = 0; | |||
| case CREATED = 100; // 新規作成 | |||
| case SMS_SENDING = 200; // SMS送信中 | |||
| case SMS_RECEIVED = 300; // SMS送信完了 | |||
| case SMS_OPENED = 400; // SMS開封 | |||
| // 郵送関連 | |||
| case MAIL_REQUEST = 500; // 郵送依頼中 | |||
| case PREPARING_FOR_MAIL = 510; // 郵送準備中 | |||
| case MAIL_DONE = 520; // 郵送完了 | |||
| // Email送信関連 | |||
| case EMAIL_SENDING = 600; // Email送信中 | |||
| case EMAIL_DONE = 610; // Email送信完了 | |||
| // PDFダウンロード | |||
| case DOWNLOAD_DONE = 700; // ダウンロード完了 | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| <?php | |||
| namespace App\Codes; | |||
| enum SMSProviderName: string | |||
| { | |||
| case FOUR_S_MESSAGE = 'FourSMessage'; | |||
| case LOG = 'log'; | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| <?php | |||
| namespace App\Codes; | |||
| enum SMSSendPurpose: string | |||
| { | |||
| case SEND_RECEIPT_ISSUING_ORDER_FORM = 'SEND_RECEIPT_ISSUING_ORDER_FORM'; | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| <?php | |||
| namespace App\Codes; | |||
| /** | |||
| * ユーザー権限 | |||
| */ | |||
| enum UserRole: int | |||
| { | |||
| case NONE = 0; | |||
| case NORMAL_ADMIN = 100; | |||
| case CONTRACT_ADMIN = 200; | |||
| case SUPER_ADMIN = 900; | |||
| } | |||
| @@ -0,0 +1,105 @@ | |||
| <?php | |||
| namespace App\Console\Commands; | |||
| use Exception; | |||
| use Illuminate\Console\Command; | |||
| use Illuminate\Support\Facades\Log; | |||
| use Illuminate\Support\Str; | |||
| abstract class BaseCommand extends Command | |||
| { | |||
| const RESULTCODE_SUCCESS = 0; | |||
| const RESULTCODE_WARN = 1; | |||
| const RESULTCODE_FAILED = 2; | |||
| /** | |||
| * Create a new command instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct() | |||
| { | |||
| parent::__construct(); | |||
| } | |||
| abstract protected function service(): int; | |||
| protected bool $outputInfoForBase = true; | |||
| /** | |||
| * Execute the console command. | |||
| * | |||
| * @return int | |||
| */ | |||
| public function handle() | |||
| { | |||
| $ret = 0; | |||
| $this->boot(); | |||
| try { | |||
| $ret = $this->service(); | |||
| } catch (Exception $e) { | |||
| $message = sprintf("例外発生:%s:%s:%d", $e->getMessage(), $e->getFile(), $e->getLine()); | |||
| $this->outputError($message, $e->getTrace()); | |||
| $ret = self::RESULTCODE_FAILED; | |||
| } | |||
| if ($ret === self::RESULTCODE_SUCCESS) { | |||
| $this->outputInfoForBase("成功しました。"); | |||
| } else if ($ret === self::RESULTCODE_WARN) { | |||
| $this->outputWarn("一部失敗があります。"); | |||
| } else if ($ret === self::RESULTCODE_FAILED) { | |||
| $this->outputError("失敗しました"); | |||
| } else { | |||
| $this->outputError(sprintf("未定義のエラーコード:%d", $ret)); | |||
| } | |||
| return $ret; | |||
| } | |||
| private function boot() | |||
| { | |||
| Log::setDefaultDriver("batch"); | |||
| Log::withContext([ | |||
| '__scheduleId' => strval(Str::uuid()), | |||
| ...$this->arguments(), | |||
| ]); | |||
| $this->outputInfoForBase(sprintf("バッチ起動 %s", $this->getCommandName())); | |||
| } | |||
| protected function outputInfo(string $message, array $context = []) | |||
| { | |||
| Log::info($message, $this->getContext($context)); | |||
| $this->info($message); | |||
| } | |||
| private function outputInfoForBase(string $message, array $context = []) | |||
| { | |||
| if ($this->outputInfoForBase) { | |||
| Log::info($message, $this->getContext($context)); | |||
| } | |||
| $this->info($message); | |||
| } | |||
| protected function outputWarn(string $message, array $context = []) | |||
| { | |||
| Log::warning($message, $this->getContext($context)); | |||
| $this->warn($message); | |||
| } | |||
| protected function outputError(string $message, array $context = []) | |||
| { | |||
| Log::error($message, $this->getContext($context)); | |||
| $this->error($message); | |||
| } | |||
| private function getContext(array $context = []) | |||
| { | |||
| return array_merge($context, ["context" => $this->arguments()]); | |||
| } | |||
| protected function getCommandName(): string | |||
| { | |||
| return ""; | |||
| } | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| <?php | |||
| namespace App\Console\Commands; | |||
| class HeartBeat extends BaseCommand | |||
| { | |||
| const COMMAND = "heartbeat {--maintenance}"; | |||
| protected bool $outputInfoForBase = false; | |||
| /** | |||
| * The name and signature of the console command. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $signature = self::COMMAND; | |||
| /** | |||
| * The console command description. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $description = 'ハートビート'; | |||
| static public function getCommand() | |||
| { | |||
| return self::COMMAND; | |||
| } | |||
| /** | |||
| * Create a new command instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct() | |||
| { | |||
| parent::__construct(); | |||
| } | |||
| /** | |||
| * Execute the console command. | |||
| * | |||
| * @return int | |||
| */ | |||
| public function service(): int | |||
| { | |||
| if ($this->option('maintenance')) { | |||
| $isMaintenanceMode = app()->isDownForMaintenance(); | |||
| if ($isMaintenanceMode) { | |||
| $this->outputWarn("down for maintenance"); | |||
| } | |||
| } else { | |||
| $this->outputInfo("heart beat"); | |||
| } | |||
| return self::RESULTCODE_SUCCESS; | |||
| } | |||
| } | |||
| @@ -0,0 +1,97 @@ | |||
| <?php | |||
| namespace App\Console\Commands; | |||
| use App\Events\SMS\ConfirmEvent; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Logic\SMS\SMSManager; | |||
| use App\Models\SMSSendOrder; | |||
| class TestMail extends BaseCommand | |||
| { | |||
| const COMMAND = "sms:poll"; | |||
| /** | |||
| * The name and signature of the console command. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $signature = self::COMMAND; | |||
| /** | |||
| * The console command description. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $description = 'SMS配信状況を取得/反映する'; | |||
| static public function getCommand() | |||
| { | |||
| return self::COMMAND; | |||
| } | |||
| /** | |||
| * Create a new command instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(private SMSManager $manager) | |||
| { | |||
| parent::__construct(); | |||
| } | |||
| /** | |||
| * Execute the console command. | |||
| * | |||
| * @return int | |||
| */ | |||
| public function service(): int | |||
| { | |||
| $targets = $this->getTargets(); | |||
| $this->outputInfo(sprintf("対象:%d件", $targets->count())); | |||
| if ($targets->isEmpty()) { | |||
| return self::RESULTCODE_SUCCESS; | |||
| } else { | |||
| } | |||
| foreach ($targets as $order) { | |||
| $this->handleOrder($order); | |||
| } | |||
| return self::RESULTCODE_SUCCESS; | |||
| } | |||
| public function getTargets() | |||
| { | |||
| return SMSSendOrder::whereDone(false) | |||
| ->get(); | |||
| } | |||
| public function handleOrder(SMSSendOrder $order) | |||
| { | |||
| $ret = $this->manager->setOrder($order) | |||
| ->poll(); | |||
| if ($ret) { | |||
| $order->save(); | |||
| $this->outputInfo(sprintf( | |||
| "ID:%s 電話番号:%s %s", | |||
| $order->id, | |||
| $order->phone_number, | |||
| $order->done ? "完了" : "未" | |||
| )); | |||
| // イベント発行 | |||
| if ($order->done) { | |||
| ConfirmEvent::dispatch($order); | |||
| } | |||
| } else { | |||
| $this->outputError(printf("失敗対象:%s", $order->toJson())); | |||
| throw new AppCommonException("POLL 失敗"); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,210 @@ | |||
| <?php | |||
| namespace App\Console\Commands; | |||
| use App\Codes\UserRole; | |||
| use App\Http\Controllers\Web\WebController; | |||
| use Exception; | |||
| use Illuminate\Routing\Route as RoutingRoute; | |||
| use Illuminate\Support\Arr; | |||
| use Illuminate\Support\Facades\Route; | |||
| use Illuminate\Support\Str; | |||
| use LogicException; | |||
| class RouteListCsv extends BaseCommand | |||
| { | |||
| /** | |||
| * コントローラー説明の個別定義 | |||
| * 主にミドルウェア側のコントローラーに適用 | |||
| */ | |||
| const CONTROLLER_DESCRIPTION = [ | |||
| \Laravel\Sanctum\Http\Controllers\CsrfCookieController::class => "csrf対策用", | |||
| ]; | |||
| /** | |||
| * The name and signature of the console command. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $signature = 'route:csv {--tsv}'; | |||
| /** | |||
| * The console command description. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $description = 'ルート一覧をcsv標準出力する'; | |||
| private Context $context; | |||
| private $fp = null; | |||
| private string $separator; | |||
| private int $rowIndex = 0; | |||
| /** | |||
| * Create a new command instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct() | |||
| { | |||
| $this->outputInfoForBase = false; | |||
| parent::__construct(); | |||
| } | |||
| /** | |||
| * Execute the console command. | |||
| * | |||
| * @return int | |||
| */ | |||
| protected function service(): int | |||
| { | |||
| $routes = Route::getRoutes(); | |||
| $routes->getRoutes(); | |||
| $this->separator = $this->getSeparator(); | |||
| try { | |||
| $this->fp = fopen(base_path("routes.csv"), "w"); | |||
| $this->putHeader(); | |||
| foreach ($routes->getRoutes() as $r) { | |||
| if (Str::startsWith($r->uri(), ["_", "clockwork"])) { | |||
| continue; | |||
| } | |||
| $this->put($r); | |||
| } | |||
| } catch (Exception $e) { | |||
| $this->outputError($e->getMessage()); | |||
| if ($this->fp !== null) { | |||
| fclose($this->fp); | |||
| $this->fp = null; | |||
| } | |||
| return self::RESULTCODE_FAILED; | |||
| } | |||
| fclose($this->fp); | |||
| return self::RESULTCODE_SUCCESS; | |||
| } | |||
| private function putHeader() | |||
| { | |||
| fputcsv($this->fp, [ | |||
| "#", | |||
| "NAME", | |||
| "説明", | |||
| "メソッド", | |||
| "URI", | |||
| "S", | |||
| "C", | |||
| "A", | |||
| "L", | |||
| "N", | |||
| "コントローラー", | |||
| ], $this->separator); | |||
| } | |||
| private function put(RoutingRoute $route) | |||
| { | |||
| if ($this->fp === null) { | |||
| throw new LogicException("FP null"); | |||
| } | |||
| $controller = $route->getController(); | |||
| fputcsv($this->fp, [ | |||
| $this->rowIndex, | |||
| $this->getControllerName($controller), | |||
| $this->getControllerDescription($controller), | |||
| $this->getMethods($route), | |||
| $route->uri(), | |||
| $this->getRoleAuth($controller, UserRole::NORMAL_ADMIN), | |||
| $this->getRoleAuth($controller, UserRole::CONTRACT_ADMIN), | |||
| $this->getRoleAuth($controller, UserRole::SUPER_ADMIN), | |||
| $controller::class, | |||
| ], $this->separator); | |||
| $this->rowIndex++; | |||
| } | |||
| private function getMethods(RoutingRoute $route) | |||
| { | |||
| return implode('|', $route->methods()); | |||
| } | |||
| private function getRoleAuth($controller, UserRole $role): string | |||
| { | |||
| $ret = true; | |||
| if ($controller instanceof WebController) { | |||
| $ret = $controller->canAccess($role); | |||
| } | |||
| return $ret ? "〇" : "-"; | |||
| } | |||
| private function getControllerName($controller): string | |||
| { | |||
| if ($controller instanceof WebController) { | |||
| return $controller->name(); | |||
| } else if ( | |||
| method_exists($controller, "name") && | |||
| is_callable([$controller, "name"]) | |||
| ) { | |||
| $ret = $controller->name(); | |||
| return is_string($ret) ? $ret : "-"; | |||
| } else { | |||
| // コントローラ名から名前空間、メソッド名、等を除去 | |||
| return Str::of($controller::class) | |||
| ->afterLast("\\") | |||
| ->beforeLast("@") | |||
| ->replace("Controller", ""); | |||
| } | |||
| } | |||
| private function getControllerDescription($controller): string | |||
| { | |||
| if ($controller instanceof WebController) { | |||
| return $controller->description(); | |||
| } else if ( | |||
| method_exists($controller, "description") && | |||
| is_callable([$controller, "description"]) | |||
| ) { | |||
| $ret = $controller->description(); | |||
| return is_string($ret) ? $ret : "-"; | |||
| } else if (Arr::has(self::CONTROLLER_DESCRIPTION, $controller::class)) { | |||
| return self::CONTROLLER_DESCRIPTION[$controller::class]; | |||
| } else { | |||
| return "-"; | |||
| } | |||
| } | |||
| private function outputSjisFile() | |||
| { | |||
| $contents = file_get_contents(base_path("routes.csv")); | |||
| file_put_contents( | |||
| base_path("routes_sjis.csv"), | |||
| mb_convert_encoding( | |||
| $contents, | |||
| "SJIS", | |||
| "UTF8" | |||
| ) | |||
| ); | |||
| } | |||
| private function getSeparator(): string | |||
| { | |||
| return $this->option("tsv") ? "\t" : ","; | |||
| } | |||
| } | |||
| @@ -0,0 +1,61 @@ | |||
| <?php | |||
| namespace App\Console\Commands; | |||
| use App\Events\Mail\ConfirmEvent; | |||
| use App\Logic\EmailManager; | |||
| use App\Mail\Test; | |||
| class TestMail extends BaseCommand | |||
| { | |||
| const COMMAND = "test:mail {email}"; | |||
| /** | |||
| * The name and signature of the console command. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $signature = self::COMMAND; | |||
| /** | |||
| * The console command description. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $description = 'テストメールを送信する(キュー登録)'; | |||
| static public function getCommand() | |||
| { | |||
| return self::COMMAND; | |||
| } | |||
| /** | |||
| * Create a new command instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct() | |||
| { | |||
| parent::__construct(); | |||
| } | |||
| /** | |||
| * Execute the console command. | |||
| * | |||
| * @return int | |||
| */ | |||
| public function service(): int | |||
| { | |||
| $email = $this->argument('email'); | |||
| $mailer = new Test(); | |||
| $mailer->setEmail($email); | |||
| $manager = new EmailManager($mailer); | |||
| $manager->confirm(); | |||
| return self::RESULTCODE_SUCCESS; | |||
| } | |||
| } | |||
| @@ -12,7 +12,9 @@ class Kernel extends ConsoleKernel | |||
| */ | |||
| protected function schedule(Schedule $schedule): void | |||
| { | |||
| // $schedule->command('inspire')->hourly(); | |||
| Schedules\HeartBeat::register($schedule); | |||
| Schedules\MailSend::register($schedule); | |||
| Schedules\SMSSend::register($schedule); | |||
| } | |||
| /** | |||
| @@ -20,7 +22,7 @@ class Kernel extends ConsoleKernel | |||
| */ | |||
| protected function commands(): void | |||
| { | |||
| $this->load(__DIR__.'/Commands'); | |||
| $this->load(__DIR__ . '/Commands'); | |||
| require base_path('routes/console.php'); | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| <?php | |||
| namespace App\Console\Schedules; | |||
| use Illuminate\Console\Scheduling\Schedule; | |||
| abstract class BaseSchedule | |||
| { | |||
| abstract static public function register(Schedule $schedule); | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| <?php | |||
| namespace App\Console\Schedules; | |||
| use App\Console\Commands\HeartBeat as CommandsHeartBeat; | |||
| use Illuminate\Console\Scheduling\Schedule; | |||
| class HeartBeat extends BaseSchedule | |||
| { | |||
| static public function register(Schedule $schedule) | |||
| { | |||
| $schedule->command(CommandsHeartBeat::class) | |||
| ->everyFiveMinutes() | |||
| ->evenInMaintenanceMode() | |||
| ->description("ハートビート"); | |||
| $schedule->command(CommandsHeartBeat::class, ['--maintenance']) | |||
| ->everyMinute() | |||
| ->evenInMaintenanceMode() | |||
| ->description("メンテナンスモード確認"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| <?php | |||
| namespace App\Console\Schedules; | |||
| use App\Codes\QueueName; | |||
| use Illuminate\Console\Scheduling\Schedule; | |||
| class MailSend extends BaseSchedule | |||
| { | |||
| static public function register(Schedule $schedule) | |||
| { | |||
| $schedule->command( | |||
| sprintf('queue:work --queue=%s --max-time=55 ', QueueName::EMAIL->value) | |||
| ) | |||
| ->everyMinute() | |||
| ->description("メールキュー処理"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| <?php | |||
| namespace App\Console\Schedules; | |||
| use App\Codes\QueueName; | |||
| use Illuminate\Console\Scheduling\Schedule; | |||
| class SMSSend extends BaseSchedule | |||
| { | |||
| static public function register(Schedule $schedule) | |||
| { | |||
| $schedule->command( | |||
| sprintf('queue:work --queue=%s --max-time=55 ', QueueName::SMS->value) | |||
| ) | |||
| ->everyMinute() | |||
| ->description("メールキュー処理"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| <?php | |||
| namespace App\Events\Mail; | |||
| use App\Mail\BaseMailer; | |||
| use App\Models\Email; | |||
| use Illuminate\Broadcasting\InteractsWithSockets; | |||
| use Illuminate\Foundation\Events\Dispatchable; | |||
| use Illuminate\Queue\SerializesModels; | |||
| class ConfirmEvent | |||
| { | |||
| use Dispatchable, InteractsWithSockets, SerializesModels; | |||
| public Email $email; | |||
| /** | |||
| * Create a new event instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(Email|BaseMailer $email) | |||
| { | |||
| if ($email instanceof Email) { | |||
| $this->email = $email; | |||
| } else { | |||
| $this->email = $email->makeModel(); | |||
| $this->email->save(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| <?php | |||
| namespace App\Events\Model; | |||
| class CreatedEvent extends ModelChangeEvent | |||
| { | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| <?php | |||
| namespace App\Events\Model; | |||
| class DeletedEvent extends ModelChangeEvent | |||
| { | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| <?php | |||
| namespace App\Events\Model; | |||
| class DeletingEvent extends ModelChangeEvent | |||
| { | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| <?php | |||
| namespace App\Events\Model; | |||
| use App\Models\Feature\IModelFeature; | |||
| use Illuminate\Broadcasting\InteractsWithSockets; | |||
| use Illuminate\Broadcasting\PrivateChannel; | |||
| use Illuminate\Foundation\Events\Dispatchable; | |||
| use Illuminate\Queue\SerializesModels; | |||
| abstract class ModelChangeEvent | |||
| { | |||
| use Dispatchable, InteractsWithSockets, SerializesModels; | |||
| public IModelFeature $model; | |||
| /** | |||
| * Create a new event instance. | |||
| */ | |||
| public function __construct(IModelFeature $model) | |||
| { | |||
| $this->model = $model; | |||
| } | |||
| /** | |||
| * Get the channels the event should broadcast on. | |||
| * | |||
| * @return array<int, \Illuminate\Broadcasting\Channel> | |||
| */ | |||
| public function broadcastOn(): array | |||
| { | |||
| return [ | |||
| new PrivateChannel('channel-name'), | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| <?php | |||
| namespace App\Events\Model; | |||
| class UpdatingEvent extends ModelChangeEvent | |||
| { | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| <?php | |||
| namespace App\Events\SMS; | |||
| use App\Models\SMSSendOrder; | |||
| use Illuminate\Broadcasting\InteractsWithSockets; | |||
| use Illuminate\Foundation\Events\Dispatchable; | |||
| use Illuminate\Queue\SerializesModels; | |||
| /** | |||
| * 送信状態の確定イベント | |||
| */ | |||
| class ConfirmEvent | |||
| { | |||
| use Dispatchable, InteractsWithSockets, SerializesModels; | |||
| /** | |||
| * Create a new event instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(public SMSSendOrder $order) | |||
| { | |||
| } | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| <?php | |||
| namespace App\Exceptions; | |||
| use Exception; | |||
| class AppCommonException extends Exception | |||
| { | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| <?php | |||
| namespace App\Exceptions; | |||
| use Exception; | |||
| class GeneralErrorMessageException extends Exception | |||
| { | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| <?php | |||
| namespace App\Features; | |||
| use App\Models\Ex\LoginUser as ExLoginUser; | |||
| trait LoginUser | |||
| { | |||
| protected function loginUser(): ExLoginUser | |||
| { | |||
| return app(ExLoginUser::class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,61 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\Login; | |||
| use App\Codes\UserRole; | |||
| use App\Http\Controllers\Web\WebController; | |||
| use App\Models\User; | |||
| use App\Repositories\MeRepository; | |||
| use Illuminate\Foundation\Auth\AuthenticatesUsers; | |||
| use Illuminate\Http\JsonResponse; | |||
| use Illuminate\Http\Request; | |||
| use Illuminate\Support\Facades\Auth; | |||
| class LoginController extends WebController | |||
| { | |||
| public function name(): string | |||
| { | |||
| return "ログイン"; | |||
| } | |||
| public function description(): string | |||
| { | |||
| return "ログインを行う"; | |||
| } | |||
| use AuthenticatesUsers; | |||
| public function __construct(LoginParam $param) | |||
| { | |||
| $this->param = $param; | |||
| } | |||
| public function param(): LoginParam | |||
| { | |||
| return $this->param; | |||
| } | |||
| protected function run(Request $request): JsonResponse | |||
| { | |||
| // 取得したユーザ情報を登録しログインを行う | |||
| $param = $this->param(); | |||
| $user = User::whereEmail($param->email)->first(); | |||
| if ($user === null) { | |||
| return $this->failedResponse(); | |||
| } | |||
| if (Auth::attempt([ | |||
| $this->username() => $param->email, | |||
| 'password' => $param->password, | |||
| ])) { | |||
| $user = Auth::user(); | |||
| return $this->successResponse(); | |||
| } else { | |||
| return $this->failedResponse([], '認証失敗'); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\Login; | |||
| use App\Http\Controllers\Web\BaseParam; | |||
| /** | |||
| * @property string $email | |||
| * @property string $password | |||
| */ | |||
| class LoginParam extends BaseParam | |||
| { | |||
| public function rules(): array | |||
| { | |||
| return [ | |||
| 'email' => $this->str(), | |||
| 'password' => $this->str(), | |||
| ]; | |||
| } | |||
| } | |||
| @@ -159,6 +159,15 @@ abstract class BaseParam implements IParam | |||
| } | |||
| protected function str(array|bool $condition = [], $nullable = false): array | |||
| { | |||
| $conditionEx = array_merge(is_array($condition) ? $condition : [], ['max:250']); | |||
| return array_merge([ | |||
| $this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED, | |||
| self::STR | |||
| ], $conditionEx); | |||
| } | |||
| protected function text(array|bool $condition = [], $nullable = false): array | |||
| { | |||
| return array_merge([ | |||
| $this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED, | |||
| @@ -0,0 +1,48 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\ReceiptIssuingOrder; | |||
| use App\Codes\UserRole; | |||
| use App\Http\Controllers\Web\IParam; | |||
| use App\Http\Controllers\Web\WebController; | |||
| use App\Logic\ReceiptIssuingOrder\CreateManager; | |||
| use Illuminate\Http\JsonResponse; | |||
| use Illuminate\Http\Request; | |||
| class CreateController extends WebController | |||
| { | |||
| public function name(): string | |||
| { | |||
| return "領収証発行依頼新規作成"; | |||
| } | |||
| public function description(): string | |||
| { | |||
| return "領収証発行依頼を新規作成する"; | |||
| } | |||
| public function __construct( | |||
| protected CreateParam $param, | |||
| private CreateManager $manager | |||
| ) { | |||
| $this->middleware('auth:sanctum'); | |||
| $this->roleAllow(UserRole::NORMAL_ADMIN); | |||
| } | |||
| protected function getParam(): IParam | |||
| { | |||
| return $this->param; | |||
| } | |||
| protected function run(Request $request): JsonResponse | |||
| { | |||
| $param = $this->param; | |||
| $this->manager->init() | |||
| ->fill($param->toArray()) | |||
| ->create(); | |||
| return $this->successResponse(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\ReceiptIssuingOrder; | |||
| use App\Http\Controllers\Web\BaseParam; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| use App\Rules\PhoneNumber; | |||
| use Carbon\Carbon; | |||
| /** | |||
| * @property ?string $handlerId | |||
| * @property ?string $orderDatetime | |||
| * @property ?string $sumamryKey1 | |||
| * @property ?string $sumamryKey2 | |||
| * @property ?string $smsPhoneNumber | |||
| * @property ?string $receiptNo | |||
| * @property ?Carbon $receiptUseDatetime | |||
| * @property ?string $receiptShopName | |||
| * @property ?string $receiptIssuer | |||
| * @property ?string $receiptPurpose | |||
| * @property string $receiptAmount | |||
| * @property ?string $memo | |||
| */ | |||
| class CreateParam extends BaseParam | |||
| { | |||
| public function rules(): array | |||
| { | |||
| return [ | |||
| ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1 => $this->str(true), | |||
| ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2 => $this->str(true), | |||
| ReceiptIssuingOrder::COL_NAME_SMS_PHONE_NUMBER => $this->str([new PhoneNumber()]), | |||
| ReceiptIssuingOrder::COL_NAME_RECEIPT_NO => $this->str(true), | |||
| ReceiptIssuingOrder::COL_NAME_RECEIPT_USE_DATETIME => $this->date(true), | |||
| ReceiptIssuingOrder::COL_NAME_RECEIPT_SHOP_NAME => $this->str(true), | |||
| ReceiptIssuingOrder::COL_NAME_RECEIPT_ISSUER => $this->str(true), | |||
| ReceiptIssuingOrder::COL_NAME_RECEIPT_PURPOSE => $this->str(true), | |||
| ReceiptIssuingOrder::COL_NAME_RECEIPT_AMOUNT => $this->numeric(['min:0', 'max:999999'], true), | |||
| ReceiptIssuingOrder::COL_NAME_MEMO => $this->text(true), | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,363 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web; | |||
| use App\Codes\HTTPResultCode as ResultCode; | |||
| use App\Codes\UserRole; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Exceptions\GeneralErrorMessageException; | |||
| use Exception; | |||
| use Illuminate\Foundation\Auth\Access\AuthorizesRequests; | |||
| use Illuminate\Foundation\Bus\DispatchesJobs; | |||
| use Illuminate\Foundation\Validation\ValidatesRequests; | |||
| use Illuminate\Http\JsonResponse; | |||
| use Illuminate\Http\Request; | |||
| use Illuminate\Http\Response; | |||
| use Illuminate\Routing\Controller as BaseController; | |||
| use Illuminate\Support\Arr; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use Illuminate\Support\Facades\Log; | |||
| use Illuminate\Support\Facades\Validator; | |||
| use Illuminate\Support\Str; | |||
| use Illuminate\Validation\ValidationException; | |||
| use LogicException; | |||
| use Symfony\Component\HttpFoundation\BinaryFileResponse; | |||
| use Symfony\Component\HttpKernel\Exception\HttpException; | |||
| abstract class WebController extends BaseController | |||
| { | |||
| use AuthorizesRequests, DispatchesJobs, ValidatesRequests; | |||
| const COL_NAME_CREATED_AT = 'created_at'; | |||
| const COL_NAME_UPDATED_AT = 'updated_at'; | |||
| const COL_NAME_RESULT_CODE = 'result'; | |||
| const COL_NAME_DATA = 'data'; | |||
| const COL_NAME_MESSAGES = 'messages'; | |||
| const COL_NAME_GENERAL_MESSAGE = 'general'; | |||
| const COL_NAME_EMAIL_ID = 'email_id'; | |||
| const COL_NAME_ERRORS = 'errors'; | |||
| /** | |||
| * バリデートした結果を格納 | |||
| * | |||
| * @var array | |||
| */ | |||
| protected $validated = []; | |||
| /** | |||
| * 画面へ返却するメールID | |||
| * | |||
| * @var integer|null | |||
| */ | |||
| private int|null $emailId = null; | |||
| /** | |||
| * 返却するメッセージ | |||
| * | |||
| * @var array|null | |||
| */ | |||
| private array|null $messages = null; | |||
| /** | |||
| * 返却するメッセージ | |||
| * | |||
| * @var string|null | |||
| */ | |||
| private string|null $generalMessage = null; | |||
| /** | |||
| * 返却するデータ | |||
| * | |||
| * @var mixed|null | |||
| */ | |||
| private $data = null; | |||
| /** | |||
| * 返却する結果コード | |||
| * | |||
| * @var ResultCode|null | |||
| */ | |||
| private ResultCode|null $resultCode = ResultCode::SECCESS; | |||
| /** | |||
| * パラメータオブジェクト | |||
| */ | |||
| abstract protected function getParam(): IParam; | |||
| /** | |||
| * コントローラーの名前 | |||
| * オーバーライドされることを想定 | |||
| * 主に、Routeのドキュメント作成用 | |||
| * | |||
| * @return string | |||
| */ | |||
| public function name(): string | |||
| { | |||
| return "---未設定---"; | |||
| } | |||
| /** | |||
| * コントローラーの説明 | |||
| * オーバーライドされることを想定 | |||
| * 主に、Routeのドキュメント作成用 | |||
| * | |||
| * @return string | |||
| */ | |||
| public function description(): string | |||
| { | |||
| return "---未設定---"; | |||
| } | |||
| /** | |||
| * オーバーライド必要 | |||
| * メインロジック | |||
| * | |||
| * @param Request $request | |||
| * @return Response|JsonResponse|string | |||
| */ | |||
| protected function run(Request $request): Response|JsonResponse|BinaryFileResponse|string | |||
| { | |||
| return $this->successResponse(); | |||
| } | |||
| private function getRules() | |||
| { | |||
| return $this->getParam()->rules(); | |||
| } | |||
| public function entry(Request $request) | |||
| { | |||
| $this->setLogContext($request); | |||
| try { | |||
| $validator = Validator::make($request->all(), $this->getRules()); | |||
| $validator->validate(); | |||
| } catch (ValidationException $e) { | |||
| logger("validate error", ['errors' => $e->errors(), 'request' => $request->all(), 'path' => $request->path()]); | |||
| return $this->validateErrorResponse($e); | |||
| } | |||
| try { | |||
| $this->validated = $validator->validated(); | |||
| $this->getParam()->setData($this->validated); | |||
| $this->authorize(); | |||
| return $this->run($request); | |||
| } catch (GeneralErrorMessageException $e) { | |||
| return $this->failedResponse([], $e->getMessage()); | |||
| } catch (AppCommonException $e) { | |||
| logs()->error(sprintf("Appエラー:%s", $e->getMessage())); | |||
| return $this->failedResponse(); | |||
| } catch (LogicException $e) { | |||
| logs()->error([ | |||
| sprintf("実装エラー:%s", $e->getMessage()), | |||
| get_class($e), | |||
| $e->getFile(), | |||
| $e->getLine(), | |||
| $request->all(), | |||
| ]); | |||
| logger(array_filter($e->getTrace(), function ($val, $key) { | |||
| return $key <= 5; | |||
| }, ARRAY_FILTER_USE_BOTH)); | |||
| return $this->failedResponse(); | |||
| } catch (ValidationException $e) { | |||
| return $this->validateErrorResponse($e); | |||
| } catch (HttpException $e) { | |||
| if ($e->getStatusCode() === 401) { | |||
| return $this->unAuthorizedResponse(); | |||
| } | |||
| throw e; | |||
| } catch (Exception $e) { | |||
| logs()->error([ | |||
| sprintf("例外エラー:%s", $e->getMessage()), | |||
| get_class($e), | |||
| $e->getFile(), | |||
| $e->getLine(), | |||
| $request->all(), | |||
| ]); | |||
| logger(array_filter($e->getTrace(), function ($val, $key) { | |||
| return $key <= 5; | |||
| }, ARRAY_FILTER_USE_BOTH)); | |||
| return $this->failedResponse(); | |||
| } | |||
| } | |||
| protected function successResponse(array|object $data = [], array|string $messages = []) | |||
| { | |||
| return $this->setData($data) | |||
| ->setMessages($messages) | |||
| ->setResultCode(ResultCode::SECCESS) | |||
| ->makeResponse(); | |||
| } | |||
| protected function failedResponse(array|object $data = [], array|string $messages = []) | |||
| { | |||
| return $this->setData($data) | |||
| ->setMessages($messages) | |||
| ->setResultCode(ResultCode::FAILED) | |||
| ->makeResponse(); | |||
| } | |||
| protected function unAuthorizedResponse(array|object $data = [], array|string $messages = []) | |||
| { | |||
| return $this->setData($data) | |||
| ->setMessages($messages) | |||
| ->setResultCode(ResultCode::UNAUTHORIZED) | |||
| ->makeResponse(); | |||
| } | |||
| protected function exclusiveErrorResponse(array|object $data = [], array|string $messages = []) | |||
| { | |||
| return $this->setData($data) | |||
| ->setMessages($messages) | |||
| ->setResultCode(ResultCode::EXCLUSIVE_ERROR) | |||
| ->makeResponse(); | |||
| } | |||
| protected function validateErrorResponse(ValidationException|array $exception, string|null $generalMessage = null) | |||
| { | |||
| $errorMessages = []; | |||
| $general = null; | |||
| if ($exception instanceof ValidationException) { | |||
| foreach ($exception->errors() as $key => $m) { | |||
| $errorMessages[$key] = $m[0]; | |||
| } | |||
| } | |||
| if (is_array($exception)) { | |||
| $errorMessages = $exception; | |||
| } | |||
| $general = $generalMessage ?? data_get($errorMessages, self::COL_NAME_GENERAL_MESSAGE); | |||
| return $this->setData([]) | |||
| ->setMessages($errorMessages) | |||
| ->setGeneralMessage($general) | |||
| ->setResultCode(ResultCode::FAILED) | |||
| ->makeResponse(); | |||
| } | |||
| private function makeResponse() | |||
| { | |||
| if ($this->resultCode === null) { | |||
| abort(403); | |||
| } | |||
| $ret = []; | |||
| Arr::set($ret, self::COL_NAME_RESULT_CODE, $this->resultCode->value); | |||
| if ($this->data !== null) { | |||
| Arr::set($ret, self::COL_NAME_DATA, $this->data); | |||
| } | |||
| if ($this->messages !== null) { | |||
| Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_ERRORS, $this->messages); | |||
| } | |||
| if ($this->generalMessage !== null) { | |||
| Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_GENERAL_MESSAGE, $this->generalMessage); | |||
| } | |||
| if ($this->emailId !== null) { | |||
| Arr::set($ret, self::COL_NAME_MESSAGES . "." . self::COL_NAME_EMAIL_ID, $this->emailId); | |||
| } | |||
| return response() | |||
| ->json($ret) | |||
| ->withHeaders($this->makeHeader()); | |||
| } | |||
| private function makeHeader(): array | |||
| { | |||
| $header = []; | |||
| $user = Auth::user(); | |||
| if ($user) { | |||
| $header["App-User-Auth"] = sprintf("%d,%d", $user->id, $user->role->value); | |||
| } else { | |||
| $header["App-User-Auth"] = 'none'; | |||
| } | |||
| return $header; | |||
| } | |||
| // 以下 認可関係 | |||
| protected array|null $roleAllow = null; | |||
| protected array|null $roleDisallow = null; | |||
| protected function roleAllow(UserRole $role) | |||
| { | |||
| $this->roleAllow = []; | |||
| foreach (UserRole::cases() as $ele) { | |||
| if ($role->value <= $ele->value) { | |||
| $this->roleAllow[] = $ele; | |||
| } | |||
| } | |||
| } | |||
| private function authorize() | |||
| { | |||
| if (!Auth::check()) { | |||
| return; | |||
| } | |||
| $role = Auth::user()->role; | |||
| if (!$this->canAccess($role)) { | |||
| abort(401); | |||
| } | |||
| } | |||
| public function canAccess(UserRole $role) | |||
| { | |||
| if (is_array($this->roleAllow) && !in_array($role, $this->roleAllow)) { | |||
| return false; | |||
| } | |||
| if (is_array($this->roleDisallow) && in_array($role, $this->roleDisallow)) { | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| // 返却用データの登録 | |||
| protected function setEmailId(int $emailId) | |||
| { | |||
| $this->emailId = $emailId; | |||
| return $this; | |||
| } | |||
| protected function setMessages(array|string $messages) | |||
| { | |||
| if (is_array($messages)) { | |||
| $this->messages = $messages; | |||
| } else { | |||
| $this->setGeneralMessage($messages); | |||
| } | |||
| return $this; | |||
| } | |||
| protected function setGeneralMessage(string|null $generalMessage) | |||
| { | |||
| $this->generalMessage = $generalMessage; | |||
| return $this; | |||
| } | |||
| protected function setData($data) | |||
| { | |||
| $this->data = $data; | |||
| return $this; | |||
| } | |||
| protected function setResultCode(ResultCode $resultCode) | |||
| { | |||
| $this->resultCode = $resultCode; | |||
| return $this; | |||
| } | |||
| protected function setLogContext(Request $request) | |||
| { | |||
| Log::withContext([ | |||
| '__requestUuid__' => strval(Str::uuid()), | |||
| '__userId__' => Auth::id(), | |||
| '__path__' => $request->path(), | |||
| ]); | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| <?php | |||
| namespace App\Jobs\Mail; | |||
| use App\Codes\QueueName; | |||
| use App\Mail\Sender; | |||
| use App\Models\Email; | |||
| use Illuminate\Bus\Queueable; | |||
| use Illuminate\Contracts\Queue\ShouldQueue; | |||
| use Illuminate\Foundation\Bus\Dispatchable; | |||
| use Illuminate\Queue\InteractsWithQueue; | |||
| use Illuminate\Queue\SerializesModels; | |||
| class SimpleMail implements ShouldQueue | |||
| { | |||
| use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | |||
| private string $emailId; | |||
| /** | |||
| * Create a new job instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(Email $email) | |||
| { | |||
| $this->emailId = $email->id; | |||
| $this->onQueue(QueueName::EMAIL->value); | |||
| } | |||
| /** | |||
| * Execute the job. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function handle() | |||
| { | |||
| Sender::send($this->emailId); | |||
| } | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| <?php | |||
| namespace App\Jobs\SMS; | |||
| use App\Codes\QueueName; | |||
| use App\Logic\SMS\SMSManager; | |||
| use App\Models\SMSSendOrder; | |||
| use Illuminate\Bus\Queueable; | |||
| use Illuminate\Contracts\Queue\ShouldQueue; | |||
| use Illuminate\Foundation\Bus\Dispatchable; | |||
| use Illuminate\Queue\InteractsWithQueue; | |||
| use Illuminate\Queue\SerializesModels; | |||
| class SendSMS implements ShouldQueue | |||
| { | |||
| use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | |||
| /** | |||
| * Create a new job instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(private SMSSendOrder $order) | |||
| { | |||
| $this->onQueue(QueueName::SMS->value); | |||
| } | |||
| /** | |||
| * Execute the job. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function handle(SMSManager $manager) | |||
| { | |||
| $ret = $manager->setOrder($this->order) | |||
| ->send(); | |||
| if ($ret) { | |||
| info(sprintf( | |||
| "SMS送信依頼成功 address:%s order_id:%s", | |||
| $this->order->phone_number, | |||
| $this->order->id, | |||
| )); | |||
| } else { | |||
| $this->fail(sprintf("SMS送信依頼失敗 order_id:%s", $this->order->id)); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| <?php | |||
| namespace App\Listeners\Mail; | |||
| use App\Events\Mail\ConfirmEvent; | |||
| use App\Jobs\Mail\SimpleMail; | |||
| use App\Util\DateUtil; | |||
| class MailSendJobRegister | |||
| { | |||
| /** | |||
| * Create the event listener. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct() | |||
| { | |||
| } | |||
| /** | |||
| * Handle the event. | |||
| * | |||
| * @param ConfirmEvent $event | |||
| * @return void | |||
| */ | |||
| public function handle(ConfirmEvent $event) | |||
| { | |||
| $email = $event->email; | |||
| if ($email->confirm_datetime === null && $email->send_datetime === null) { | |||
| $email->confirm_datetime = DateUtil::now(); | |||
| $email->save(); | |||
| SimpleMail::dispatch($event->email); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Listeners\Model; | |||
| use App\Events\Model\CreatedEvent; | |||
| class CreatedListener extends ModelListener | |||
| { | |||
| protected const ACTION = '作成'; | |||
| public function handle(CreatedEvent $event): void | |||
| { | |||
| $this->handleEvent($event->model); | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Listeners\Model; | |||
| use App\Events\Model\DeletedEvent; | |||
| class DeletedListener extends ModelListener | |||
| { | |||
| protected const ACTION = '削除'; | |||
| public function handle(DeletedEvent $event): void | |||
| { | |||
| $this->handleEvent($event->model); | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Listeners\Model; | |||
| use App\Events\Model\DeletingEvent; | |||
| class DeletingListener extends ModelListener | |||
| { | |||
| protected const ACTION = '削除'; | |||
| public function handle(DeletingEvent $event): void | |||
| { | |||
| $this->handleEvent($event->model); | |||
| } | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| <?php | |||
| namespace App\Listeners\Model; | |||
| use App\Models\ColumnName; | |||
| use App\Models\Feature\IModelFeature; | |||
| use Illuminate\Database\Eloquent\Model; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use Illuminate\Support\Facades\Log; | |||
| abstract class ModelListener | |||
| { | |||
| protected const ACTION = '-'; | |||
| protected function handleEvent(IModelFeature $model): void | |||
| { | |||
| // 履歴作成処理 | |||
| $this->createHistory($model); | |||
| // 更新者作成者設定処理 | |||
| $this->setHandlerIds(); | |||
| } | |||
| protected function createHistory(IModelFeature $model) | |||
| { | |||
| $history = $model->getHistory(); | |||
| if ($history !== null) { | |||
| $history->fillFromOrigin($model); | |||
| $history->save(); | |||
| $changeMessage = ""; | |||
| if ($model instanceof Model) { | |||
| $before = $model->getOriginal(); | |||
| $after = $model; | |||
| $message = $model->getChangeLogMessage($before, $after); | |||
| if ($message !== null) { | |||
| $changeMessage = sprintf("[%s]", $message); | |||
| } | |||
| } | |||
| Log::info(sprintf( | |||
| "モデル変更検知[%s][%s][ID:%s]%s", | |||
| $model->getModelName(), | |||
| static::ACTION, | |||
| data_get($model, ColumnName::ID), | |||
| $changeMessage | |||
| )); | |||
| } | |||
| } | |||
| protected function setHandlerIds() | |||
| { | |||
| if (Auth::check()) { | |||
| $this->created_by = Auth::id(); | |||
| $this->updated_by = Auth::id(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Listeners\Model; | |||
| use App\Events\Model\UpdatingEvent; | |||
| class UpdatingListener extends ModelListener | |||
| { | |||
| protected const ACTION = '更新'; | |||
| public function handle(UpdatingEvent $event): void | |||
| { | |||
| $this->handleEvent($event->model); | |||
| } | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| <?php | |||
| namespace App\Listeners\SMS; | |||
| use App\Events\SMS\ConfirmEvent; | |||
| /** | |||
| * SMS送信依頼の確定イベントをキャッチ | |||
| * 領収証発行依頼のSMS送信完了を設定する | |||
| */ | |||
| class ConfirmListener | |||
| { | |||
| /** | |||
| * Create the event listener. | |||
| */ | |||
| public function __construct() | |||
| { | |||
| // | |||
| } | |||
| /** | |||
| * Handle the event. | |||
| */ | |||
| public function handle(ConfirmEvent $event): void | |||
| { | |||
| $smsSendOrder = $event->order; | |||
| $receiptIssuingOrder = $smsSendOrder->receiptIssuingOrder; | |||
| if ($receiptIssuingOrder === null) { | |||
| return; | |||
| } | |||
| $success = $smsSendOrder->send_datetime !== null; | |||
| if ($success) { | |||
| $receiptIssuingOrder->sms_send_success = true; | |||
| $receiptIssuingOrder->save(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,101 @@ | |||
| <?php | |||
| namespace App\Logic; | |||
| use App\Events\Mail\ConfirmEvent; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Mail\BaseMailer; | |||
| use App\Models\Email; | |||
| use Exception; | |||
| use Illuminate\Support\Carbon; | |||
| use Validator; | |||
| class EmailManager | |||
| { | |||
| private Email $model; | |||
| private bool $canSend = false; | |||
| public function __construct(int|BaseMailer $param) | |||
| { | |||
| if (is_numeric($param)) { | |||
| $this->model = Email::lockForUpdate()->findOrfail($param); | |||
| $this->canSend = $this->model->send_datetime === null; | |||
| if (!$this->checkAuth()) throw new AppCommonException("メール権限エラー"); | |||
| } else if ($param instanceof BaseMailer) { | |||
| $this->model = $param->makeModel(); | |||
| } | |||
| } | |||
| public function checkAuth() | |||
| { | |||
| return true; | |||
| } | |||
| public function getEmailId() | |||
| { | |||
| return $this->model->id; | |||
| } | |||
| public function getTimestamp(): Carbon|null | |||
| { | |||
| return $this->model->updated_at; | |||
| } | |||
| public function create() | |||
| { | |||
| $this->model->save(); | |||
| return []; | |||
| } | |||
| public function setSubject(string $subject) | |||
| { | |||
| if ($this->canSend()) { | |||
| $this->model->subject = $subject; | |||
| } else { | |||
| throw new AppCommonException("送信済みメール編集エラー"); | |||
| } | |||
| return $this; | |||
| } | |||
| public function setContent(string $content) | |||
| { | |||
| if ($this->canSend()) { | |||
| $this->model->content = $content; | |||
| } else { | |||
| throw new AppCommonException("送信済みメール編集エラー"); | |||
| } | |||
| return $this; | |||
| } | |||
| public function update() | |||
| { | |||
| $this->model->save(); | |||
| return []; | |||
| } | |||
| public function confirm() | |||
| { | |||
| $validator = Validator::make(['email' => $this->model->email], [ | |||
| 'email' => ['required', 'email:strict,dns'] | |||
| ]); | |||
| if ($validator->fails()) { | |||
| throw new Exception(sprintf("%s [%s]", $validator->errors()->first(), $this->model->email)); | |||
| } | |||
| if ($this->canSend() !== null) { | |||
| ConfirmEvent::dispatch($this->model); | |||
| } else { | |||
| throw new AppCommonException("送信済みエラー"); | |||
| } | |||
| } | |||
| public function canSend() | |||
| { | |||
| return $this->canSend; | |||
| } | |||
| } | |||
| @@ -0,0 +1,79 @@ | |||
| <?php | |||
| namespace App\Logic\ReceiptIssuingOrder; | |||
| use App\Codes\ReceiptIssuingOrderStatus; | |||
| use App\Codes\SMSSendPurpose; | |||
| use App\Logic\SMS\SMSManager; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| use Illuminate\Support\Facades\View; | |||
| use LogicException; | |||
| class CreateManager extends ReceiptIssuingOrderManager | |||
| { | |||
| public function __construct( | |||
| protected ReceiptIssuingOrder $order, | |||
| protected SMSManager $smsManager | |||
| ) { | |||
| } | |||
| public function init() | |||
| { | |||
| $order = $this->order; | |||
| $this->initialized = true; | |||
| return $this; | |||
| } | |||
| public function fill(array $attr) | |||
| { | |||
| $this->order->fill($attr); | |||
| return $this; | |||
| } | |||
| public function create(): array | |||
| { | |||
| $order = $this->order; | |||
| // パラメータチェック | |||
| $messages = $this->paramCheck(); | |||
| if (count($messages) !== 0) { | |||
| return $messages; | |||
| } | |||
| // モデル更新 | |||
| $order->status = ReceiptIssuingOrderStatus::CREATED; | |||
| $this->refreshToken(); | |||
| $contractId = $this->loginUser()->getContractId(); | |||
| if ($contractId === null) { | |||
| throw new LogicException("契約不良"); | |||
| } | |||
| $order->setContract($contractId) | |||
| ->save(); | |||
| // SMS配信 | |||
| $smsSendOrder = $this->smsManager::makeSMSSendOrder($order, SMSSendPurpose::SEND_RECEIPT_ISSUING_ORDER_FORM, $this->makeSMSContents()); | |||
| $smsSendOrder->send(); | |||
| return []; | |||
| } | |||
| private function paramCheck(): array | |||
| { | |||
| $ret = []; | |||
| $order = $this->order; | |||
| return $ret; | |||
| } | |||
| private function makeSMSContents(): string | |||
| { | |||
| return View::make('sms.announce_receipt_issusing_order_form', [ | |||
| 'url' => implode('/', [config('app.url'), 'receipt-issuing-order/create', $this->order->access_token]) | |||
| ])->render(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,54 @@ | |||
| <?php | |||
| namespace App\Logic\ReceiptIssuingOrder; | |||
| use App\Codes\ReceiptIssuingOrderStatus; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Logic\SMS\SMSManager; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| use PDF; | |||
| class UpdateManager extends ReceiptIssuingOrderManager | |||
| { | |||
| public function __construct( | |||
| protected ReceiptIssuingOrder $order, | |||
| protected SMSManager $smsManager | |||
| ) { | |||
| } | |||
| public function initByToken(string $token) | |||
| { | |||
| $ret = $this->checkToken($token); | |||
| if (!$ret) { | |||
| throw new AppCommonException("トークン不正"); | |||
| } | |||
| $this->initialized = true; | |||
| return $this; | |||
| } | |||
| public function initById(string $id) | |||
| { | |||
| $this->fetch($id); | |||
| $this->initialized = true; | |||
| return $this; | |||
| } | |||
| protected function service() | |||
| { | |||
| $order = $this->order; | |||
| $pdf = PDF::loadView('pdf', $order); | |||
| // はがきサイズを指定 | |||
| $ret = $pdf->setOption('page-height', 148) | |||
| ->setOption('page-width', 100) | |||
| ->setOption('encoding', 'utf-8') | |||
| ->inline(); | |||
| $order->status = ReceiptIssuingOrderStatus::DOWNLOAD_DONE; | |||
| return $ret; | |||
| } | |||
| } | |||
| @@ -0,0 +1,73 @@ | |||
| <?php | |||
| namespace App\Logic\ReceiptIssuingOrder; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Features\LoginUser; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| use App\Util\DateUtil; | |||
| use Illuminate\Support\Str; | |||
| use LogicException; | |||
| abstract class ReceiptIssuingOrderManager | |||
| { | |||
| use LoginUser; | |||
| protected bool $initialized = false; | |||
| protected function __construct(protected ReceiptIssuingOrder $order) | |||
| { | |||
| } | |||
| protected function fetch(string $receiptIssuingOrderId) | |||
| { | |||
| $order = ReceiptIssuingOrder::findOrFail($receiptIssuingOrderId); | |||
| if (!$this->loginUser()->checkAuthorization($order)) { | |||
| throw new AppCommonException("認可不良"); | |||
| } | |||
| $this->order = $order; | |||
| } | |||
| protected function refreshToken() | |||
| { | |||
| $order = $this->order; | |||
| $order->access_token = base64_encode(Str::uuid()); | |||
| $order->access_token_expires_at = DateUtil::now()->adddays(7); | |||
| } | |||
| protected function checkToken(string $token): bool | |||
| { | |||
| if ($this->order->isNotSavedModel()) { | |||
| throw new LogicException("初期化不良"); | |||
| } | |||
| $order = ReceiptIssuingOrder::whereAccessToken($token) | |||
| ->first(); | |||
| if (!($order instanceof ReceiptIssuingOrder)) { | |||
| return false; | |||
| } | |||
| if ($order->access_token_expires_at === null) { | |||
| return false; | |||
| } | |||
| if ($order->access_token !== $token) { | |||
| return false; | |||
| } | |||
| $now = DateUtil::now(); | |||
| $ret = $now->lt($this->order->access_token_expires_at); | |||
| if (!$ret) { | |||
| return false; | |||
| } | |||
| $this->order = $order; | |||
| return true; | |||
| } | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| <?php | |||
| namespace App\Logic\ReceiptIssuingOrder; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Logic\SMS\SMSManager; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| class UpdateManager extends ReceiptIssuingOrderManager | |||
| { | |||
| public function __construct( | |||
| protected ReceiptIssuingOrder $order, | |||
| protected SMSManager $smsManager | |||
| ) { | |||
| } | |||
| public function initByToken(string $token) | |||
| { | |||
| $ret = $this->checkToken($token); | |||
| if (!$ret) { | |||
| throw new AppCommonException("トークン不正"); | |||
| } | |||
| $this->initialized = true; | |||
| return $this; | |||
| } | |||
| public function initById(string $id) | |||
| { | |||
| $this->fetch($id); | |||
| $this->initialized = true; | |||
| return $this; | |||
| } | |||
| public function fill(array $attr) | |||
| { | |||
| $this->order->fill($attr); | |||
| return $this; | |||
| } | |||
| protected function service() | |||
| { | |||
| $order = $this->order; | |||
| // パラメータチェック | |||
| $messages = $this->paramCheck(); | |||
| if (count($messages) !== 0) { | |||
| return $messages; | |||
| } | |||
| // モデル更新 | |||
| $order->save(); | |||
| return []; | |||
| } | |||
| private function paramCheck(): array | |||
| { | |||
| $ret = []; | |||
| $order = $this->order; | |||
| return $ret; | |||
| } | |||
| } | |||
| @@ -0,0 +1,329 @@ | |||
| <?php | |||
| namespace App\Logic\SMS; | |||
| use App\Codes\SMSProviderName; | |||
| use App\Codes\SMSSendPurpose; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| use App\Models\SMSProvider; | |||
| use App\Models\SMSProviderFourSMessageCommunication; | |||
| use App\Models\SMSSendOrder; | |||
| use App\Util\DateUtil; | |||
| use Exception; | |||
| use Illuminate\Http\Client\Response; | |||
| use Illuminate\Support\Collection; | |||
| use Illuminate\Support\Facades\Http; | |||
| use LogicException; | |||
| class FourSMessageManager implements SMSManager | |||
| { | |||
| private const SEND_URL = ['POST', "https://4sm.jp/api/sms_send"]; | |||
| private const CANCEL_URL = ['POST', "https://4sm.jp/api/sms_cancel"]; | |||
| private const RESULT_URL = ['POST', "https://4sm.jp/api/get_send_result"]; | |||
| private static function getUserId(): string | |||
| { | |||
| $ret = config("logic.sms.FourSMessage.userId"); | |||
| if ($ret === null) { | |||
| throw new LogicException("SMS FourSMessage UserID 未定義"); | |||
| } | |||
| return $ret; | |||
| } | |||
| private static function getPassword(): string | |||
| { | |||
| $ret = config("logic.sms.FourSMessage.password"); | |||
| if ($ret === null) { | |||
| throw new LogicException("SMS FourSMessage Password 未定義"); | |||
| } | |||
| return $ret; | |||
| } | |||
| public static function makeSMSSendOrder(ReceiptIssuingOrder $receiptIssuingOrder, SMSSendPurpose $purpose, string $contents): SMSSendOrder | |||
| { | |||
| $order = new SMSSendOrder(); | |||
| $order->receipt_issuing_order_id = $receiptIssuingOrder->id; | |||
| $order->contract_id = $receiptIssuingOrder->contract_id; | |||
| $order->phone_number = $receiptIssuingOrder->sms_phone_number; | |||
| $order->sumamry_key1 = $receiptIssuingOrder->sumamry_key1; | |||
| $order->sumamry_key2 = $receiptIssuingOrder->sumamry_key2; | |||
| $order->purpose = $purpose; | |||
| $order->content = $contents; | |||
| return $$order; | |||
| } | |||
| public function __construct( | |||
| private SMSSendOrder $order, | |||
| ) { | |||
| } | |||
| public function loadOrder(string $id): static | |||
| { | |||
| $this->order = SMSSendOrder::findOrFail($id); | |||
| return $this; | |||
| } | |||
| public function setOrder(SMSSendOrder $order): static | |||
| { | |||
| $this->order = $order; | |||
| return $this; | |||
| } | |||
| public function getOrder() | |||
| { | |||
| return $this->order; | |||
| } | |||
| public function send(): bool | |||
| { | |||
| $sendContents = [ | |||
| 'cp_userid' => static::getUserId(), | |||
| 'cp_password' => static::getPassword(), | |||
| 'carrier_id' => '99', | |||
| 'message' => $this->order->content, | |||
| 'address' => $this->order->phone_number, | |||
| 'urlshorterflg' => '1', | |||
| 'send_date' => '202305120000', | |||
| ]; | |||
| $this->order->sms_provider_id = CommunicationData::getMst()->id; | |||
| $res = $this->communication(self::SEND_URL, $sendContents); | |||
| if ($res->isSuccess()) { | |||
| // 送信依頼を作成 | |||
| $this->order->save(); | |||
| $comm = new SMSProviderFourSMessageCommunication(); | |||
| $comm->sms_send_order_id = $this->order->id; | |||
| $comm->contract_id = $this->order->contract_id; | |||
| $comm->request_id = $res->requestId; | |||
| $comm->request_date = $res->requestDate; | |||
| $comm->save(); | |||
| return true; | |||
| } else { | |||
| return false; | |||
| } | |||
| } | |||
| public function poll(): bool | |||
| { | |||
| $requestId = SMSProviderFourSMessageCommunication::whereSmsSendOrderId($this->order->id) | |||
| ->firstOrFail() | |||
| ->request_id; | |||
| $sendContents = [ | |||
| 'request_id' => $requestId, | |||
| 'user_id' => static::getUserId(), | |||
| 'password' => base64_encode(static::getPassword()), | |||
| ]; | |||
| $res = $this->communication(self::RESULT_URL, $sendContents); | |||
| if ($res->isSuccess()) { | |||
| $res->save(); | |||
| } | |||
| return $res->isSuccess(); | |||
| } | |||
| public function cancel(): bool | |||
| { | |||
| $requestId = SMSProviderFourSMessageCommunication::whereSmsSendOrderId($this->order->id) | |||
| ->firstOrFail() | |||
| ->request_id; | |||
| $sendContents = [ | |||
| 'request_id' => $requestId, | |||
| 'cp_userid' => static::getUserId(), | |||
| 'cp_password' => static::getPassword(), | |||
| ]; | |||
| $res = $this->communication(self::CANCEL_URL, $sendContents); | |||
| return $res->isSuccess(); | |||
| } | |||
| private function communication(array $urlDef, array $sendData): CommunicationData | |||
| { | |||
| $method = $urlDef[0]; | |||
| $form = Http::withHeaders([ | |||
| 'Content-Type' => 'application/x-www-form-urlencoded', | |||
| ]) | |||
| ->asForm(); | |||
| if ($method === 'GET') { | |||
| $query = ""; | |||
| foreach ($sendData as $key => $value) { | |||
| if ($query !== "") { | |||
| $query .= '&'; | |||
| } | |||
| $query .= $key . "=" . $value; | |||
| } | |||
| $url = $urlDef[1] . '?' . $query; | |||
| $res = $form->get($url); | |||
| } else { | |||
| $url = $urlDef[1]; | |||
| $res = $form->post($url, $sendData); | |||
| } | |||
| logger(['SMS-SendContet' => $sendData, 'URL' => $url]); | |||
| if (!$res->ok()) { | |||
| $res->throw(); | |||
| } | |||
| logger(['SMS-ReveiveContet' => $res->json()]); | |||
| $ret = new CommunicationData($res, $this->order); | |||
| return $ret; | |||
| } | |||
| } | |||
| class CommunicationData | |||
| { | |||
| public string|null $result; | |||
| public string|null $requestId; | |||
| public string|null $requestDate; | |||
| public string|null $errorCode; | |||
| public string|null $errorMessage; | |||
| /** | |||
| * @var Collection<SMSProviderFourSMessageCommunication> | |||
| */ | |||
| public Collection $data; | |||
| private SMSSendOrder $order; | |||
| private const RESULT_SUCCESS = 'SUCCESS'; | |||
| private const DATE_FORMAT = 'YmdHi'; | |||
| public function __construct(Response $res, SMSSendOrder $order) | |||
| { | |||
| $this->order = $order; | |||
| $data = $res->json(); | |||
| $response = data_get($data, 'response'); | |||
| if ($response !== null) { | |||
| $data = $response; | |||
| } | |||
| $this->result = data_get($data, 'result'); | |||
| $this->requestId = data_get($data, 'request_id'); | |||
| $this->requestDate = data_get($data, 'request_date'); | |||
| $this->errorCode = data_get($data, 'error_code'); | |||
| $this->errorMessage = data_get($data, 'error_message'); | |||
| $this->data = collect(); | |||
| $records = data_get($data, 'records'); | |||
| if (is_array($records)) { | |||
| foreach ($records as $record) { | |||
| $requestId = data_get($record, 'request_id'); | |||
| // 空文字の場合があるので、数値にキャストする | |||
| $record['success_count'] = intVal($record['success_count']); | |||
| $ele = SMSProviderFourSMessageCommunication::whereRequestId($requestId) | |||
| ->firstOrFail(); | |||
| $ele->fill($record); | |||
| $ele->sms_send_order_id = $order->id; | |||
| $this->data->push($ele); | |||
| } | |||
| } | |||
| } | |||
| public function isSuccess(): bool | |||
| { | |||
| return $this->result === self::RESULT_SUCCESS; | |||
| } | |||
| public function save() | |||
| { | |||
| foreach ($this->data as $data) { | |||
| $data->save(); | |||
| if ($this->isFixRecord($data)) { | |||
| $this->order->done = true; | |||
| if ($data->deliv_rcpt_date !== null) { | |||
| $this->order->send_datetime = DateUtil::parse($data->deliv_rcpt_date, self::DATE_FORMAT); | |||
| } | |||
| if ($data->success_count > 0) { | |||
| $this->order->cost = $this->calcCost($data); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| public function isFixRecord(SMSProviderFourSMessageCommunication $comm): bool | |||
| { | |||
| // 以下のコードになれば「状態確定」のタイミングであると判断できます。 | |||
| // ・受付状態 : 90受付取消、または99受付失敗 | |||
| // ・送信エラー: 31~35送信エラー、または40送達済 | |||
| if (in_array($comm->request_status, ['90', '99'])) { | |||
| return true; | |||
| } | |||
| if (in_array($comm->sending_status, [ | |||
| '31', | |||
| '32', | |||
| '33', | |||
| '34', | |||
| '35', | |||
| '40', | |||
| ])) { | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| private function calcCost(SMSProviderFourSMessageCommunication $comm): int | |||
| { | |||
| $provider = self::getMst(); | |||
| return $comm->success_count * $provider->cost; | |||
| } | |||
| private static function getMst(): SMSProvider | |||
| { | |||
| static $provider = null; | |||
| if ($provider === null) { | |||
| $provider = SMSProvider::whereProviderName(SMSProviderName::FOUR_S_MESSAGE->value) | |||
| ->firstOrFail(); | |||
| } | |||
| return $provider; | |||
| } | |||
| } | |||
| @@ -0,0 +1,91 @@ | |||
| <?php | |||
| namespace App\Logic\SMS; | |||
| use App\Codes\SMSProviderName; | |||
| use App\Codes\SMSSendPurpose; | |||
| use App\Events\SMS\ConfirmEvent; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| use App\Models\SMSProvider; | |||
| use App\Models\SMSSendOrder; | |||
| use App\Util\DateUtil; | |||
| class LogManager implements SMSManager | |||
| { | |||
| public static function makeSMSSendOrder(ReceiptIssuingOrder $receiptIssuingOrder, SMSSendPurpose $purpose, string $contents): SMSSendOrder | |||
| { | |||
| $order = new SMSSendOrder(); | |||
| $order->receipt_issuing_order_id = $receiptIssuingOrder->id; | |||
| $order->contract_id = $receiptIssuingOrder->contract_id; | |||
| $order->phone_number = $receiptIssuingOrder->sms_phone_number; | |||
| $order->sumamry_key1 = $receiptIssuingOrder->sumamry_key1; | |||
| $order->sumamry_key2 = $receiptIssuingOrder->sumamry_key2; | |||
| $order->purpose = $purpose; | |||
| $order->content = $contents; | |||
| $order->sms_provider_id = static::getMst()->id; | |||
| return $order; | |||
| } | |||
| public function __construct( | |||
| private SMSSendOrder $order, | |||
| ) { | |||
| } | |||
| public function loadOrder(string $id): static | |||
| { | |||
| $this->order = SMSSendOrder::findOrFail($id); | |||
| return $this; | |||
| } | |||
| public function setOrder(SMSSendOrder $order): static | |||
| { | |||
| $this->order = $order; | |||
| return $this; | |||
| } | |||
| public function getOrder() | |||
| { | |||
| return $this->order; | |||
| } | |||
| public function send(): bool | |||
| { | |||
| $this->order->send_datetime = DateUtil::now(); | |||
| $this->order->done = true; | |||
| $this->order->save(); | |||
| info(sprintf("SMS送信ダミー:<<%s>>", $this->order->content)); | |||
| return true; | |||
| } | |||
| public function poll(): bool | |||
| { | |||
| return true; | |||
| } | |||
| public function cancel(): bool | |||
| { | |||
| return true; | |||
| } | |||
| private static function getMst(): SMSProvider | |||
| { | |||
| static $provider = null; | |||
| if ($provider === null) { | |||
| $provider = SMSProvider::whereProviderName(SMSProviderName::LOG->value) | |||
| ->firstOrFail(); | |||
| } | |||
| return $provider; | |||
| } | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| <?php | |||
| namespace App\Logic\SMS; | |||
| use App\Codes\SMSSendPurpose; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| use App\Models\SMSSendOrder; | |||
| interface SMSManager | |||
| { | |||
| public static function makeSMSSendOrder(ReceiptIssuingOrder $order, SMSSendPurpose $purpose, string $contents): SMSSendOrder; | |||
| public function loadOrder(string $id): static; | |||
| public function setOrder(SMSSendOrder $order): static; | |||
| public function getOrder(); | |||
| public function send(): bool; | |||
| public function poll(): bool; | |||
| public function cancel(): bool; | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Mail\Admins; | |||
| use App\Mail\BaseMailer; | |||
| abstract class Admin extends BaseMailer | |||
| { | |||
| public function getParams(): array | |||
| { | |||
| return array_merge($this->getAdminParams(), []); | |||
| } | |||
| abstract public function getAdminParams(): array; | |||
| } | |||
| @@ -0,0 +1,92 @@ | |||
| <?php | |||
| namespace App\Mail\Admins; | |||
| use App\Models\Parking; | |||
| use App\Models\SeasonTicketContract; | |||
| use App\Models\SeasonTicketContractSubscription; | |||
| use App\Models\User; | |||
| use App\Models\UserDetail; | |||
| use Illuminate\Support\Carbon; | |||
| class Ask extends Admin | |||
| { | |||
| protected string $askSubject; | |||
| protected string $askContents; | |||
| protected string|null $parkName = null; | |||
| protected string|null $userName; | |||
| protected string|null $userEmail; | |||
| protected int|null $seasonTicketSeqNo = null; | |||
| protected Carbon|null $subscribeDatetime = null; | |||
| public function __construct( | |||
| array $data, | |||
| User|null $user = null, | |||
| UserDetail|null $userDetail = null, | |||
| SeasonTicketContract|null $seasonTicketContract = null, | |||
| SeasonTicketContractSubscription|null $subscription = null, | |||
| Parking|null $parking = null, | |||
| ) { | |||
| $this->setValues($data); | |||
| if ($seasonTicketContract) { | |||
| $this->seasonTicketSeqNo = $seasonTicketContract->season_ticket_seq_no; | |||
| } | |||
| if ($subscription) { | |||
| $this->subscribeDatetime = $subscription->subscribe_datetime; | |||
| } | |||
| if ($user) { | |||
| $this->userEmail = $user->email; | |||
| } | |||
| if ($userDetail) { | |||
| $this->userName = sprintf( | |||
| "%s %s", | |||
| $userDetail->first_name, | |||
| $userDetail->last_name | |||
| ); | |||
| } | |||
| if ($parking) { | |||
| $this->parkName = $parking->park_name; | |||
| } | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.admins.ask'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return sprintf( | |||
| '【スマートパーキングパス】【問い合わせ】【%s】%s', | |||
| $this->userName, | |||
| $this->askSubject | |||
| ); | |||
| } | |||
| private function memberRegistration(): string | |||
| { | |||
| if ($this->seasonTicketSeqNo !== null || $this->subscribeDatetime !== null) { | |||
| return "あり"; | |||
| } | |||
| return "なし"; | |||
| } | |||
| public function getAdminParams(): array | |||
| { | |||
| return [ | |||
| 'askSubject' => $this->askSubject, | |||
| 'askContents' => $this->askContents, | |||
| 'parkName' => $this->parkName, | |||
| 'userName' => $this->userName, | |||
| 'userEmail' => $this->userEmail, | |||
| 'seasonTicketSeqNo' => $this->seasonTicketSeqNo, | |||
| 'subscribeDatetime' => $this->subscribeDatetime ? $this->subscribeDatetime->format('Y/m/d H:i:s') : null, | |||
| 'memberRegistration' => $this->memberRegistration(), | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,177 @@ | |||
| <?php | |||
| namespace App\Mail; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Models\Email; | |||
| use App\Models\User; | |||
| use App\Util\DateUtil; | |||
| use Illuminate\Bus\Queueable; | |||
| use Illuminate\Mail\Mailable; | |||
| use Illuminate\Queue\SerializesModels; | |||
| use Illuminate\Support\Facades\Mail; | |||
| use Illuminate\Support\Str; | |||
| abstract class BaseMailer extends Mailable | |||
| { | |||
| use Queueable, SerializesModels; | |||
| protected $configRoot = 'mail.mailers.smtp_ht.'; | |||
| protected $casts = []; | |||
| protected User|null $__user = null; | |||
| protected string $__email = ""; | |||
| protected string|null $__userId = null; | |||
| protected string|null $__contractId = null; | |||
| protected string|null $__receiptIssuingOrderId = null; | |||
| public function sendEmail(string $email) | |||
| { | |||
| $this->__email = $email; | |||
| Mail::to($email) | |||
| ->send($this); | |||
| } | |||
| public function setUser(User $user) | |||
| { | |||
| $this->__userId = $user->id; | |||
| $this->__email = $user->email; | |||
| return $this; | |||
| } | |||
| public function setEmail(string $target) | |||
| { | |||
| $this->__email = $target; | |||
| return $this; | |||
| } | |||
| public function setUserId(string $userId) | |||
| { | |||
| $this->__userId = $userId; | |||
| return $this; | |||
| } | |||
| public function setContractId(string $contractId) | |||
| { | |||
| $this->__contractId = $contractId; | |||
| return $this; | |||
| } | |||
| public function setReceiptIssuingOrderId(string $receiptIssuingOrderId) | |||
| { | |||
| $this->__receiptIssuingOrderId = $receiptIssuingOrderId; | |||
| return $this; | |||
| } | |||
| public function build() | |||
| { | |||
| return $this->text($this->getTemplateName()) | |||
| ->subject($this->getSubject()) | |||
| ->with($this->getParams()); | |||
| } | |||
| public function setConfigDefault() | |||
| { | |||
| $root = $this->configRoot; | |||
| config([ | |||
| $root . "transport" => "smtp", | |||
| $root . "host" => env('MAIL_HOST', 'smtp.mailgun.org'), | |||
| $root . 'port' => env('MAIL_PORT', 587), | |||
| $root . 'encryption' => env('MAIL_ENCRYPTION', 'tls'), | |||
| $root . 'username' => env('MAIL_USERNAME'), | |||
| $root . 'password' => env('MAIL_PASSWORD'), | |||
| $root . 'timeout' => null, | |||
| $root . 'auth_mode' => null, | |||
| $root . 'verify_peer' => false, | |||
| ]); | |||
| } | |||
| public function setConfig(array $config) | |||
| { | |||
| $root = $this->configRoot; | |||
| $ret = []; | |||
| foreach ($config as $key => $val) { | |||
| $ret[$root . $key] = $val; | |||
| } | |||
| config($ret); | |||
| } | |||
| protected function setValues(\stdClass|array $data) | |||
| { | |||
| foreach ($data as $key => $val) { | |||
| $this->setValue($key, $val); | |||
| } | |||
| } | |||
| protected function setValue(string $key, $value) | |||
| { | |||
| $val = $value; | |||
| $camel = Str::camel($key); | |||
| $snake = Str::snake($key); | |||
| $property = ""; | |||
| if (property_exists($this, $camel)) { | |||
| $property = $camel; | |||
| } else if (property_exists($this, $snake)) { | |||
| $property = $snake; | |||
| } else { | |||
| return; | |||
| } | |||
| if (data_get($this->casts, $property) === 'datetime') { | |||
| $this->$property = DateUtil::parse($val); | |||
| } else { | |||
| $this->$property = $val; | |||
| } | |||
| } | |||
| public function makeModel(): Email | |||
| { | |||
| if ($this->__email === "") { | |||
| throw new AppCommonException("Email宛先不明"); | |||
| } | |||
| $model = new Email(); | |||
| $model->subject = $this->getSubject(); | |||
| $model->content = $this->render(); | |||
| $model->type = get_class($this); | |||
| $model->email = $this->__email; | |||
| $model->contract_id = $this->__contractId; | |||
| $model->user_id = $this->__userId; | |||
| $model->receipt_issuing_order_id = $this->__receiptIssuingOrderId; | |||
| return $model; | |||
| } | |||
| abstract public function getTemplateName(): string; | |||
| abstract public function getSubject(): string; | |||
| abstract public function getParams(): array; | |||
| /** | |||
| * 画面のURLを生成する | |||
| * | |||
| * @param array|string $path | |||
| * @return string | |||
| */ | |||
| protected function getAppUrl(array|string $path): string | |||
| { | |||
| $elements = [config("app.url")]; | |||
| if (is_array($path)) { | |||
| $elements = array_merge($elements, $path); | |||
| } else { | |||
| $elements[] = $path; | |||
| } | |||
| return implode( | |||
| "/", | |||
| $elements, | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| <?php | |||
| namespace App\Mail\Common; | |||
| use App\Models\Parking; | |||
| use App\Models\User; | |||
| use App\Models\UserDetail; | |||
| use Illuminate\Support\Carbon; | |||
| trait AskConfirmation | |||
| { | |||
| protected string $askSubject; | |||
| protected string $askContents; | |||
| protected string|null $parkName = null; | |||
| protected string|null $userName; | |||
| protected string|null $userEmail; | |||
| protected int|null $seasonTicketSeqNo = null; | |||
| protected Carbon|null $subscribeDatetime = null; | |||
| private bool $forMember = false; | |||
| abstract protected function setValues(\stdClass|array $data); | |||
| protected function setAskConfirmationData( | |||
| array $data, | |||
| User|null $user = null, | |||
| UserDetail|null $userDetail = null, | |||
| Parking|null $parking = null, | |||
| ) { | |||
| $this->setValues($data); | |||
| if ($user) { | |||
| $this->userEmail = $user->email; | |||
| $this->forMember = true; | |||
| } | |||
| if ($userDetail) { | |||
| $this->userName = sprintf( | |||
| "%s %s", | |||
| $userDetail->first_name, | |||
| $userDetail->last_name | |||
| ); | |||
| $this->forMember = true; | |||
| } | |||
| if ($parking) { | |||
| $this->parkName = $parking->park_name; | |||
| } | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return $this->forMember ? 'mails.members.ask_confirmation' : 'mails.guests.ask_confirmation'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】お問い合わせ内容の確認'; | |||
| } | |||
| protected function getAskConfirmationParams(): array | |||
| { | |||
| return [ | |||
| 'askSubject' => $this->askSubject, | |||
| 'askContents' => $this->askContents, | |||
| 'parkName' => $this->parkName, | |||
| 'userName' => $this->userName, | |||
| 'userEmail' => $this->userEmail, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| <?php | |||
| namespace App\Mail\Guests; | |||
| use App\Mail\Common\AskConfirmation as CommonAskConfirmation; | |||
| use App\Models\Parking; | |||
| use App\Models\User; | |||
| use App\Models\UserDetail; | |||
| class AskConfirmation extends Guest | |||
| { | |||
| use CommonAskConfirmation; | |||
| public function __construct( | |||
| array $data, | |||
| User|null $user = null, | |||
| UserDetail|null $userDetail = null, | |||
| Parking|null $parking = null, | |||
| ) { | |||
| $this->setAskConfirmationData($data, $user, $userDetail, $parking); | |||
| } | |||
| public function getGuestParams(): array | |||
| { | |||
| return $this->getAskConfirmationParams(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,63 @@ | |||
| <?php | |||
| namespace App\Mail\Guests; | |||
| use App\Models\ChangeEmail; | |||
| use Exception; | |||
| use Illuminate\Support\Carbon; | |||
| class ChangeEmailStart extends Guest | |||
| { | |||
| protected string $token; | |||
| protected Carbon $expiresAt; | |||
| protected $casts = [ | |||
| 'expiresAt' => 'datetime', | |||
| ]; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(ChangeEmail $model) | |||
| { | |||
| logger($model->toArray()); | |||
| $this->setValues($model->toArray()); | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.guests.change_email_start'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】Email変更手続きのご案内'; | |||
| } | |||
| public function getGuestParams(): array | |||
| { | |||
| return [ | |||
| 'url' => $this->getUrl(), | |||
| 'expiresAt' => $this->getExpiresAt(), | |||
| ]; | |||
| } | |||
| private function getUrl() | |||
| { | |||
| return implode( | |||
| "/", | |||
| [ | |||
| config("app.url"), | |||
| 'change-email', | |||
| $this->token | |||
| ] | |||
| ); | |||
| } | |||
| private function getExpiresAt() | |||
| { | |||
| return $this->expiresAt->format('Y/m/d H:i'); | |||
| } | |||
| } | |||
| @@ -0,0 +1,65 @@ | |||
| <?php | |||
| namespace App\Mail\Guests; | |||
| use App\Models\EmailVerify as ModelsEmailVerify; | |||
| use Exception; | |||
| use Illuminate\Support\Carbon; | |||
| class EmailVerify extends Guest | |||
| { | |||
| protected string $token; | |||
| protected Carbon $expiresAt; | |||
| protected $casts = [ | |||
| 'expiresAt' => 'datetime', | |||
| ]; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(ModelsEmailVerify $model) | |||
| { | |||
| $this->setValues($model->toArray()); | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.guests.email_verify'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】会員登録のご案内'; | |||
| } | |||
| public function getGuestParams(): array | |||
| { | |||
| return [ | |||
| 'url' => $this->getUrl(), | |||
| 'expiresAt' => $this->getExpiresAt(), | |||
| ]; | |||
| } | |||
| private function getUrl() | |||
| { | |||
| return implode( | |||
| "/", | |||
| [ | |||
| config("app.url"), | |||
| 'register/user', | |||
| $this->token | |||
| ] | |||
| ); | |||
| } | |||
| private function getExpiresAt() | |||
| { | |||
| $date = $this->expiresAt; | |||
| if ($date !== null) { | |||
| return $date->format('Y/m/d H:i'); | |||
| } | |||
| throw new Exception("有効期限日付不正"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Mail\Guests; | |||
| use App\Mail\BaseMailer; | |||
| abstract class Guest extends BaseMailer | |||
| { | |||
| public function getParams(): array | |||
| { | |||
| return array_merge($this->getGuestParams(), []); | |||
| } | |||
| abstract public function getGuestParams(): array; | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| <?php | |||
| namespace App\Mail\Members; | |||
| use App\Mail\Common\AskConfirmation as CommonAskConfirmation; | |||
| use App\Models\Parking; | |||
| use App\Models\User; | |||
| use App\Models\UserDetail; | |||
| class AskConfirmation extends Member | |||
| { | |||
| use CommonAskConfirmation; | |||
| public function __construct( | |||
| array $data, | |||
| User|null $user = null, | |||
| UserDetail|null $userDetail = null, | |||
| Parking|null $parking = null, | |||
| ) { | |||
| $this->setAskConfirmationData($data, $user, $userDetail, $parking); | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return $this->getAskConfirmationParams(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| <?php | |||
| namespace App\Mail\Members; | |||
| use App\Models\SeasonTicketContract; | |||
| use App\Models\Parking; | |||
| class AutoCancelSeasonTicketContract extends Member | |||
| { | |||
| protected string $parkName; | |||
| protected int $seasonTicketSeqNo; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(Parking $parking, SeasonTicketContract $seasonTicketContract) | |||
| { | |||
| $this->setValues($parking->toArray()); | |||
| $this->setValues($seasonTicketContract->toArray()); | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.auto_cancel_season_ticket_contract'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】駐車場利用停止のお知らせ'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'parkName' => $this->parkName, | |||
| 'seasonTicketSeqNo' => $this->seasonTicketSeqNo, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| <?php | |||
| namespace App\Mail\Members; | |||
| use Illuminate\Bus\Queueable; | |||
| use Illuminate\Queue\SerializesModels; | |||
| class Bulk extends Member | |||
| { | |||
| use Queueable, SerializesModels; | |||
| private string $__subject; | |||
| private string $__contents; | |||
| public function __construct(string $subject, string $contents) | |||
| { | |||
| $this->__subject = $subject; | |||
| $this->__contents = $contents; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.bulk'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return $this->__subject; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'contents' => $this->__contents, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| <?php | |||
| namespace App\Mail\Members; | |||
| use App\Mail\BaseMailer; | |||
| abstract class Member extends BaseMailer | |||
| { | |||
| public function getParams(): array | |||
| { | |||
| return array_merge($this->getMemberParams(), [ | |||
| 'email' => $this->__email | |||
| ]); | |||
| } | |||
| abstract public function getMemberParams(): array; | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| <?php | |||
| namespace App\Mail\Members; | |||
| use App\Exceptions\AppCommonException; | |||
| class ResetIDm extends Member | |||
| { | |||
| private string $confirmationCode; | |||
| private int $seasonTicketSeqNo; | |||
| public function __construct() | |||
| { | |||
| $context = $this->context(); | |||
| $seasonTicketContract = $context->getSeasonTicketContract(); | |||
| if ($seasonTicketContract === null) { | |||
| throw new AppCommonException("コンテキスト不正 定期契約情報"); | |||
| } | |||
| $this->confirmationCode = $seasonTicketContract->confirmation_code; | |||
| $this->seasonTicketSeqNo = $seasonTicketContract->season_ticket_seq_no; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.reset_idm'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】ICカードリセットのお知らせ'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'confirmationCode' => $this->confirmationCode, | |||
| 'seasonTicketSeqNo' => $this->seasonTicketSeqNo, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,61 @@ | |||
| <?php | |||
| namespace App\Mail\Members; | |||
| use App\Models\ResetPassword; | |||
| use Illuminate\Support\Carbon; | |||
| class ResetPasswordStart extends Member | |||
| { | |||
| protected string $token; | |||
| protected Carbon $expiresAt; | |||
| protected $casts = [ | |||
| 'expiresAt' => 'datetime', | |||
| ]; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(ResetPassword $model) | |||
| { | |||
| $this->setValues($model->toArray()); | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.reset_password_start'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】パスワードリセット手続きのご案内'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'url' => $this->getUrl(), | |||
| 'expiresAt' => $this->getExpiresAt(), | |||
| ]; | |||
| } | |||
| private function getUrl() | |||
| { | |||
| return implode( | |||
| "/", | |||
| [ | |||
| config("app.url"), | |||
| 'password-reset', | |||
| $this->token | |||
| ] | |||
| ); | |||
| } | |||
| private function getExpiresAt() | |||
| { | |||
| return $this->expiresAt->format('Y/m/d H:i'); | |||
| } | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| <?php | |||
| namespace App\Mail\Members; | |||
| use Illuminate\Support\Carbon; | |||
| class SeasonTicketContractExpireRemind extends Member | |||
| { | |||
| protected string|null $parkName; | |||
| protected int $seasonTicketSeqNo; | |||
| protected Carbon $expirationEndDate; | |||
| protected $casts = [ | |||
| 'expirationEndDate' => 'datetime', | |||
| ]; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(\stdClass $data) | |||
| { | |||
| $this->setValues($data); | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.season_ticket_contract_expires_remind'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】定期期限切れ予告通知'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'parkName' => $this->parkName, | |||
| 'seasonTicketSeqNo' => $this->seasonTicketSeqNo, | |||
| 'expirationEndDate' => $this->expirationEndDate, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,111 @@ | |||
| <?php | |||
| namespace App\Mail\Members\Subscription; | |||
| use App\Codes\ParkingUseTypeCode; | |||
| use App\Codes\VehicleTypeCode; | |||
| use App\Mail\Members\Member; | |||
| use App\Models\Parking; | |||
| use App\Models\SeasonTicketContract; | |||
| use App\Models\SeasonTicketContractSubscription; | |||
| use App\Models\User; | |||
| use Illuminate\Support\Carbon; | |||
| use LogicException; | |||
| class Approve extends Member | |||
| { | |||
| private string $email; | |||
| private string $parkName; | |||
| private Carbon|null $useStartRequestDate; | |||
| private VehicleTypeCode $vehicleType; | |||
| private string $contractorTypeName; | |||
| private int $seasonTicketSeqNo; | |||
| private int|null $confirmationCode; | |||
| private ParkingUseTypeCode $parkingUseType; | |||
| private string $customerCode; | |||
| private string $parkingManagementCode; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct( | |||
| User $user, | |||
| SeasonTicketContractSubscription|null $subscription, | |||
| Parking $parking, | |||
| SeasonTicketContract $seasonTicketContract, | |||
| string $contractorTypeName | |||
| ) { | |||
| $this->email = $user->email; | |||
| $this->parkName = $parking->park_name; | |||
| $this->useStartRequestDate = $subscription ? $subscription->use_start_request_date : null; | |||
| $this->vehicleType = $seasonTicketContract->vehicle_type; | |||
| $this->contractorTypeName = $contractorTypeName; | |||
| $this->seasonTicketSeqNo = $seasonTicketContract->season_ticket_seq_no; | |||
| $this->confirmationCode = $seasonTicketContract->confirmation_code; | |||
| $this->parkingUseType = $seasonTicketContract->parking_use_type; | |||
| $this->customerCode = $seasonTicketContract->customer_code; | |||
| $this->parkingManagementCode = $seasonTicketContract->parking_management_code; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.subscription.approve'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】定期申し込み完了'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'email' => $this->email, | |||
| 'park_name' => $this->parkName, | |||
| 'use_start_request_date' => $this->getUseStartRequestDate(), | |||
| 'vehicle_type' => $this->getVehicleType(), | |||
| 'contractor_type_name' => $this->contractorTypeName, | |||
| 'how_to_use' => $this->getHowToUse(), | |||
| 'season_ticket_seq_no' => $this->seasonTicketSeqNo, | |||
| 'confirmation_code' => $this->confirmationCode, | |||
| 'ic_card_rental_request' => $this->parkingUseType === ParkingUseTypeCode::IC_RENTAL, | |||
| 'ic_self_card_request' => $this->parkingUseType === ParkingUseTypeCode::IC_SELF, | |||
| 'ic_qr_code_request' => $this->parkingUseType === ParkingUseTypeCode::QR, | |||
| 'my_qr_code_url' => $this->getMyQrCodeUrl(), | |||
| ]; | |||
| } | |||
| private function getUseStartRequestDate() | |||
| { | |||
| if ($this->useStartRequestDate === null) return "-"; | |||
| return $this->useStartRequestDate->format("Y年m月d日"); | |||
| } | |||
| private function getVehicleType() | |||
| { | |||
| return VehicleTypeCode::getName($this->vehicleType); | |||
| } | |||
| private function getHowToUse() | |||
| { | |||
| switch ($this->parkingUseType) { | |||
| case ParkingUseTypeCode::IC_RENTAL: | |||
| return "貸与ICカード"; | |||
| case ParkingUseTypeCode::IC_SELF: | |||
| return "個人ICカード"; | |||
| case ParkingUseTypeCode::QR: | |||
| return "QRコード"; | |||
| } | |||
| throw new LogicException(sprintf("不適切な駐車場利用方法 %d", $this->parkingUseType->value)); | |||
| } | |||
| private function getMyQrCodeUrl() | |||
| { | |||
| return $this->getAppUrl(['app', 'qrcode', 'detail', $this->customerCode, $this->parkingManagementCode]); | |||
| } | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| <?php | |||
| namespace App\Mail\Members\Subscription; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Mail\Members\Member; | |||
| class Cancel extends Member | |||
| { | |||
| private string $parkName; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct() | |||
| { | |||
| $context = $this->context(); | |||
| $parking = $context->getParking(); | |||
| if ($parking === null) { | |||
| throw new AppCommonException("コンテキスト不正 駐車場情報"); | |||
| } | |||
| $this->parkName = $parking->park_name; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.subscription.cancel'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】申込キャンセルのお知らせ'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'parkName' => $this->parkName, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| <?php | |||
| namespace App\Mail\Members\Subscription; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Mail\Members\Member; | |||
| use Illuminate\Support\Carbon; | |||
| class Entry extends Member | |||
| { | |||
| private string $email; | |||
| private string $parkName; | |||
| private Carbon|null $useStartRequestDate; | |||
| private string $memo; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct() | |||
| { | |||
| $context = $this->context(); | |||
| $user = $context->getUser(); | |||
| $subscription = $context->getSeasonTicketContractSubscription(); | |||
| $parking = $context->getParking(); | |||
| if ($user === null) { | |||
| throw new AppCommonException("コンテキスト不正 利用者情報"); | |||
| } | |||
| if ($subscription === null) { | |||
| throw new AppCommonException("コンテキスト不正 申込情報"); | |||
| } | |||
| if ($parking === null) { | |||
| throw new AppCommonException("コンテキスト不正 駐車場情報"); | |||
| } | |||
| $this->email = $user->email; | |||
| $this->parkName = $parking->park_name; | |||
| $this->useStartRequestDate = $subscription->use_start_request_date; | |||
| $this->memo = $subscription->memo ?? ""; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.subscription.entry'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】定期申し込み内容'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'email' => $this->email, | |||
| 'park_name' => $this->parkName, | |||
| 'use_start_request_date' => $this->getUseStartRequestDate(), | |||
| 'memo' => $this->memo | |||
| ]; | |||
| } | |||
| public function getUseStartRequestDate() | |||
| { | |||
| if ($this->useStartRequestDate === null) return "-"; | |||
| return $this->useStartRequestDate->format("Y年m月d日"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| <?php | |||
| namespace App\Mail\Members\Subscription; | |||
| use App\Mail\Members\Member; | |||
| use App\Models\SeasonTicketContractSubscriptionMessage; | |||
| class Hold extends Member | |||
| { | |||
| private string $parkName; | |||
| private string|null $message; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(SeasonTicketContractSubscriptionMessage $message) | |||
| { | |||
| $context = $this->context(); | |||
| $parking = $context->getParking(); | |||
| if ($parking === null) { | |||
| throw new AppCommonException("コンテキスト不正 駐車場情報"); | |||
| } | |||
| $this->parkName = $parking->park_name; | |||
| $this->message = $message->message; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.subscription.hold'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】申込確認のお知らせ'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'parkName' => $this->parkName, | |||
| 'memo' => $this->message, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| <?php | |||
| namespace App\Mail\Members\Subscription; | |||
| use App\Mail\Members\Member; | |||
| use App\Models\SeasonTicketContractSubscriptionMessage; | |||
| class Reject extends Member | |||
| { | |||
| private string $parkName; | |||
| private string $message; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(SeasonTicketContractSubscriptionMessage $message) | |||
| { | |||
| $context = $this->context(); | |||
| $parking = $context->getParking(); | |||
| if ($parking === null) { | |||
| throw new AppCommonException("コンテキスト不正 駐車場情報"); | |||
| } | |||
| $this->parkName = $parking->park_name; | |||
| $this->message = $message->message ?? ""; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.subscription.reject'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】申込却下のお知らせ'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'parkName' => $this->parkName, | |||
| 'memo' => $this->message, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| <?php | |||
| namespace App\Mail\Members\Subscription; | |||
| use App\Mail\Members\Member; | |||
| use App\Models\SeasonTicketContractSubscriptionMessage; | |||
| class Returned extends Member | |||
| { | |||
| private string $parkName; | |||
| private string $message; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(SeasonTicketContractSubscriptionMessage $message) | |||
| { | |||
| $context = $this->context(); | |||
| $parking = $context->getParking(); | |||
| if ($parking === null) { | |||
| throw new AppCommonException("コンテキスト不正 駐車場情報"); | |||
| } | |||
| $this->parkName = $parking->park_name; | |||
| $this->message = $message->message ?? ""; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.subscription.returned'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】申込差し戻しのお知らせ'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'parkName' => $this->parkName, | |||
| 'memo' => $this->message, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,76 @@ | |||
| <?php | |||
| namespace App\Mail\Members; | |||
| use App\Models\ResetPassword; | |||
| use App\Models\User; | |||
| use Illuminate\Support\Carbon; | |||
| class UserRegisterComplete extends Member | |||
| { | |||
| private string $email; | |||
| private bool $includePasswordReset = false; | |||
| private Carbon|null $expiresAt = null; | |||
| private string $token = ""; | |||
| /** | |||
| * Create a new message instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct(User $model, ResetPassword|null $password = null) | |||
| { | |||
| $this->email = $model->email; | |||
| if ($password !== null) { | |||
| $this->includePasswordReset = true; | |||
| $this->expiresAt = $password->expires_at; | |||
| $this->token = $password->token; | |||
| } | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.members.user_register_complete'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return '【スマートパーキングパス】会員登録完了'; | |||
| } | |||
| public function getMemberParams(): array | |||
| { | |||
| return [ | |||
| 'email' => $this->email, | |||
| 'includePasswordReset' => $this->includePasswordReset, | |||
| 'url' => $this->getUrl(), | |||
| 'expiresAt' => $this->getExpiresAt(), | |||
| ]; | |||
| } | |||
| private function getUrl() | |||
| { | |||
| return implode( | |||
| "/", | |||
| [ | |||
| config("app.url"), | |||
| 'password-reset', | |||
| $this->token | |||
| ] | |||
| ); | |||
| } | |||
| private function getExpiresAt() | |||
| { | |||
| if ($this->expiresAt === null) { | |||
| return ""; | |||
| } else { | |||
| return $this->expiresAt->format('Y/m/d H:i'); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| <?php | |||
| namespace App\Mail; | |||
| use App\Middlewares\Now; | |||
| use App\Models\Email; | |||
| use App\Util\DateUtil; | |||
| use Exception; | |||
| use Illuminate\Support\Facades\Log; | |||
| use Illuminate\Support\Facades\Mail; | |||
| class Sender | |||
| { | |||
| public static function send(string $emailId) | |||
| { | |||
| $email = Email::findOrFail($emailId); | |||
| info("メール送信", [ | |||
| 'id' => $email->id, | |||
| 'email' => $email->email, | |||
| 'mailer' => $email->type, | |||
| ]); | |||
| try { | |||
| Mail::to($email->email) | |||
| ->send(new TextMail($email->subject, $email->content)); | |||
| } catch (Exception $e) { | |||
| Log::error("メール送信失敗", [ | |||
| 'id' => $email->id, | |||
| 'email' => $email->email, | |||
| 'mailer' => $email->type, | |||
| ]); | |||
| $email->is_failed = true; | |||
| $email->save(); | |||
| throw $e; | |||
| } | |||
| $email->send_datetime = DateUtil::now(); | |||
| $email->save(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| <?php | |||
| namespace App\Mail; | |||
| class Test extends BaseMailer | |||
| { | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.test'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return 'メールテスト'; | |||
| } | |||
| public function getParams(): array | |||
| { | |||
| return []; | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| <?php | |||
| namespace App\Mail; | |||
| use Illuminate\Bus\Queueable; | |||
| use Illuminate\Queue\SerializesModels; | |||
| class TextMail extends BaseMailer | |||
| { | |||
| use Queueable, SerializesModels; | |||
| private string $__subject; | |||
| private string $__contents; | |||
| public function __construct(string $subject, string $contents) | |||
| { | |||
| $this->__subject = $subject; | |||
| $this->__contents = $contents; | |||
| } | |||
| public function getTemplateName(): string | |||
| { | |||
| return 'mails.free_text'; | |||
| } | |||
| public function getSubject(): string | |||
| { | |||
| return $this->__subject; | |||
| } | |||
| public function getParams(): array | |||
| { | |||
| return [ | |||
| 'contents' => $this->__contents, | |||
| ]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| use Illuminate\Database\Eloquent\Concerns\HasUuids; | |||
| use Illuminate\Database\Eloquent\SoftDeletes; | |||
| abstract class AppModel extends BaseModel | |||
| { | |||
| use SoftDeletes, HasUuids; | |||
| public function getHistory(): ?HistoryModel | |||
| { | |||
| $historyName = static::class . 'History'; | |||
| return new $historyName; | |||
| } | |||
| public function getChangeLogMessage($before, $after): ?string | |||
| { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,69 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| use App\Events\Model\CreatedEvent; | |||
| use App\Events\Model\DeletedEvent; | |||
| use App\Events\Model\UpdatingEvent; | |||
| use App\Models\Feature\IModelFeature; | |||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | |||
| use Illuminate\Database\Eloquent\Model; | |||
| use Illuminate\Database\Query\Builder; | |||
| use Illuminate\Support\Facades\DB; | |||
| abstract class BaseModel extends Model implements IModelFeature | |||
| { | |||
| use HasFactory; | |||
| const COL_NAME_ID = ColumnName::ID; | |||
| const COL_NAME_CREATED_BY = ColumnName::CREATED_BY; | |||
| const COL_NAME_UPDATED_BY = ColumnName::UPDATED_BY; | |||
| const COL_NAME_CREATED_AT = ColumnName::CREATED_AT; | |||
| const COL_NAME_UPDATED_AT = ColumnName::UPDATED_AT; | |||
| const COL_NAME_DELETED_AT = ColumnName::DELETED_AT; | |||
| protected $guarded = [ | |||
| self::COL_NAME_ID, | |||
| self::COL_NAME_CREATED_BY, | |||
| self::COL_NAME_UPDATED_BY, | |||
| self::COL_NAME_CREATED_AT, | |||
| self::COL_NAME_UPDATED_AT, | |||
| self::COL_NAME_DELETED_AT, | |||
| ]; | |||
| public static function getBuilder(string $name = 'main'): Builder | |||
| { | |||
| return DB::table(static::getTableName(), $name); | |||
| } | |||
| public static function getTableName(): string | |||
| { | |||
| return (new static)->getTable(); | |||
| } | |||
| public function copy(IModelFeature $from): static | |||
| { | |||
| $data = $from->getAttributeKeys(); | |||
| foreach ($data as $key) { | |||
| $this->$key = $from->$key; | |||
| } | |||
| return $this; | |||
| } | |||
| public function getAttributeKeys(): array | |||
| { | |||
| return array_values(array_unique(array_merge(array_keys($this->attributesToArray()), $this->hidden))); | |||
| } | |||
| public function isNotSavedModel(): bool | |||
| { | |||
| return data_get($this, ColumnName::ID) === null; | |||
| } | |||
| protected $dispatchesEvents = [ | |||
| 'created' => CreatedEvent::class, | |||
| 'updating' => UpdatingEvent::class, | |||
| 'deleted' => DeletedEvent::class, | |||
| ]; | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| abstract class ColumnName | |||
| { | |||
| // 共通 | |||
| const ID = 'id'; | |||
| const UPDATED_AT = 'updated_at'; | |||
| const UPDATED_BY = 'updated_by'; | |||
| const CREATED_AT = 'created_at'; | |||
| const CREATED_BY = 'created_by'; | |||
| const DELETED_AT = 'deleted_at'; | |||
| const HISTORY_ID = 'history_id'; | |||
| // 業務 | |||
| const CONTRACT_ID = 'contract_id'; | |||
| const USER_ID = 'user_id'; | |||
| const RECEIPT_ISSUING_ORDER_ID = "receipt_issuing_order_id"; | |||
| const SMS_SEND_ORDER_ID = "sms_send_order_id"; | |||
| const SMS_PROVIDER_ID = "sms_provider_id"; | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| use Illuminate\Support\Str; | |||
| class Contract extends AppModel | |||
| { | |||
| const COL_NAME_NAME = 'name'; | |||
| const COL_NAME_CUSTOM = 'custom'; | |||
| public function getModelName(): string | |||
| { | |||
| return "契約"; | |||
| } | |||
| public function includeCustom(string $target): bool | |||
| { | |||
| return in_array($target, $this->custom(), true); | |||
| } | |||
| public function custom(): array | |||
| { | |||
| $custom = data_get($this, self::COL_NAME_CUSTOM); | |||
| if ($custom) { | |||
| return explode(',', $custom); | |||
| } else { | |||
| return []; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| class ContractHistory extends HistoryModel | |||
| { | |||
| public function getModelName(): string | |||
| { | |||
| return "契約履歴"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| class Email extends AppModel | |||
| { | |||
| public function getModelName(): string | |||
| { | |||
| return "Email"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| class EmailHistory extends HistoryModel | |||
| { | |||
| public function getModelName(): string | |||
| { | |||
| return "Email履歴"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,76 @@ | |||
| <?php | |||
| namespace App\Models\Ex; | |||
| use App\Codes\UserRole; | |||
| use App\Models\ColumnName; | |||
| use App\Models\Contract; | |||
| use App\Models\User; | |||
| use Illuminate\Database\Eloquent\Model; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use LogicException; | |||
| class LoginUser | |||
| { | |||
| private const SESSION_KEY_SADMIN_CONTRACT_ID = 'SESSION_KEY_SADMIN_CONTRACT_ID'; | |||
| public function __construct( | |||
| private User $user, | |||
| private Contract $contract | |||
| ) { | |||
| } | |||
| public function user(): ?User | |||
| { | |||
| return Auth::user(); | |||
| } | |||
| public function contract(): ?Contract | |||
| { | |||
| if (!Auth::check()) { | |||
| return null; | |||
| } | |||
| if ($this->contract->isNotSavedModel()) { | |||
| $this->contract = $this->user()->contract; | |||
| } | |||
| return $this->contract; | |||
| } | |||
| public function checkAuthorization(array|Model $target): bool | |||
| { | |||
| if (!Auth::check()) { | |||
| return false; | |||
| } | |||
| if ($this->user()->role === UserRole::SUPER_ADMIN) { | |||
| return true; | |||
| } | |||
| $contractId = data_get($target, ColumnName::CONTRACT_ID); | |||
| if ($contractId === null) { | |||
| throw new LogicException("契約ID不正"); | |||
| } | |||
| return $contractId === $this->user()->contract_id; | |||
| } | |||
| public function getContractId(): ?string | |||
| { | |||
| if ($this->user()->role === UserRole::SUPER_ADMIN) { | |||
| $session = request()->session(); | |||
| if ($session->exists(self::SESSION_KEY_SADMIN_CONTRACT_ID)) { | |||
| return $session->get(self::SESSION_KEY_SADMIN_CONTRACT_ID); | |||
| } | |||
| return $this->contract()->id; | |||
| } | |||
| return data_get($this->contract(), Contract::COL_NAME_ID); | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| <?php | |||
| namespace App\Models\Feature; | |||
| use App\Models\ColumnName; | |||
| use App\Models\Contract; | |||
| use Illuminate\Database\Eloquent\Model; | |||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | |||
| use LogicException; | |||
| trait ContractFeature | |||
| { | |||
| const COL_NAME_CONTRACT_ID = ColumnName::CONTRACT_ID; | |||
| public function setContract(Contract|string $contract): static | |||
| { | |||
| $id = is_string($contract) ? $contract : $contract->id; | |||
| data_set($this, self::COL_NAME_CONTRACT_ID, $id); | |||
| return $this; | |||
| } | |||
| public function contract(): BelongsTo | |||
| { | |||
| if ($this instanceof Model) { | |||
| return $this->belongsTo(Contract::class); | |||
| } else { | |||
| throw new LogicException("不正"); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| <?php | |||
| namespace App\Models\Feature; | |||
| use App\Models\HistoryModel; | |||
| use Illuminate\Database\Query\Builder; | |||
| interface IModelFeature | |||
| { | |||
| public static function getBuilder(string $name = 'main'): Builder; | |||
| public static function getTableName(): string; | |||
| public function copy(IModelFeature $from): static; | |||
| public function getAttributeKeys(): array; | |||
| public function isNotSavedModel(): bool; | |||
| public function getHistory(): HistoryModel|null; | |||
| /** | |||
| * モデルの和名を取得する | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getModelName(): string; | |||
| public function getChangeLogMessage($before, $after): string|null; | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| <?php | |||
| namespace App\Models\Feature; | |||
| use App\Models\ColumnName; | |||
| use App\Models\ReceiptIssuingOrder; | |||
| use Illuminate\Database\Eloquent\Model; | |||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | |||
| use LogicException; | |||
| trait ReceiptIssuingOrderFeature | |||
| { | |||
| const COL_NAME_RECEIPT_ISSUING_ORDER_ID = ColumnName::RECEIPT_ISSUING_ORDER_ID; | |||
| public function setReceiptIssuingOrder(ReceiptIssuingOrder|string $receiptIssuingOrder): static | |||
| { | |||
| $id = is_string($receiptIssuingOrder) ? $receiptIssuingOrder : $receiptIssuingOrder->id; | |||
| data_set($this, self::COL_NAME_RECEIPT_ISSUING_ORDER_ID, $id); | |||
| return $this; | |||
| } | |||
| public function receiptIssuingOrder(): BelongsTo | |||
| { | |||
| if ($this instanceof Model) { | |||
| return $this->belongsTo(ReceiptIssuingOrder::class); | |||
| } else { | |||
| throw new LogicException("不正"); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| <?php | |||
| namespace App\Models\Feature; | |||
| use App\Models\ColumnName; | |||
| use App\Models\SMSProvider; | |||
| use Illuminate\Database\Eloquent\Model; | |||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | |||
| use LogicException; | |||
| trait SMSProviderFeature | |||
| { | |||
| const COL_NAME_SMS_PROVIDER_ID = ColumnName::SMS_PROVIDER_ID; | |||
| public function setSMSProvider(SMSProvider|string $smsProvider): static | |||
| { | |||
| $id = is_string($smsProvider) ? $smsProvider : $smsProvider->id; | |||
| data_set($this, self::COL_NAME_SMS_PROVIDER_ID, $id); | |||
| return $this; | |||
| } | |||
| public function smsProvider(): BelongsTo | |||
| { | |||
| if ($this instanceof Model) { | |||
| return $this->belongsTo(SMSProvider::class); | |||
| } else { | |||
| throw new LogicException("不正"); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| <?php | |||
| namespace App\Models\Feature; | |||
| use App\Models\ColumnName; | |||
| use App\Models\SMSSendOrder; | |||
| use Illuminate\Database\Eloquent\Model; | |||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | |||
| use LogicException; | |||
| trait SMSSendOrderFeature | |||
| { | |||
| const COL_NAME_SMS_SEND_ORDER_ID = ColumnName::SMS_SEND_ORDER_ID; | |||
| public function setSMSSendOrder(SMSSendOrder|string $smsSendOrder) | |||
| { | |||
| $id = is_string($smsSendOrder) ? $smsSendOrder : $smsSendOrder->id; | |||
| data_set($this, self::COL_NAME_SMS_SEND_ORDER_ID, $id); | |||
| } | |||
| public function smsSendOrder(): BelongsTo | |||
| { | |||
| if ($this instanceof Model) { | |||
| return $this->belongsTo(SMSSendOrder::class); | |||
| } else { | |||
| throw new LogicException("不正"); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,39 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| use App\Models\Feature\IModelFeature; | |||
| use Illuminate\Database\Eloquent\Collection; | |||
| abstract class HistoryModel extends BaseModel | |||
| { | |||
| const COL_NAME_HISTORY_ID = ColumnName::HISTORY_ID; | |||
| protected $primaryKey = ColumnName::HISTORY_ID; | |||
| /** | |||
| * @param string $id | |||
| * @return Collection<static> | |||
| */ | |||
| public static function findById(string $id) | |||
| { | |||
| return static::query()->where(ColumnName::ID, $id) | |||
| ->orderBy(ColumnName::CREATED_AT) | |||
| ->get(); | |||
| } | |||
| public function fillFromOrigin(IModelFeature $originModel) | |||
| { | |||
| return $this->copy($originModel); | |||
| } | |||
| public function getHistory(): ?HistoryModel | |||
| { | |||
| return null; | |||
| } | |||
| public function getChangeLogMessage($before, $after): ?string | |||
| { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| use App\Models\Feature\ReceiptIssuingOrderFeature; | |||
| class ReceiptIssuingHTParkingCustomOrder extends AppModel | |||
| { | |||
| use ReceiptIssuingOrderFeature; | |||
| public function getModelName(): string | |||
| { | |||
| return "領収証発行依頼(HT駐車場カスタム)"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| class ReceiptIssuingHTParkingCustomOrderHistory extends HistoryModel | |||
| { | |||
| public function getModelName(): string | |||
| { | |||
| return "領収証発行依頼履歴(HT駐車場カスタム)"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| use App\Codes\ReceiptIssuingOrderStatus; | |||
| use App\Models\Feature\ContractFeature; | |||
| class ReceiptIssuingOrder extends AppModel | |||
| { | |||
| use ContractFeature; | |||
| const COL_NAME_HANDLER_ID = "handler_id"; | |||
| const COL_NAME_ORDER_DATETIME = "order_datetime"; | |||
| const COL_NAME_STATUS = "status"; | |||
| const COL_NAME_SUMMARY_KEY1 = "sumamry_key1"; | |||
| const COL_NAME_SUMMARY_KEY2 = "sumamry_key2"; | |||
| const COL_NAME_ACCESS_TOKEN = "access_token"; | |||
| const COL_NAME_ACCESS_TOKEN_EXPIRES_AT = "access_token_expires_at"; | |||
| const COL_NAME_SMS_PHONE_NUMBER = "sms_phone_number"; | |||
| const COL_NAME_SMS_SEND_SUCCESS = "sms_send_success"; | |||
| const COL_NAME_RECEIPT_NO = "receipt_no"; | |||
| const COL_NAME_RECEIPT_USE_DATETIME = "receipt_use_datetime"; | |||
| const COL_NAME_RECEIPT_SHOP_NAME = "receipt_shop_name"; | |||
| const COL_NAME_RECEIPT_ISSUER = "receipt_issuer"; | |||
| const COL_NAME_RECEIPT_PURPOSE = "receipt_purpose"; | |||
| const COL_NAME_RECEIPT_INVOICE_NO = "receipt_invoice_no"; | |||
| const COL_NAME_RECEIPT_AMOUNT = "receipt_amount"; | |||
| const COL_NAME_RECEIPT_HOW_TO_RECEIVE = "receipt_how_to_receive"; | |||
| const COL_NAME_EMAIL = "email"; | |||
| const COL_NAME_MAIL_PREF_CODE = "mail_pref_code"; | |||
| const COL_NAME_MAIL_ZIP_CODE = "mail_zip_code"; | |||
| const COL_NAME_MAIL_ADDRESS1 = "mail_address1"; | |||
| const COL_NAME_MAIL_ADDRESS2 = "mail_address2"; | |||
| const COL_NAME_MAIL_ADDRESS3 = "mail_address3"; | |||
| const COL_NAME_MAIL_POST_DATE = "mail_post_date"; | |||
| const COL_NAME_MEMO = "memo"; | |||
| protected $casts = [ | |||
| self::COL_NAME_STATUS => ReceiptIssuingOrderStatus::class, | |||
| ]; | |||
| public function getModelName(): string | |||
| { | |||
| return "領収証発行依頼"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| class ReceiptIssuingOrderHistory extends HistoryModel | |||
| { | |||
| public function getModelName(): string | |||
| { | |||
| return "領収証発行依頼履歴"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| class SMSProvider extends AppModel | |||
| { | |||
| const COL_NAME_PROVIDER_NAME = 'provider_name'; | |||
| public function getModelName(): string | |||
| { | |||
| return "SMSプロバイダーマスタ"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| use App\Codes\SMSProvicer; | |||
| use App\Models\Feature\ContractFeature; | |||
| use App\Models\Feature\SMSSendOrderFeature; | |||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | |||
| class SMSProviderFourSMessageCommunication extends AppModel | |||
| { | |||
| use ContractFeature, SMSSendOrderFeature; | |||
| public function getModelName(): string | |||
| { | |||
| return "FourSMessage通信実績"; | |||
| } | |||
| public function getHistory(): ?HistoryModel | |||
| { | |||
| return null; | |||
| } | |||
| } | |||