Browse Source

init

master
sosuke.iwabuchi 1 year ago
commit
099e367028
100 changed files with 3650 additions and 0 deletions
  1. +18
    -0
      .editorconfig
  2. +82
    -0
      .env.example
  3. +11
    -0
      .gitattributes
  4. +22
    -0
      .gitignore
  5. +66
    -0
      README.md
  6. +11
    -0
      app/Codes/EnvironmentName.php
  7. +11
    -0
      app/Codes/HTTPResultCode.php
  8. +9
    -0
      app/Codes/QueueName.php
  9. +10
    -0
      app/Codes/UserRole.php
  10. +27
    -0
      app/Console/Kernel.php
  11. +12
    -0
      app/Contexts/Model/Deposit.php
  12. +39
    -0
      app/Contexts/Model/ModelContext.php
  13. +12
    -0
      app/Contexts/Model/Shop.php
  14. +42
    -0
      app/Events/Email/ConfirmEvent.php
  15. +8
    -0
      app/Events/Model/CreatedEvent.php
  16. +8
    -0
      app/Events/Model/CreatingEvent.php
  17. +8
    -0
      app/Events/Model/DeletedEvent.php
  18. +8
    -0
      app/Events/Model/DeletingEvent.php
  19. +36
    -0
      app/Events/Model/ModelChangeEvent.php
  20. +8
    -0
      app/Events/Model/UpdatingEvent.php
  21. +9
    -0
      app/Exceptions/AppCommonException.php
  22. +13
    -0
      app/Exceptions/ConfigException.php
  23. +12
    -0
      app/Exceptions/ExclusiveException.php
  24. +9
    -0
      app/Exceptions/GeneralErrorMessageException.php
  25. +30
    -0
      app/Exceptions/Handler.php
  26. +12
    -0
      app/Exceptions/SkipException.php
  27. +21
    -0
      app/Exceptions/TempFileNotExistsException.php
  28. +14
    -0
      app/Features/InstanceAble.php
  29. +12
    -0
      app/Http/Controllers/Controller.php
  30. +46
    -0
      app/Http/Controllers/Server/IF24_01Controller.php
  31. +71
    -0
      app/Http/Controllers/Server/IF24_02Controller.php
  32. +161
    -0
      app/Http/Controllers/Server/IFController.php
  33. +43
    -0
      app/Http/Controllers/Web/Auth/LoginController.php
  34. +21
    -0
      app/Http/Controllers/Web/Auth/LoginParam.php
  35. +33
    -0
      app/Http/Controllers/Web/Auth/LogoutController.php
  36. +9
    -0
      app/Http/Controllers/Web/Auth/LogoutParam.php
  37. +17
    -0
      app/Http/Controllers/Web/Auth/Me.php
  38. +40
    -0
      app/Http/Controllers/Web/Auth/MeController.php
  39. +9
    -0
      app/Http/Controllers/Web/Auth/MeParam.php
  40. +247
    -0
      app/Http/Controllers/Web/BaseParam.php
  41. +10
    -0
      app/Http/Controllers/Web/IParam.php
  42. +37
    -0
      app/Http/Controllers/Web/IndexController.php
  43. +13
    -0
      app/Http/Controllers/Web/NoneParams.php
  44. +68
    -0
      app/Http/Controllers/Web/QRService/CreateTicketController.php
  45. +20
    -0
      app/Http/Controllers/Web/QRService/CreateTicketParam.php
  46. +9
    -0
      app/Http/Controllers/Web/RoleCheck.php
  47. +14
    -0
      app/Http/Controllers/Web/Rule.php
  48. +93
    -0
      app/Http/Controllers/Web/RuleAnalyzer.php
  49. +13
    -0
      app/Http/Controllers/Web/SortableParam.php
  50. +12
    -0
      app/Http/Controllers/Web/TimestampParam.php
  51. +394
    -0
      app/Http/Controllers/Web/WebController.php
  52. +68
    -0
      app/Http/Kernel.php
  53. +17
    -0
      app/Http/Middleware/Authenticate.php
  54. +17
    -0
      app/Http/Middleware/EncryptCookies.php
  55. +17
    -0
      app/Http/Middleware/PreventRequestsDuringMaintenance.php
  56. +30
    -0
      app/Http/Middleware/RedirectIfAuthenticated.php
  57. +19
    -0
      app/Http/Middleware/TrimStrings.php
  58. +20
    -0
      app/Http/Middleware/TrustHosts.php
  59. +28
    -0
      app/Http/Middleware/TrustProxies.php
  60. +22
    -0
      app/Http/Middleware/ValidateSignature.php
  61. +16
    -0
      app/Http/Middleware/VerifyCsrfToken.php
  62. +37
    -0
      app/Listeners/Email/EmailSendJobRegister.php
  63. +15
    -0
      app/Listeners/Model/CreatedListener.php
  64. +20
    -0
      app/Listeners/Model/CreatingListener.php
  65. +15
    -0
      app/Listeners/Model/DeletedListener.php
  66. +15
    -0
      app/Listeners/Model/DeletingListener.php
  67. +48
    -0
      app/Listeners/Model/ModelListener.php
  68. +26
    -0
      app/Listeners/Model/UpdatingListener.php
  69. +115
    -0
      app/Logics/QRService/CertificateLogic.php
  70. +179
    -0
      app/Logics/QRService/CreateLogic.php
  71. +114
    -0
      app/Logics/QRService/DepositCheck.php
  72. +25
    -0
      app/Logics/QRService/QRCryptoLogic.php
  73. +48
    -0
      app/Models/AppModel.php
  74. +95
    -0
      app/Models/BaseModel.php
  75. +10
    -0
      app/Models/Cast.php
  76. +30
    -0
      app/Models/ColumnName.php
  77. +38
    -0
      app/Models/Feature/IModelFeature.php
  78. +13
    -0
      app/Models/Feature/UserId.php
  79. +39
    -0
      app/Models/HistoryModel.php
  80. +17
    -0
      app/Models/Htpms/MstCustomer.php
  81. +28
    -0
      app/Models/HtpmsCustomer/Deposit/Deposit.php
  82. +36
    -0
      app/Models/HtpmsCustomer/Deposit/DepositTransfer.php
  83. +16
    -0
      app/Models/HtpmsCustomer/Existing/Parking.php
  84. +10
    -0
      app/Models/HtpmsCustomer/HtpmsCustomerAppModel.php
  85. +25
    -0
      app/Models/HtpmsCustomer/HtpmsCustomerConnectionSwitch.php
  86. +10
    -0
      app/Models/HtpmsCustomer/HtpmsCustomerHistoryModel.php
  87. +25
    -0
      app/Models/HtpmsCustomer/Mst/Shop.php
  88. +18
    -0
      app/Models/HtpmsCustomer/Mst/ShopHistory.php
  89. +23
    -0
      app/Models/HtpmsCustomer/Mst/ShopNoRelation.php
  90. +18
    -0
      app/Models/HtpmsCustomer/Mst/ShopNoRelationHistory.php
  91. +26
    -0
      app/Models/HtpmsCustomer/QRService/AcquisitionAvailableSetting.php
  92. +18
    -0
      app/Models/HtpmsCustomer/QRService/AcquisitionAvailableSettingHistory.php
  93. +77
    -0
      app/Models/HtpmsCustomer/QRService/AcquisitionTicket.php
  94. +28
    -0
      app/Models/HtpmsCustomer/QRService/AcquisitionTicketToken.php
  95. +26
    -0
      app/Models/HtpmsCustomer/QRService/CertificationAvailableSetting.php
  96. +18
    -0
      app/Models/HtpmsCustomer/QRService/CertificationAvailableSettingHistory.php
  97. +72
    -0
      app/Models/HtpmsCustomer/QRService/CertificationTicket.php
  98. +23
    -0
      app/Models/HtpmsCustomer/QRService/ServiceParkingGroup.php
  99. +18
    -0
      app/Models/HtpmsCustomer/QRService/ServiceParkingGroupHistory.php
  100. +22
    -0
      app/Models/HtpmsCustomer/QRService/ServiceParkingGroupRelation.php

+ 18
- 0
.editorconfig View File

@@ -0,0 +1,18 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2

[docker-compose.yml]
indent_size = 4

+ 82
- 0
.env.example View File

@@ -0,0 +1,82 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql

DB_HOST=pgsql
DB_PORT=5432
DB_DATABASE=ht_ic_web
DB_USERNAME=sail
DB_PASSWORD=password

DB_HOST_HTPMS=pgsql
DB_PORT_HTPMS=5432
DB_DATABASE_HTPMS=htpms
DB_USERNAME_HTPMS=hellotechno
DB_PASSWORD_HTPMS=htpass2022

DB_HOST_HTPMS_CUSTOMER=pgsql
DB_PORT_HTPMS_CUSTOMER=5432
DB_DATABASE_HTPMS_CUSTOMER=htpms_520
DB_USERNAME_HTPMS_CUSTOMER=hellotechno
DB_PASSWORD_HTPMS_CUSTOMER=htpass2022

DB_HOST_HTPMS_SYSTEM=pgsql
DB_PORT_HTPMS_SYSTEM=5432
DB_DATABASE_HTPMS_SYSTEM=htpmssystem
DB_USERNAME_HTPMS_SYSTEM=hellotechno
DB_PASSWORD_HTPMS_SYSTEM=htpass2022

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_APP_NAME="${APP_NAME}"
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}"

SIF_SERVER_IP_ADDRESS=

NOW_DATETIME=null

+ 11
- 0
.gitattributes View File

@@ -0,0 +1,11 @@
* text=auto eol=lf

*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php

/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

+ 22
- 0
.gitignore View File

@@ -0,0 +1,22 @@
/.phpunit.cache
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.env.production
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.vscode
_ide*.php
/db_dump
sail

+ 66
- 0
README.md View File

@@ -0,0 +1,66 @@
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>

<p align="center">
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>

## About Laravel

Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:

- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).

Laravel is accessible, powerful, and provides tools required for large, robust applications.

## Learning Laravel

Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.

You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.

If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.

## Laravel Sponsors

We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).

### Premium Partners

- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[WebReinvent](https://webreinvent.com/)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
- **[Jump24](https://jump24.co.uk)**
- **[Redberry](https://redberry.international/laravel/)**
- **[Active Logic](https://activelogic.com)**
- **[byte5](https://byte5.de)**
- **[OP.GG](https://op.gg)**

## Contributing

Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).

## Code of Conduct

In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).

## Security Vulnerabilities

If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.

## License

The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

+ 11
- 0
app/Codes/EnvironmentName.php View File

@@ -0,0 +1,11 @@
<?php

namespace App\Codes;

enum EnvironmentName: string
{
case TEST = 'testing';
case LOCAL = 'local';
case STAGING = 'staging';
case PRODUCTOIN = 'production';
}

+ 11
- 0
app/Codes/HTTPResultCode.php View File

@@ -0,0 +1,11 @@
<?php

namespace App\Codes;

enum HTTPResultCode: int
{
case SECCESS = 0;
case FAILED = 1;
case UNAUTHORIZED = 2;
case EXCLUSIVE_ERROR = 3;
}

+ 9
- 0
app/Codes/QueueName.php View File

@@ -0,0 +1,9 @@
<?php

namespace App\Codes;

enum QueueName: string
{
case EMAIL = 'email';
case JOB = 'job';
}

+ 10
- 0
app/Codes/UserRole.php View File

@@ -0,0 +1,10 @@
<?php

namespace App\Codes;

enum UserRole: string
{
case ADMIN = "admin";
case CUSTOMER = "customer";
case SHOP = "shop";
}

+ 27
- 0
app/Console/Kernel.php View File

@@ -0,0 +1,27 @@
<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
}

/**
* Register the commands for the application.
*/
protected function commands(): void
{
$this->load(__DIR__.'/Commands');

require base_path('routes/console.php');
}
}

+ 12
- 0
app/Contexts/Model/Deposit.php View File

@@ -0,0 +1,12 @@
<?php

namespace App\Contexts\Model;

use App\Models\HtpmsCustomer\Deposit\Deposit as Model;

/**
* @extends ModelContext<Model>
*/
class Deposit extends ModelContext
{
}

+ 39
- 0
app/Contexts/Model/ModelContext.php View File

@@ -0,0 +1,39 @@
<?php

namespace App\Contexts\Model;

use App\Features\InstanceAble;
use Illuminate\Database\Eloquent\Model;
use LogicException;

/**
* @template TValue of Model
*/
abstract class ModelContext
{
use InstanceAble;

/** @var ?TValue $model */
private ?Model $model = null;

/**
* @param TValue $model
* @param boolean $override
* @return void
*/
public function set($model, bool $override = false)
{
if ($this->model !== null && $override === false) {
throw new LogicException("コンテキスト 不正オーバーライド");
}
$this->model = $model;
}

/**
* @return TValue
*/
public function get()
{
return $this->model;
}
}

+ 12
- 0
app/Contexts/Model/Shop.php View File

@@ -0,0 +1,12 @@
<?php

namespace App\Contexts\Model;

use App\Models\HtpmsCustomer\Mst\Shop as Model;

/**
* @extends ModelContext<Model>
*/
class Shop extends ModelContext
{
}

+ 42
- 0
app/Events/Email/ConfirmEvent.php View File

@@ -0,0 +1,42 @@
<?php

namespace App\Events\Email;

use App\Email\BaseEmailer;
use App\Models\Email;
use App\Models\EmailAttachment;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Database\Eloquent\Collection;
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|BaseEmailer $email, ?Collection $attachments = null)
{
if ($email instanceof Email) {
$this->email = $email;
} else {
$this->email = $email->makeModel();
$this->email->save();
}
if ($attachments !== null) {
foreach ($attachments as $attachment) {
if (!($attachment instanceof EmailAttachment)) continue;

$emailId = $this->email->id;
$attachment->email_id = $emailId;
$attachment->save();
}
}
}
}

+ 8
- 0
app/Events/Model/CreatedEvent.php View File

@@ -0,0 +1,8 @@
<?php

namespace App\Events\Model;


class CreatedEvent extends ModelChangeEvent
{
}

+ 8
- 0
app/Events/Model/CreatingEvent.php View File

@@ -0,0 +1,8 @@
<?php

namespace App\Events\Model;


class CreatingEvent extends ModelChangeEvent
{
}

+ 8
- 0
app/Events/Model/DeletedEvent.php View File

@@ -0,0 +1,8 @@
<?php

namespace App\Events\Model;


class DeletedEvent extends ModelChangeEvent
{
}

+ 8
- 0
app/Events/Model/DeletingEvent.php View File

@@ -0,0 +1,8 @@
<?php

namespace App\Events\Model;


class DeletingEvent extends ModelChangeEvent
{
}

+ 36
- 0
app/Events/Model/ModelChangeEvent.php View File

@@ -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'),
];
}
}

+ 8
- 0
app/Events/Model/UpdatingEvent.php View File

@@ -0,0 +1,8 @@
<?php

namespace App\Events\Model;


class UpdatingEvent extends ModelChangeEvent
{
}

+ 9
- 0
app/Exceptions/AppCommonException.php View File

@@ -0,0 +1,9 @@
<?php

namespace App\Exceptions;

use Exception;

class AppCommonException extends Exception
{
}

+ 13
- 0
app/Exceptions/ConfigException.php View File

@@ -0,0 +1,13 @@
<?php

namespace App\Exceptions;

use Exception;

class ConfigException extends Exception
{
public function __construct(string $key, $value)
{
parent::__construct("設定エラー: key:" . $key . " value:" . $value);
}
}

+ 12
- 0
app/Exceptions/ExclusiveException.php View File

@@ -0,0 +1,12 @@
<?php

namespace App\Exceptions;

use Exception;

/**
* 排他エラー
*/
class ExclusiveException extends Exception
{
}

+ 9
- 0
app/Exceptions/GeneralErrorMessageException.php View File

@@ -0,0 +1,9 @@
<?php

namespace App\Exceptions;

use Exception;

class GeneralErrorMessageException extends Exception
{
}

+ 30
- 0
app/Exceptions/Handler.php View File

@@ -0,0 +1,30 @@
<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;

class Handler extends ExceptionHandler
{
/**
* The list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];

/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
}

+ 12
- 0
app/Exceptions/SkipException.php View File

@@ -0,0 +1,12 @@
<?php

namespace App\Exceptions;

use Exception;

/**
* スキップ
*/
class SkipException extends Exception
{
}

+ 21
- 0
app/Exceptions/TempFileNotExistsException.php View File

@@ -0,0 +1,21 @@
<?php

namespace App\Exceptions;

use Exception;

class TempFileNotExistsException extends Exception
{
private $filepath = "";

public function setFilepath(string $filepath): static
{
$this->filepath = $filepath;
return $this;
}

public function getFilepath(): string
{
return $this->filepath;
}
}

+ 14
- 0
app/Features/InstanceAble.php View File

@@ -0,0 +1,14 @@
<?php

namespace App\Features;

trait InstanceAble
{
/**
* @return static
*/
public static function instance()
{
return app()->make(static::class);
}
}

+ 12
- 0
app/Http/Controllers/Controller.php View File

@@ -0,0 +1,12 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;

class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
}

+ 46
- 0
app/Http/Controllers/Server/IF24_01Controller.php View File

@@ -0,0 +1,46 @@
<?php

namespace App\Http\Controllers\Server;

use App\Logics\QRService\CertificateLogic;
use App\Transmission\Layouts\IF24_01Request;
use App\Transmission\Layouts\IF24_01Response;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class IF24_01Controller extends IFController
{
protected function run(Request $request): array
{

try {
DB::beginTransaction();
$layout = IF24_01Request::create($request);
$response = $this->handleRequest($layout);
DB::commit();
} catch (Exception $e) {
DB::rollBack();
throw $e;
}

return $this->successResponse($request, $response);
}

private function handleRequest(IF24_01Request $request): IF24_01Response
{
$qr = CertificateLogic::getUsable(
$request->header->parkingManagementCode,
$request->publishingTerminalCode,
$request->publishingDate,
$request->publishingNo,
);

$response = new IF24_01Response();

$response->discountTicketCode = $qr->discount_ticket_code;
$response->shopNo = $qr->shop_no;

return $response;
}
}

+ 71
- 0
app/Http/Controllers/Server/IF24_02Controller.php View File

@@ -0,0 +1,71 @@
<?php

namespace App\Http\Controllers\Server;

use App\Exceptions\AppCommonException;
use App\Logics\QRService\CertificateLogic;
use App\Logics\QRService\CreateLogic;
use App\Transmission\Layouts\Code\QRTypeCode;
use App\Transmission\Layouts\IF24_02Request;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Nette\NotImplementedException;

class IF24_02Controller extends IFController
{
protected function run(Request $request): array
{
try {
IF24_02Request::validateLayout($request);
} catch (ValidationException $e) {
logger($e->getMessage(), [__LINE__]);
logger($e->errors());
logger($request->toArray());
return $this->failResponse($request);
}

try {
DB::beginTransaction();
$layout = IF24_02Request::create($request);
$this->handleRequest($layout);
DB::commit();
} catch (Exception $e) {
DB::rollBack();
throw $e;
}

return $this->successResponse($request);
}

private function handleRequest(IF24_02Request $request)
{
// 認証チェック
if ($request->qrTypeCode === QRTypeCode::方式1_認証方式) {
CertificateLogic::use(
$request->header->parkingManagementCode,
$request->publishingTerminalCode,
$request->publishingDate,
$request->publishingNo,
$request->discountTicketCode,
$request->adjustDatetime,
$request->discountAmount,
);
} else if ($request->qrTypeCode === QRTypeCode::方式2_印字方式) {
throw new NotImplementedException("方式2未実装");
} else if ($request->qrTypeCode === QRTypeCode::方式3_取得方式) {
CreateLogic::use(
$request->shopNo,
$request->header->parkingManagementCode,
$request->publishingDate,
$request->publishingNo,
$request->discountTicketCode,
$request->adjustDatetime,
$request->discountAmount,
);
} else {
throw new AppCommonException("想定外のQRタイプコード");
}
}
}

+ 161
- 0
app/Http/Controllers/Server/IFController.php View File

@@ -0,0 +1,161 @@
<?php

namespace App\Http\Controllers\Server;

use App\Codes\EnvironmentName;
use App\Exceptions\AppCommonException;
use App\Http\Controllers\Controller;
use App\Models\Htpms\MstCustomer;
use App\Models\HtpmsCustomer\HtpmsCustomerConnectionSwitch;
use App\Transmission\ResultCode;
use App\Transmission\Layouts\IFCommonHeader;
use App\Transmission\Layouts\IFResponse;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

abstract class IFController extends Controller
{
protected IFCommonHeader $header;

public function __constract()
{
$this->header = new IFCommonHeader();
}

abstract protected function run(Request $request): array;


public function entry(Request $request)
{
try {
$this->setLogContext($request);
$this->checkHeader($request);
logger(sprintf(
"リクエスト受信:%s 顧客コード:%s 駐車場管理コード:%s IF:%s",
$request->path(),
$this->header->customerCode,
$this->header->parkingManagementCode,
$this->header->interfaceId
));

if (!$this->checkIp($request)) {
throw new Exception("IP不正");
}

// 顧客コードから接続先DBの切り替え
$this->switchDb();

$result = $this->run($request);
} catch (ValidationException $e) {
logs()->error("バリデーション失敗");
logs()->error($e->errors());
logs()->debug("Request Contents", $request->toArray());
logs()->debug($request->toArray());

$result = $this->failResponseFromEmpty();
return response()->json($result);
} catch (AppCommonException $e) {
logs()->error(sprintf(
"リクエスト失敗:%s 顧客コード:%s 駐車場管理コード:%s",
$request->path(),
$this->header->customerCode,
$this->header->parkingManagementCode,
));
logs()->error($e->getMessage());
$result = $this->failResponse($request);
return response()->json($result);
} catch (Exception $e) {
$contexts = [
'path' => $request->path(),
'ip' => $request->ip(),
'data' => $request->all(),
'message' => $e->getMessage()
];
logs()->error("例外発生", $contexts);
logs()->error($e->getMessage());
$result = $this->failResponseFromEmpty($request);
return response()->json($result);
}

return response()->json($result);
}

private function checkHeader(Request $request)
{
// 駐車場情報取得
IFCommonHeader::validateLayout($request);
$this->header = IFCommonHeader::createFromRequest($request);
}

private function checkIp(Request $request)
{
// SIFのIPが一致しているか確認
if (app()->environment([EnvironmentName::LOCAL->value, EnvironmentName::STAGING->value, EnvironmentName::TEST->value])) {
return true;
}
return in_array($request->ip(), config("transmission.sif_ip_address"));
}

protected function successResponse(Request $request, array|IFResponse $body = []): array
{
if ($body instanceof IFResponse) {
return $this->Response(ResultCode::SUCCESS, $request, $body->getBodyArray());
} else {
return $this->Response(ResultCode::SUCCESS, $request, $body);
}
}

protected function failResponse(Request $request, array|IFResponse $body = []): array
{
if ($body instanceof IFResponse) {
return $this->Response(ResultCode::FAIL, $request, $body->toArray());
} else {
return $this->Response(ResultCode::FAIL, $request, $body);
}
}

private function Response(ResultCode $resultCode, Request $request, array $body): array
{
$response = [];
$header = IFCommonHeader::createFromRequest($request);
$header->resultCode = $resultCode->value;
$response[IFCommonHeader::COL_NAME_HEADER] = $header->toArray();
if (!empty($body)) {
$response[IFCommonHeader::COL_NAME_BODY] = $body;
}
return $response;
}

protected function failResponseFromEmpty(): array
{
$response = [];
$header = new IFCommonHeader();
$header->resultCode = ResultCode::FAIL->value;
$response[IFCommonHeader::COL_NAME_HEADER] = $header->toArray();
return $response;
}

protected function setLogContext(Request $request)
{
$context = [
'__requestUuid__' => strval(Str::uuid()),
'__path__' => $request->path(),
'__ip__' => $request->ip(),
];

if (app()->environment([EnvironmentName::LOCAL->value, EnvironmentName::STAGING->value])) {
$context["__requestParam__"] = $request->all();
}

Log::withContext($context);
}

protected function switchDb()
{
$customer = MstCustomer::whereCustomerId($this->header->customerCode)->firstOrFail();
HtpmsCustomerConnectionSwitch::switch($customer->id);
}
}

+ 43
- 0
app/Http/Controllers/Web/Auth/LoginController.php View File

@@ -0,0 +1,43 @@
<?php

namespace App\Http\Controllers\Web\Auth;

use App\Http\Controllers\Web\WebController;
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 "ログインを行う";
}


public function __construct(protected LoginParam $param)
{
parent::__construct();
}

protected function run(Request $request): JsonResponse
{
// 取得したユーザ情報を登録しログインを行う
$param = $this->param;

if (Auth::attempt([
'email' => $param->email,
'password' => $param->password,
])) {
return $this->successResponse();
} else {
return $this->failedResponse();
}
}
}

+ 21
- 0
app/Http/Controllers/Web/Auth/LoginParam.php View File

@@ -0,0 +1,21 @@
<?php

namespace App\Http\Controllers\Web\Auth;

use App\Http\Controllers\Web\BaseParam;
use App\Http\Controllers\Web\Rule;

/**
* @property string $email
* @property string $password
*/
class LoginParam extends BaseParam
{
public function rules(): array
{
return [
'email' => $this->str([...Rule::email()]),
'password' => $this->str(),
];
}
}

+ 33
- 0
app/Http/Controllers/Web/Auth/LogoutController.php View File

@@ -0,0 +1,33 @@
<?php

namespace App\Http\Controllers\Web\Auth;

use App\Http\Controllers\Web\WebController;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LogoutController extends WebController
{

public function name(): string
{
return "ログアウト";
}

public function description(): string
{
return "ログアウトを行う";
}

public function __construct(protected LogoutParam $param)
{
parent::__construct();
}

protected function run(Request $request): JsonResponse
{
Auth::logout();
return $this->successResponse();
}
}

+ 9
- 0
app/Http/Controllers/Web/Auth/LogoutParam.php View File

@@ -0,0 +1,9 @@
<?php

namespace App\Http\Controllers\Web\Auth;

use App\Http\Controllers\Web\NoneParams;

class LogoutParam extends NoneParams
{
}

+ 17
- 0
app/Http/Controllers/Web/Auth/Me.php View File

@@ -0,0 +1,17 @@
<?php

namespace App\Http\Controllers\Web\Auth;

use App\Exceptions\AppCommonException;
use Illuminate\Support\Facades\Auth;

trait Me
{
public function me(): array
{
if (!Auth::check()) {
throw new AppCommonException("Me失敗");
}
return Auth::user()->toArray();
}
}

+ 40
- 0
app/Http/Controllers/Web/Auth/MeController.php View File

@@ -0,0 +1,40 @@
<?php

namespace App\Http\Controllers\Web\Auth;

use App\Exceptions\AppCommonException;
use App\Http\Controllers\Web\WebController;
use App\Kintone\Models\Customer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class MeController extends WebController
{
use Me;


public function name(): string
{
return "ログインユーザー情報の参照";
}

public function description(): string
{
return "ログインユーザー情報を取得する";
}

public function __construct(protected MeParam $param)
{
parent::__construct();
}

protected function run(Request $request): JsonResponse
{
try {
return $this->successResponse($this->me());
} catch (AppCommonException) {
return $this->failedResponse();
}
}
}

+ 9
- 0
app/Http/Controllers/Web/Auth/MeParam.php View File

@@ -0,0 +1,9 @@
<?php

namespace App\Http\Controllers\Web\Auth;

use App\Http\Controllers\Web\NoneParams;

class MeParam extends NoneParams
{
}

+ 247
- 0
app/Http/Controllers/Web/BaseParam.php View File

@@ -0,0 +1,247 @@
<?php

namespace App\Http\Controllers\Web;

use App\Repositories\BaseRepository;
use App\Util\DateUtil;
use Exception;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\Enum;
use ReflectionClass;

abstract class BaseParam implements IParam
{
const REQUIRED = 'required';
const NULLABLE = 'nullable';
const STR = 'string';
const NUMERIC = 'numeric';
const DATE = 'date';
const BOOLEAN_ = 'boolean';
const IMAGE = 'image';
const FILE = 'file';
const ARRAY = 'array';

const PARAM_NAME_TIMESTAMP = 'timestamp';

private array $param = [];

abstract public function rules(): array;

public function __set($name, $value)
{
$name = Str::snake($name);
$rule = data_get($this->rules(), $name, null);
if (!$rule) {
throw new Exception('存在しないパラメータ ' . $name);
}

$this->param[$name] = $value;
}

public function __get($name)
{
return data_get($this->param, Str::snake($name), null);
}

public function toArray(bool $toCamelCase = false): array
{
if ($toCamelCase === false) {
return $this->param;
}

$ret = [];
foreach ($this->param as $key => $val) {
$camelKey = Str::camel($key);
$ret[$camelKey] = $val;
}
return $ret;
}

public function setData(array $data)
{

$dots = Arr::dot($data);

$ruleRegExs = RuleAnalyzer::convertToRegEx($this->rules());

// logger($ruleRegExs);

foreach ($dots as $name => $value) {

if ($value === null) {
data_set($this->param, $name, null);
continue;
}

$analyzer = new RuleAnalyzer($name, $ruleRegExs);

if ($analyzer->isMathed()) {
$content = $this->getSettableData($analyzer->getType(), $value);
data_set($this->param, $name, $content);
}
}

// logger($this->param);
}

private function getSettableData($rule, $value)
{
if (is_string($rule)) {
if ($rule === self::STR) {
return strval($value);
}
if ($rule === self::NUMERIC) {
return intval($value);
}
if ($rule === self::BOOLEAN_) {
return boolval($value);
}
if ($rule === self::DATE) {
if (is_string($value)) {
return DateUtil::parse($value);
} else {
return null;
}
}
if ($rule === self::IMAGE || $rule === self::FILE) {
return $value;
}
if ($rule === self::ARRAY) {
return $value;
}
} elseif ($rule instanceof Enum) {
// リフレクションを使ってEnumの型を取得する
$ref = new ReflectionClass((get_class($rule)));
$type = $ref->getProperty('type');
$type->setAccessible(true);
$enum = $type->getValue($rule);
try {
return $enum::tryFrom($value);
} catch (Exception $e) {
logs()->error('Enum パース失敗', ['rule' => $rule, 'value' => $value, 'exception' => $e->getMessage()]);
throw $e;
}
}


throw new Exception(sprintf("不正な変換 ",));
}

/**
* 排他チェック
*
* @param Carbon|null $timestamp
* @return boolean
*/
public function checkTimestamp(Carbon|null $timestamp): bool
{
if ($timestamp === null) return true;

$param = $this->__get(self::PARAM_NAME_TIMESTAMP);
if ($param === null || !$param instanceof Carbon) {
logger("無効なタイムスタンプ確認");
logger($param);
return false;
}

return $param->eq($timestamp);
}

private function isNullable(array|bool $condition, bool $nullable): bool
{
if (is_array($condition)) {
return $nullable;
} else {
return $condition;
}
}

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,
self::STR
], is_array($condition) ? $condition : []);
}
protected function numeric(array|bool $condition = [], $nullable = false): array
{
return array_merge([
$this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED,
self::NUMERIC
], is_array($condition) ? $condition : []);
}
protected function boolean(array|bool $condition = [], $nullable = false): array
{
return array_merge([
$this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED,
self::BOOLEAN_
], is_array($condition) ? $condition : []);
}
protected function array(array|bool $condition = [], $nullable = false): array
{
return array_merge([
$this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED,
self::ARRAY
], is_array($condition) ? $condition : []);
}
protected function date(array|bool $condition = [], $nullable = false): array
{
return array_merge([
$this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED,
self::DATE
], is_array($condition) ? $condition : []);
}
protected function enum(array|bool $condition = [], $nullable = false): array
{
return array_merge([
$this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED,
], is_array($condition) ? $condition : []);
}
protected function image(array|bool $condition = [], $nullable = false): array
{
return array_merge([
$this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED,
self::IMAGE
], is_array($condition) ? $condition : []);
}
protected function images(string $name, $nullable = false): array
{
$need = $nullable ? self::NULLABLE : self::REQUIRED;
return [
$name => [$need, self::ARRAY],
sprintf("%s.*", $name) => [$need, self::IMAGE]
];
}
protected function file(array|bool $condition = [], $nullable = false): array
{
return array_merge([
$this->isNullable($condition, $nullable) ? self::NULLABLE : self::REQUIRED,
self::FILE
], is_array($condition) ? $condition : []);
}

protected function sortableRules()
{
return [
BaseRepository::CONDITION_SORT_TARGET => $this->str(true),
BaseRepository::CONDITION_SORT_ORDER => $this->str(true),
BaseRepository::CONDITION_LIMIT => $this->numeric(true),
];
}

protected function timestamp(bool $nullable = false)
{
return [self::PARAM_NAME_TIMESTAMP => $this->date($nullable)];
}
}

+ 10
- 0
app/Http/Controllers/Web/IParam.php View File

@@ -0,0 +1,10 @@
<?php

namespace App\Http\Controllers\Web;

interface IParam
{
public function setData(array $data);
public function rules(): array;
public function toArray(): array;
}

+ 37
- 0
app/Http/Controllers/Web/IndexController.php View File

@@ -0,0 +1,37 @@
<?php

namespace App\Http\Controllers\Web;

use Illuminate\Routing\Controller as BaseController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class IndexController extends BaseController
{

public function description(): string
{
return "viewの返却";
}

public function entry(Request $request)
{
if (!$request->expectsJson()) {
return response()->view('index')->withHeaders($this->makeHeader());
} else {
return response()->json([], 404);
}
}

private function makeHeader(): array
{
$header = [];
$user = Auth::user();
if ($user) {
$header["Ht-User-Auth"] = sprintf("%d,%d", $user->id, $user->role->value);
} else {
$header["Ht-User-Auth"] = 'none';
}
return $header;
}
}

+ 13
- 0
app/Http/Controllers/Web/NoneParams.php View File

@@ -0,0 +1,13 @@
<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Web\BaseParam;

class NoneParams extends BaseParam
{
public function rules(): array
{
return [];
}
}

+ 68
- 0
app/Http/Controllers/Web/QRService/CreateTicketController.php View File

@@ -0,0 +1,68 @@
<?php

namespace App\Http\Controllers\Web\QRService;

use App\Http\Controllers\Web\WebController;
use App\Logics\QRService\CreateLogic;
use App\Logics\QRService\QRCryptoLogic;
use App\Models\HtpmsCustomer\QRService\AcquisitionTicket;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class CreateTicketController extends WebController
{

public function name(): string
{
return "サービス券取得";
}

public function description(): string
{
return "サービス券を取得する";
}


public function __construct(protected CreateTicketParam $param)
{
parent::__construct();
}

protected function run(Request $request): JsonResponse
{
$param = $this->param;

if ($param->ticketId) {
$ticket = AcquisitionTicket::findOrFail($param->ticketId);
if (!$ticket->canUse()) {
$ticket = CreateLogic::create($param->token);
}
} else {
$ticket = CreateLogic::create($param->token);
}

$res = [
'ticket_id' => $ticket->id,
'data' => $this->convertToQrStr($ticket),
];

return $this->successResponse($res);
}

private function convertToQrStr(AcquisitionTicket $ticket): string
{
$body = sprintf(
"%02d%s$06d%02d%02d",
"01",
$ticket->publishing_date->format('Ymd'),
$ticket->publishing_no,
$ticket->shop_no,
$ticket->discount_ticket_code,
);
return sprintf(
"HT004%s",
QRCryptoLogic::encrypt($body)
);
}
}

+ 20
- 0
app/Http/Controllers/Web/QRService/CreateTicketParam.php View File

@@ -0,0 +1,20 @@
<?php

namespace App\Http\Controllers\Web\QRService;

use App\Http\Controllers\Web\BaseParam;

/**
* @property string $token
* @property string|null $ticketId
*/
class CreateTicketParam extends BaseParam
{
public function rules(): array
{
return [
'token' => $this->str(),
'ticket_id' => $this->str(true),
];
}
}

+ 9
- 0
app/Http/Controllers/Web/RoleCheck.php View File

@@ -0,0 +1,9 @@
<?php

namespace App\Http\Controllers\Web;

use Illuminate\Support\Carbon;

class RoleCheck
{
}

+ 14
- 0
app/Http/Controllers/Web/Rule.php View File

@@ -0,0 +1,14 @@
<?php

namespace App\Http\Controllers\Web;

abstract class Rule
{
public static function email(): array
{
$ret = [];
$ret[] = "email:strict,filter,dns";
$ret[] = "max:255";
return $ret;
}
}

+ 93
- 0
app/Http/Controllers/Web/RuleAnalyzer.php View File

@@ -0,0 +1,93 @@
<?php

namespace App\Http\Controllers\Web;

use Illuminate\Support\Str;

class RuleAnalyzer
{

static public function convertToRegEx(array $rules)
{
$ret = [];
foreach ($rules as $name => $ruleList) {
$pattern = '/^' . Str::replace('*', '\d+', Str::replace('.', '\.', $name)) . '$/';
$ret[$pattern] = $ruleList;
}
return $ret;
}

private string $path;
private string $pattern;

private int|null $arrayIndex = null;

private bool $mathed = false;

private array $rules;

public function __construct(string $path, array &$rules)
{
$this->path = $path;

// パターンマッチング
foreach ($rules as $pattern => $ruleList) {
if (preg_match($pattern, $path, $matcheds)) {
$this->pattern = $pattern;
$this->rules = $ruleList;
$this->mathed = true;
break;
}
}

if (!$this->mathed) {
return;
}

// 配列インデックスの取得
$this->arrayIndex = $this->getArrayIndexFromPath($path);
}

public function isMathed()
{
return $this->mathed;
}

public function getRules()
{
return $this->rules;
}

public function getType()
{
return $this->rules[1];
}

public function isArrayMember()
{
return $this->arrayIndex !== null;
}

public function getArrayIndex()
{
return $this->arrayIndex;
}

private function getArrayIndexFromPath(string $path)
{

preg_match('/^.+\.(\d+)\.[0-9A-Za-z_]+$/', $path, $matches);
if (count($matches) === 0) {
return null;
}

return intval($matches[1]);
}

public function getArrayName()
{
$list = explode('.*.', $this->pattern);
array_pop($list);
return implode('.*.');
}
}

+ 13
- 0
app/Http/Controllers/Web/SortableParam.php View File

@@ -0,0 +1,13 @@
<?php

namespace App\Http\Controllers\Web;


/**
* @property ?string $sort
* @property ?string $order
* @property ?int $limit
*/
interface SortableParam
{
}

+ 12
- 0
app/Http/Controllers/Web/TimestampParam.php View File

@@ -0,0 +1,12 @@
<?php

namespace App\Http\Controllers\Web;

use Illuminate\Support\Carbon;

/**
* @property ?Carbon $timestamp
*/
interface TimestampParam
{
}

+ 394
- 0
app/Http/Controllers/Web/WebController.php View File

@@ -0,0 +1,394 @@
<?php

namespace App\Http\Controllers\Web;

use App\Codes\EnvironmentName;
use App\Codes\HTTPResultCode as ResultCode;
use App\Codes\UserRole;
use App\Exceptions\AppCommonException;
use App\Exceptions\ExclusiveException;
use App\Exceptions\GeneralErrorMessageException;
use App\Util\DBUtil;
use Exception;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Client\Response as ClientResponse;
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;

protected DBUtil $transaction;

/**
* 返却する結果コード
*
* @var ResultCode|null
*/
protected ResultCode|null $resultCode = ResultCode::SECCESS;

public function __construct()
{
$this->transaction = DBUtil::instance();
}


/**
* パラメータオブジェクト
*/
protected function getParam(): IParam
{
if (!property_exists(static::class, 'param')) {
throw new LogicException("param未定義");
}

$param = $this->param;

if (!is_subclass_of($param, IParam::class)) {
throw new LogicException("param型不正");
}
return $this->param;
}


/**
* コントローラーの名前
* オーバーライドされることを想定
* 主に、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|ClientResponse |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()]);
logger($request->toArray());
return $this->validateErrorResponse($e);
}

try {
$this->validated = $validator->validated();
$this->getParam()->setData($this->validated);

$this->authorize();

$this->transaction->beginTransaction();
$ret = $this->run($request);

$this->transaction->commit();
return $ret;
} catch (GeneralErrorMessageException $e) {
$this->transaction->rollBack();
return $this->failedResponse([], $e->getMessage());
} catch (AppCommonException $e) {
$this->transaction->rollBack();
logs()->error(sprintf("Appエラー:%s File:%s Line:%d", $e->getMessage(), $e->getFile(), $e->getLine()));
return $this->failedResponse();
} catch (ExclusiveException $e) {
$this->transaction->rollBack();
logs()->error(sprintf("排他エラー:%s", $e->getMessage()));
return $this->exclusiveErrorResponse();
} catch (LogicException $e) {
$this->transaction->rollBack();
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) {
$this->transaction->rollBack();
return $this->validateErrorResponse($e);
} catch (HttpException $e) {
$this->transaction->rollBack();
if ($e->getStatusCode() === 401) {
return $this->unAuthorizedResponse();
}
throw $e;
} catch (Exception $e) {
$this->transaction->rollBack();
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();
}

protected 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);
}

if (request()->wantsJson()) {
return response()
->json($ret)
->withHeaders($this->makeHeader());
} else {

if (app()->environment([EnvironmentName::PRODUCTOIN->value])) {
abort(500);
}
return response()
->json($ret)
->withHeaders($this->makeHeader());
}
}

private function makeHeader(): array
{
$header = [];
$user = Auth::user();
if ($user) {
$header["App-User-Auth"] = sprintf("%s", $user->id);
} else {
$header["App-User-Auth"] = 'none';
}
return $header;
}

// 以下 認可関係
protected array|null $roleAllow = null;
protected array|null $roleDisallow = null;
protected array|null $customAllow = 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;
}
}

// 返却用データの登録
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(),
]);
}
}

+ 68
- 0
app/Http/Kernel.php View File

@@ -0,0 +1,68 @@
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];

/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],

'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];

/**
* The application's middleware aliases.
*
* Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
*
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
}

+ 17
- 0
app/Http/Middleware/Authenticate.php View File

@@ -0,0 +1,17 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;

class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*/
protected function redirectTo(Request $request): ?string
{
return $request->expectsJson() ? null : route('login');
}
}

+ 17
- 0
app/Http/Middleware/EncryptCookies.php View File

@@ -0,0 +1,17 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

+ 17
- 0
app/Http/Middleware/PreventRequestsDuringMaintenance.php View File

@@ -0,0 +1,17 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;

class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

+ 30
- 0
app/Http/Middleware/RedirectIfAuthenticated.php View File

@@ -0,0 +1,30 @@
<?php

namespace App\Http\Middleware;

use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;

class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;

foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}

return $next($request);
}
}

+ 19
- 0
app/Http/Middleware/TrimStrings.php View File

@@ -0,0 +1,19 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;

class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

+ 20
- 0
app/Http/Middleware/TrustHosts.php View File

@@ -0,0 +1,20 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Http\Middleware\TrustHosts as Middleware;

class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts(): array
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

+ 28
- 0
app/Http/Middleware/TrustProxies.php View File

@@ -0,0 +1,28 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;

class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;

/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

+ 22
- 0
app/Http/Middleware/ValidateSignature.php View File

@@ -0,0 +1,22 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Routing\Middleware\ValidateSignature as Middleware;

class ValidateSignature extends Middleware
{
/**
* The names of the query string parameters that should be ignored.
*
* @var array<int, string>
*/
protected $except = [
// 'fbclid',
// 'utm_campaign',
// 'utm_content',
// 'utm_medium',
// 'utm_source',
// 'utm_term',
];
}

+ 16
- 0
app/Http/Middleware/VerifyCsrfToken.php View File

@@ -0,0 +1,16 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{

/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [];
}

+ 37
- 0
app/Listeners/Email/EmailSendJobRegister.php View File

@@ -0,0 +1,37 @@
<?php

namespace App\Listeners\Email;

use App\Events\Email\ConfirmEvent;
use App\Jobs\Email\SimpleEmail;
use App\Util\DateUtil;

class EmailSendJobRegister
{

/**
* 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();

SimpleEmail::dispatch($event->email);
}
}
}

+ 15
- 0
app/Listeners/Model/CreatedListener.php View File

@@ -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);
}
}

+ 20
- 0
app/Listeners/Model/CreatingListener.php View File

@@ -0,0 +1,20 @@
<?php

namespace App\Listeners\Model;

use App\Events\Model\CreatingEvent;
use App\Models\User;
use Illuminate\Support\Facades\Hash;

class CreatingListener extends ModelListener
{
protected const ACTION = '作成中';

public function handle(CreatingEvent $event): void
{
// ログインパスワードのハッシュ化
if ($event->model instanceof User) {
$event->model->password = Hash::make($event->model->password);
}
}
}

+ 15
- 0
app/Listeners/Model/DeletedListener.php View File

@@ -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);
}
}

+ 15
- 0
app/Listeners/Model/DeletingListener.php View File

@@ -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);
}
}

+ 48
- 0
app/Listeners/Model/ModelListener.php View File

@@ -0,0 +1,48 @@
<?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);
}

protected function createHistory(IModelFeature $model)
{

$history = $model->getHistory();
$changeMessage = "";
if ($history !== null) {
$history->fillFromOrigin($model);
$history->save();

if ($model instanceof Model) {
$before = $model->getOriginal();
$after = $model;
$message = $model->getChangeLogMessage($before, $after);
if ($message !== null) {
$changeMessage = sprintf("[%s]", $message);
}
}
}
Log::debug(sprintf(
"モデル変更検知[%s][%s][ID:%s]%s",
$model->getModelName(),
static::ACTION,
data_get($model, ColumnName::ID),
$changeMessage
));
}
}

+ 26
- 0
app/Listeners/Model/UpdatingListener.php View File

@@ -0,0 +1,26 @@
<?php

namespace App\Listeners\Model;

use App\Events\Model\UpdatingEvent;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

class UpdatingListener extends ModelListener
{
protected const ACTION = '更新';

public function handle(UpdatingEvent $event): void
{
// 更新者作成者の設定
if (Auth::check()) {
$id = Auth::id();
$event->model->updated_by = $id;
if ($event->model->created_by === null) {
$event->model->created_by = $id;
}
}
$this->handleEvent($event->model);
}
}

+ 115
- 0
app/Logics/QRService/CertificateLogic.php View File

@@ -0,0 +1,115 @@
<?php

namespace App\Logics\QRService;

use App\Exceptions\AppCommonException;
use App\Models\HtpmsCustomer\Mst\ShopNoRelation;
use App\Models\HtpmsCustomer\QRService\CertificationAvailableSetting;
use App\Models\HtpmsCustomer\QRService\CertificationTicket;
use App\Util\DateUtil;
use Illuminate\Support\Carbon;

class CertificateLogic
{
use DepositCheck;

public static function certificate(
string $parkingManagementCode,
string $adjusterTerminalCode,
Carbon $publishingDate,
int $seqNo,
string $shopId,
int $discountTicketCode
) {


// 割引設定有無の確認
$check = CertificationAvailableSetting::whereParkingManagementCode($parkingManagementCode)
->whereShopId($shopId)
->whereDiscountTicketCode($discountTicketCode)
->exists();

if (!$check) {
throw new AppCommonException("認証できない割引");
}

// デポジットチェック
if (!self::canCertificate($shopId)) {
throw new AppCommonException("認証不可 デポジット");
}

[$shop] = self::getData($shopId);



$qr = new CertificationTicket();
$qr->parking_management_code = $parkingManagementCode;
$qr->publishing_terminal_code = $adjusterTerminalCode;
$qr->publishing_date = $publishingDate;
$qr->publishing_no = $seqNo;
$qr->shop_id = $shopId;
$qr->discount_ticket_code = $discountTicketCode;

// 店舗番号の解決
$relation = ShopNoRelation::whereShopId($shopId)
->whereParkingManagementCode($parkingManagementCode)
->first();
if ($relation instanceof ShopNoRelation === false) {
throw new AppCommonException("店舗番号紐づけ未設定");
}
$qr->shop_no = $relation->shop_no;

// 有効期限設定
$qr->expires_at = DateUtil::now()->addMinutes($shop->qr_service_expire_min);
$qr->save();

return $qr;
}

public static function getUsable(
string $parkingManagementCode,
string $adjusterTerminalCode,
Carbon $publishingDate,
int $seqNo
): CertificationTicket {

$qr = CertificationTicket::whereParkingManagementCode($parkingManagementCode)
->wherePublishingTerminalCode($adjusterTerminalCode)
->wherePublishingDate($publishingDate)
->wherePublishingNo($seqNo)
->first();

if ($qr instanceof CertificationTicket === false) {
throw new AppCommonException("QR 認証なし");
}
if ($qr->iseExpired()) {
throw new AppCommonException("QR 期限切れ");
}
if ($qr->isUsed()) {
throw new AppCommonException("QR 使用済み");
}

return $qr;
}

public static function use(
string $parkingManagementCode,
string $adjusterTerminalCode,
Carbon $publishingDate,
int $seqNo,
int $discountTicketCode,
Carbon $adjustDatetime,
int $discountAmount,
) {
// QRコード情報の取得
$qr = self::getUsable($parkingManagementCode, $adjusterTerminalCode, $publishingDate, $seqNo);

// デポジット処理
self::useDeposit($qr->shop_id, $discountAmount);
$qr->used_at = $adjustDatetime;
$qr->discount_amount = $discountAmount;
$qr->discount_ticket_code = $discountTicketCode;

$qr->save();
}
}

+ 179
- 0
app/Logics/QRService/CreateLogic.php View File

@@ -0,0 +1,179 @@
<?php

namespace App\Logics\QRService;

use App\Exceptions\AppCommonException;
use App\Models\ColumnName;
use App\Models\HtpmsCustomer\QRService\AcquisitionAvailableSetting;
use App\Models\HtpmsCustomer\QRService\AcquisitionTicket;
use App\Models\HtpmsCustomer\QRService\AcquisitionTicketToken;
use App\Models\HtpmsCustomer\QRService\ServiceParkingGroupRelation;
use App\Util\DateUtil;
use Illuminate\Support\Carbon;
use Str;

class CreateLogic
{
use DepositCheck;

/**
* サービス券を作成する
*/
public static function create(
string $token,
) {

// トークンから店舗ID検索
$t = AcquisitionTicketToken::whereToken($token)->first();
if ($t instanceof AcquisitionTicketToken === false) {
throw new AppCommonException(sprintf("トークン不正 %s", $token));
}
$shopId = $t->shop_id;


// 割引設定有無の確認
$check = AcquisitionAvailableSetting::whereShopId($shopId)
->first();

if ($check instanceof AcquisitionAvailableSetting === false) {
throw new AppCommonException("取得できない割引");
}

// デポジットチェック
if (!self::canCreate($shopId)) {
throw new AppCommonException("認証不可 デポジット");
}

[$shop] = self::getData($shopId);

// 設定値の検索
$setting = AcquisitionAvailableSetting::whereShopId($shopId)
->first();
if ($setting instanceof AcquisitionAvailableSetting === false) {
throw new AppCommonException("QRサービス券取得可能設定 不正");
}
$qr = new AcquisitionTicket();
$qr->qr_service_parking_group_id = $check->qr_service_parking_group_id;
$qr->publishing_no = self::getNextSeqNo($setting->shop_no);
$qr->shop_id = $shopId;

$qr->shop_no = $setting->shop_no;
$qr->discount_ticket_code = $setting->discount_ticket_code;
$qr->qr_service_parking_group_id = $setting->qr_service_parking_group_id;

// 有効期限設定
$qr->expires_at = DateUtil::now()->addMinutes($shop->qr_service_expire_min);
$qr->publishing_date = DateUtil::now();
$qr->save();

return $qr;
}

public static function getUsable(int $shopNo, Carbon $publishingDate, int $seqNo): AcquisitionTicket
{
$qr = AcquisitionTicket::query()
->whereShopNo($shopNo)
->wherePublishingDate($publishingDate)
->wherePublishingNo($seqNo)
->lockForUpdate()
->first();

if ($qr instanceof AcquisitionTicket === false) {
throw new AppCommonException("QR 認証なし");
}
if ($qr->iseExpired()) {
throw new AppCommonException("QR 期限切れ");
}
if ($qr->isUsed()) {
throw new AppCommonException("QR 使用済み");
}

return $qr;
}

/**
* サービス券を使用する
*
* @param integer $shopNo
* @param string $parkingManagementCode
* @param Carbon $publishingDate
* @param integer $seqNo
* @param integer $discountTicketCode
* @param Carbon $adjustDatetime
* @param integer $discountAmount
* @return void
*/
public static function use(
int $shopNo,
string $parkingManagementCode,
Carbon $publishingDate,
int $seqNo,
int $discountTicketCode,
Carbon $adjustDatetime,
int $discountAmount,
) {
// QRコード情報の取得
$qr = self::getUsable($shopNo, $publishingDate, $seqNo);

// QRコードサービス券駐車場グループに含まれているかチェック
self::checkQrParkingGroup($qr->qr_service_parking_group_id, $parkingManagementCode);

// デポジット処理
self::useDeposit($qr->shop_id, $discountAmount);
$qr->discount_amount = $discountAmount;

// 利用情報
$qr->used_at = $adjustDatetime;
$qr->discount_ticket_code = $discountTicketCode;
$qr->parking_management_code = $parkingManagementCode;

$qr->save();
}

public static function getToken(string $shopId, bool $refresh = false): AcquisitionTicketToken
{
$token = AcquisitionTicketToken::whereShopId($shopId)
->first();
if ($refresh) {
if ($token instanceof AcquisitionTicketToken === true) {
$token->delete();
$token = null;
}
}

if ($token instanceof AcquisitionTicketToken) {
return $token;
}

// トークン新規作成
$token = new AcquisitionTicketToken();
$token->token = Str::uuid()->toString();
$token->shop_id = $shopId;
$token->save();

return $token;
}

private static function getNextSeqNo(int $shopNo)
{
$max = AcquisitionTicket::query()
->whereShopNo($shopNo)
->wherePublishingDate(DateUtil::now())
->max(ColumnName::PUBLISHING_NO);

if ($max === null) return 1;
return $max + 1;
}

private static function checkQrParkingGroup(string $groupId, string $parkingManagementCode)
{

$query = ServiceParkingGroupRelation::query()
->whereQrServiceParkingGroupId($groupId)
->whereParkingManagementCode($parkingManagementCode);

if (!$query->exists()) {
throw new AppCommonException("利用できない駐車場");
}
}
}

+ 114
- 0
app/Logics/QRService/DepositCheck.php View File

@@ -0,0 +1,114 @@
<?php

namespace App\Logics\QRService;

use App\Contexts\Model\Deposit as ContextDeposit;
use App\Contexts\Model\Shop as ContextShop;
use App\Exceptions\AppCommonException;
use App\Models\HtpmsCustomer\Deposit\Deposit;
use App\Models\HtpmsCustomer\Deposit\DepositTransfer;
use App\Models\HtpmsCustomer\Mst\Shop;
use App\Util\DateUtil;
use LogicException;

trait DepositCheck
{
protected static function canCreate(string $shopId): bool
{
[$shop, $deposit] = self::getData($shopId);
return self::checkDepositWhenCertificate($shop, $deposit);
}
protected static function canCertificate(string $shopId): bool
{
[$shop, $deposit] = self::getData($shopId);
return self::checkDepositWhenCertificate($shop, $deposit);
}
protected static function canUseDeposit(string $shopId, $amount): bool
{
[$shop, $deposit] = self::getData($shopId);
return self::checkDepositWhenUse($shop, $deposit, $amount);
}

protected static function useDeposit(
string $shopId,
int $amount,
) {
// データ取得
[$shop, $deposit] = self::getData($shopId);

if (!self::checkDepositWhenUse($shop, $deposit, $amount)) {
throw new AppCommonException("利用時残高不足");
}

// 異動履歴作成
$history = self::makeTransferHistory($shopId, -1 * $amount);

// デポジット減算
$deposit->deposit -= $amount;

// 保存
$deposit->save();
$history->save();
}

/**
* デポジット異動履歴の作成 amountはdepositを使う場合は負の整数、チャージする場合は正の整数
*/
protected static function makeTransferHistory(string $shopId, int $amount): DepositTransfer
{
[$shop, $deposit] = self::getData($shopId);
$transfer = new DepositTransfer();
$transfer->shop_id = $shopId;
$transfer->transfer_datetime = DateUtil::now();
$transfer->transfer_amount = -1 * $amount;
$transfer->before_amount = $deposit->deposit;
$transfer->after_amount = $deposit->deposit + $amount;
return $transfer;
}

/**
* @param string $shopId
* @return array{Shop, Deposit}
*/
protected static function getData(string $shopId)
{
// データ取得 コンテキストから取得する

$shop = ContextShop::instance()->get();
$deposit = ContextDeposit::instance()->get();

if ($shop === null) {
$shop = Shop::whereId($shopId)->firstOrFail();
ContextShop::instance()->set($shop);
} else {
// 確認
if ($shopId !== $shop->id) {
throw new LogicException("コンテキスト不正");
}
}
if ($deposit === null) {
$deposit = Deposit::whereShopId($shopId)->firstOrFail();
ContextDeposit::instance()->set($deposit);
} else {
// 確認
if ($shopId !== $deposit->shop_id) {
throw new LogicException("コンテキスト不正");
}
}

return [$shop, $deposit];
}

private static function checkDepositWhenUse(Shop $shop, Deposit $deposit, int $amount)
{
return $shop->under_amount_when_use < $deposit->deposit - $amount;
}
private static function checkDepositWhenCertificate(Shop $shop, Deposit $deposit)
{
return $shop->under_amount_when_auth < $deposit->deposit;
}
private static function checkDepositWhenCreate(Shop $shop, Deposit $deposit)
{
return $shop->under_amount_when_create < $deposit->deposit;
}
}

+ 25
- 0
app/Logics/QRService/QRCryptoLogic.php View File

@@ -0,0 +1,25 @@
<?php

namespace App\Logics\QRService;

class QRCryptoLogic
{

const CIPHER = "aes-256-ecb";
const KEY = "axT59AhYNaxyK/X1fpQhEQ==";

static public function encrypt(string $source)
{
$key = self::KEY;
$binary = openssl_encrypt($source, self::CIPHER, $key, OPENSSL_RAW_DATA);

return base64_encode($binary);
}

static public function decrypt(string $source)
{
$key = self::KEY;
$binary = base64_decode($source);
return openssl_decrypt($binary, self::CIPHER, $key, OPENSSL_RAW_DATA);
}
}

+ 48
- 0
app/Models/AppModel.php View File

@@ -0,0 +1,48 @@
<?php

namespace App\Models;

use Exception;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;

abstract class AppModel extends BaseModel
{
use SoftDeletes, HasUuids;

public function getHistory(): ?HistoryModel
{
$historyName = static::class . 'History';

$history = new $historyName;

if ($history instanceof HistoryModel) {
if (Auth::check()) {
$id = Auth::id();
$history->updated_by = $id;
$history->created_by = $id;
}
return $history;
} else {
throw new Exception("履歴モデル不正");
}
}

public function getChangeLogMessage($before, $after): ?string
{
return null;
}

public function setId(?string $uuid = null)
{
if ($this->id !== null) return;

if ($uuid) {
$this->id = $uuid;
} else {
$this->id = Str::uuid();
}
}
}

+ 95
- 0
app/Models/BaseModel.php View File

@@ -0,0 +1,95 @@
<?php

namespace App\Models;

use App\Events\Model\CreatedEvent;
use App\Events\Model\CreatingEvent;
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;
use Illuminate\Support\Str;

/**
* @mixin \Eloquent
*/
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)
->whereNull($name . "." . static::COL_NAME_DELETED_AT);
}

public static function getTableName(): string
{
return (new static)->getTable();
}

public static function hasColumn(string $columnName): bool
{
$target = sprintf("%s::COL_NAME_%s", static::class, Str::upper($columnName));
$ret = defined($target);
return $ret;
}


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 = [
'creating' => CreatingEvent::class,
'created' => CreatedEvent::class,
'updating' => UpdatingEvent::class,
'deleted' => DeletedEvent::class,
];


// カラムが存在する項目のみfillするようオーバーライド
public function fill(array $atr)
{
$filterd = array_filter($atr, function ($value, $key) {
return static::hasColumn($key);
}, ARRAY_FILTER_USE_BOTH);

return parent::fill($filterd);
}
}

+ 10
- 0
app/Models/Cast.php View File

@@ -0,0 +1,10 @@
<?php

namespace App\Models;

abstract class Cast
{
const DATE = "date";
const DATETIME = "datetime";
const HASHED = "hashed";
}

+ 30
- 0
app/Models/ColumnName.php View File

@@ -0,0 +1,30 @@
<?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 USER_ID = 'user_id';
const EMAIL_ID = "email_id";
const SHOP_ID = "shop_id";
const SHOP_NO = "shop_no";
const PARKING_MANAGEMENT_CODE = "parking_management_code"; // 駐車場管理コード
const DISCOUNT_TICKET_PARKING_CODE = "discount_ticket_parking_code"; // サービス券駐車場コード
const DISCOUNT_TICKET_CODE = "discount_ticket_code"; // サービス券コード
const QR_SERVICE_PARKING_GROUP_ID = "qr_service_parking_group_id"; // QRサービス券駐車場グループID
const PUBLISHING_TERMINAL_CODE = "publishing_terminal_code"; // 発行端末番号
const PUBLISHING_DATE = "publishing_date"; // QRサービス券発行日
const PUBLISHING_NO = "publishing_no"; // QRサービス券発行日


}

+ 38
- 0
app/Models/Feature/IModelFeature.php View File

@@ -0,0 +1,38 @@
<?php

namespace App\Models\Feature;

use App\Models\HistoryModel;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Carbon;

/**
* @property ?Carbon $updated_at
* @property ?Carbon $created_at
* @property ?string $updated_by
* @property ?string $created_by
*/
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;
}

+ 13
- 0
app/Models/Feature/UserId.php View File

@@ -0,0 +1,13 @@
<?php

namespace App\Models\Feature;

use App\Models\User;

trait UserId
{
public function user()
{
return $this->belongsTo(User::class);
}
}

+ 39
- 0
app/Models/HistoryModel.php View File

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

+ 17
- 0
app/Models/Htpms/MstCustomer.php View File

@@ -0,0 +1,17 @@
<?php

namespace App\Models\Htpms;

use Illuminate\Database\Eloquent\Model;

class MstCustomer extends Model
{
const COL_NAME_ID = 'id';
const COL_NAME_CUSTOMER_ID = 'customer_id';
const COL_NAME_CUSTOMER_NAME = 'customer_name';

protected $connection = 'htpms';
protected $table = 'mst_customer';
protected $fillable = []; // 参照専用

}

+ 28
- 0
app/Models/HtpmsCustomer/Deposit/Deposit.php View File

@@ -0,0 +1,28 @@
<?php

namespace App\Models\HtpmsCustomer\Deposit;

use App\Models\ColumnName;
use App\Models\HistoryModel;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* デポジット
*/
class Deposit extends HtpmsCustomerAppModel
{
const COL_NAME_SHOP_ID = ColumnName::SHOP_ID; // 店舗ID
const COL_NAME_DEPOSIT = "deposit"; // デポジット残高

protected $table = "tbl3_dep_deposits";

public function getHistory(): ?HistoryModel
{
return null;
}

public function getModelName(): string
{
return "デポジット";
}
}

+ 36
- 0
app/Models/HtpmsCustomer/Deposit/DepositTransfer.php View File

@@ -0,0 +1,36 @@
<?php

namespace App\Models\HtpmsCustomer\Deposit;

use App\Models\Cast;
use App\Models\ColumnName;
use App\Models\HistoryModel;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* デポジット異動履歴
*/
class DepositTransfer extends HtpmsCustomerAppModel
{
const COL_NAME_SHOP_ID = ColumnName::SHOP_ID; // 店舗ID
const COL_NAME_TRANSFER_DATETIME = "transfer_datetime"; // 異動日時
const COL_NAME_TRANSFER_AMOUNT = "transfer_amount"; // デポジット残高
const COL_NAME_BEFORE_AMOUNT = "before_amount"; // 異動前デポジット
const COL_NAME_AFTER_AMOUNT = "after_amount"; // 異動後デポジット

protected $table = "tbl3_dep_deposit_transfers";

protected $casts = [
self::COL_NAME_TRANSFER_DATETIME => Cast::DATETIME,
];

public function getHistory(): ?HistoryModel
{
return null;
}

public function getModelName(): string
{
return "デポジット異動履歴";
}
}

+ 16
- 0
app/Models/HtpmsCustomer/Existing/Parking.php View File

@@ -0,0 +1,16 @@
<?php

namespace App\Models\HtpmsCustomer\Existing;

use Illuminate\Database\Eloquent\Model;

class Parking extends Model
{
const COL_NAME_PARKING_MANAGEMENT_CODE = 'park_code';
const COL_NAME_PARKING_NAME = 'park_name';

protected $connection = 'htpms_customer';
protected $table = 'tbl_park';
protected $fillable = []; // 参照専用

}

+ 10
- 0
app/Models/HtpmsCustomer/HtpmsCustomerAppModel.php View File

@@ -0,0 +1,10 @@
<?php

namespace App\Models\HtpmsCustomer;

use App\Models\AppModel;

abstract class HtpmsCustomerAppModel extends AppModel
{
protected $connection = 'htpms_customer';
}

+ 25
- 0
app/Models/HtpmsCustomer/HtpmsCustomerConnectionSwitch.php View File

@@ -0,0 +1,25 @@
<?php

namespace App\Models\HtpmsCustomer;

use Illuminate\Support\Facades\DB;

class HtpmsCustomerConnectionSwitch
{
public static function switch(int $customerId): void
{
$connectionsRoot = "database.connections";
$connectionHtmsCustomer = "htpms_customer";

$databaseNameKey = "{$connectionsRoot}.{$connectionHtmsCustomer}.database";
$currentDatabaseName = config($databaseNameKey);

$afterDatabaseName = "htpms_{$customerId}";

if ($currentDatabaseName !== $afterDatabaseName) {
$conf = [$databaseNameKey => $afterDatabaseName];
config($conf);
DB::reconnect($connectionHtmsCustomer);
}
}
}

+ 10
- 0
app/Models/HtpmsCustomer/HtpmsCustomerHistoryModel.php View File

@@ -0,0 +1,10 @@
<?php

namespace App\Models\HtpmsCustomer;

use App\Models\HistoryModel;

abstract class HtpmsCustomerHistoryModel extends HistoryModel
{
protected $connection = "htpms_customer";
}

+ 25
- 0
app/Models/HtpmsCustomer/Mst/Shop.php View File

@@ -0,0 +1,25 @@
<?php

namespace App\Models\HtpmsCustomer\Mst;

use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* 店舗マスタ
*/
class Shop extends HtpmsCustomerAppModel
{
const COL_NAME_NAME = "name"; // 名称
const COL_NAME_MEMO = "memo"; // メモ
const COL_NAME_QR_SERVICE_EXPIRE_MIN = "qr_service_expire_min"; // QRサービス券有効期限
const COL_NAME_UNDER_AMOUNT_WHEN_CREATE = "under_amount_when_create"; //発行時デポジット下限値
const COL_NAME_UNDER_AMOUNT_WHEN_AUTH = "under_amount_when_auth"; //認証時デポジット下限値
const COL_NAME_UNDER_AMOUNT_WHEN_USE = "under_amount_when_use"; // 利用時デポジット下限値

protected $table = "tbl3_mst_shops";

public function getModelName(): string
{
return "店舗マスタ";
}
}

+ 18
- 0
app/Models/HtpmsCustomer/Mst/ShopHistory.php View File

@@ -0,0 +1,18 @@
<?php

namespace App\Models\HtpmsCustomer\Mst;

use App\Models\HtpmsCustomer\HtpmsCustomerHistoryModel;

/**
* 店舗マスタ履歴
*/
class ShopHistory extends HtpmsCustomerHistoryModel
{
protected $table = "tbl3_mst_shop_histories";

public function getModelName(): string
{
return "店舗マスタ履歴";
}
}

+ 23
- 0
app/Models/HtpmsCustomer/Mst/ShopNoRelation.php View File

@@ -0,0 +1,23 @@
<?php

namespace App\Models\HtpmsCustomer\Mst;

use App\Models\ColumnName;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* 店舗番号紐づけ
*/
class ShopNoRelation extends HtpmsCustomerAppModel
{
const COL_NAME_SHOP_ID = ColumnName::SHOP_ID;
const COL_NAME_PARKING_MANAGEMENT_CODE = ColumnName::PARKING_MANAGEMENT_CODE;
const COL_NAME_SHOP_NO = ColumnName::SHOP_NO;

protected $table = "tbl3_mst_shop_no_relations";

public function getModelName(): string
{
return "店舗番号紐づけ";
}
}

+ 18
- 0
app/Models/HtpmsCustomer/Mst/ShopNoRelationHistory.php View File

@@ -0,0 +1,18 @@
<?php

namespace App\Models\HtpmsCustomer\Mst;

use App\Models\HtpmsCustomer\HtpmsCustomerHistoryModel;

/**
* 店舗番号紐づけ履歴
*/
class ShopNoRelationHistory extends HtpmsCustomerHistoryModel
{
protected $table = "tbl3_mst_shop_no_relation_histories";

public function getModelName(): string
{
return "店舗マスタ履歴";
}
}

+ 26
- 0
app/Models/HtpmsCustomer/QRService/AcquisitionAvailableSetting.php View File

@@ -0,0 +1,26 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\ColumnName;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* QRサービス券取得可能設定
*/
class AcquisitionAvailableSetting extends HtpmsCustomerAppModel
{
const COL_NAME_SHOP_ID = ColumnName::SHOP_ID; // 店舗ID
const COL_NAME_QR_SERVICE_PARKING_GROUP_ID = ColumnName::QR_SERVICE_PARKING_GROUP_ID; // 駐車場管理コード
const COL_NAME_SHOP_NO = ColumnName::SHOP_NO; // 店舗番号
const COL_NAME_DISCOUNT_TICKET_CODE = ColumnName::DISCOUNT_TICKET_CODE; // サービス券コード


protected $table = "tbl3_qrs_acquisition_available_settings";


public function getModelName(): string
{
return "QRサービス券取得可能設定";
}
}

+ 18
- 0
app/Models/HtpmsCustomer/QRService/AcquisitionAvailableSettingHistory.php View File

@@ -0,0 +1,18 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\HtpmsCustomer\HtpmsCustomerHistoryModel;

/**
* QRサービス券取得可能設定履歴
*/
class AcquisitionAvailableSettingHistory extends HtpmsCustomerHistoryModel
{
protected $table = "tbl3_qrs_acquisition_available_setting_histories";

public function getModelName(): string
{
return "QRサービス券取得可能設定履歴";
}
}

+ 77
- 0
app/Models/HtpmsCustomer/QRService/AcquisitionTicket.php View File

@@ -0,0 +1,77 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\Cast;
use App\Models\ColumnName;
use App\Models\HistoryModel;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;
use App\Util\DateUtil;
use Illuminate\Support\Carbon;

/**
* 取得済みQRサービス券
*/
class AcquisitionTicket extends HtpmsCustomerAppModel
{
const COL_NAME_SHOP_ID = ColumnName::SHOP_ID; // 店舗ID
const COL_NAME_PUBLISHING_DATE = ColumnName::PUBLISHING_DATE;
const COL_NAME_PUBLISHING_NO = ColumnName::PUBLISHING_NO;
const COL_NAME_QR_SERVICE_PARKING_GROUP_ID = ColumnName::QR_SERVICE_PARKING_GROUP_ID;
const COL_NAME_DISCOUNT_TICKET_CODE = ColumnName::DISCOUNT_TICKET_CODE; // サービス券コード
const COL_NAME_SHOP_NO = ColumnName::SHOP_NO; // 店舗番号
const COL_NAME_EXPIRES_AT = "expires_at"; // 有効期限
const COL_NAME_PARKING_MANAGEMENT_CODE = ColumnName::PARKING_MANAGEMENT_CODE; // 駐車場管理コード
const COL_NAME_USED_AT = "used_at"; // 利用日時
const COL_NAME_DISCOUNT_AMOUNT = "discount_amount"; // 割引金額

protected $table = "tbl3_qrs_acquisition_tickets";

protected $casts = [
self::COL_NAME_PUBLISHING_DATE => Cast::DATE,
self::COL_NAME_EXPIRES_AT => Cast::DATETIME,
self::COL_NAME_USED_AT => Cast::DATETIME,
];

public function getHistory(): ?HistoryModel
{
return null;
}

public function getModelName(): string
{
return "取得済みQRサービス券";
}

/**
* 期限切れ判定 切れている場合trueを返却
*/
public function iseExpired(?Carbon $基準時刻 = null): bool
{
if ($this->expires_at === null) return false;

if ($基準時刻 === null) {
$基準時刻 = DateUtil::now();
}

if ($this->expires_at < $基準時刻) {
return true;
}
return false;
}

/**
* 使用済み判定 使用済みの場合trueを返却
*
* @return boolean
*/
public function isUsed(): bool
{
return $this->used_at !== null;
}

public function canUse(): bool
{
return $this->isUsed() === false && $this->iseExpired() === false;
}
}

+ 28
- 0
app/Models/HtpmsCustomer/QRService/AcquisitionTicketToken.php View File

@@ -0,0 +1,28 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\ColumnName;
use App\Models\HistoryModel;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* QRサービス券取得用トークン
*/
class AcquisitionTicketToken extends HtpmsCustomerAppModel
{
const COL_NAME_SHOP_ID = ColumnName::SHOP_ID;
const COL_NAME_TOKEN = "token";

protected $table = "tbl3_qrs_acquisition_ticket_tokens";

public function getHistory(): ?HistoryModel
{
return null;
}

public function getModelName(): string
{
return "QRサービス券取得用トークン";
}
}

+ 26
- 0
app/Models/HtpmsCustomer/QRService/CertificationAvailableSetting.php View File

@@ -0,0 +1,26 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\ColumnName;
use App\Models\HistoryModel;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* QRサービス券認証可能設定
*/
class CertificationAvailableSetting extends HtpmsCustomerAppModel
{
const COL_NAME_SHOP_ID = ColumnName::SHOP_ID; // 店舗ID
const COL_NAME_PARKING_MANAGEMENT_CODE = ColumnName::PARKING_MANAGEMENT_CODE; // 駐車場管理コード
const COL_NAME_DISCOUNT_TICKET_CODE = ColumnName::DISCOUNT_TICKET_CODE; // サービス券コード


protected $table = "tbl3_qrs_certification_available_settings";


public function getModelName(): string
{
return "QRサービス券認証可能設定";
}
}

+ 18
- 0
app/Models/HtpmsCustomer/QRService/CertificationAvailableSettingHistory.php View File

@@ -0,0 +1,18 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\HtpmsCustomer\HtpmsCustomerHistoryModel;

/**
* QRサービス券認証可能設定履歴
*/
class CertificationAvailableSettingHistory extends HtpmsCustomerHistoryModel
{
protected $table = "tbl3_qrs_certification_available_setting_histories";

public function getModelName(): string
{
return "QRサービス券認証可能設定履歴";
}
}

+ 72
- 0
app/Models/HtpmsCustomer/QRService/CertificationTicket.php View File

@@ -0,0 +1,72 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\Cast;
use App\Models\ColumnName;
use App\Models\HistoryModel;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;
use App\Util\DateUtil;
use Illuminate\Support\Carbon;

/**
* 認証済みQRサービス券
*/
class CertificationTicket extends HtpmsCustomerAppModel
{
const COL_NAME_PARKING_MANAGEMENT_CODE = ColumnName::PARKING_MANAGEMENT_CODE; // 駐車場管理コード
const COL_NAME_DISCOUNT_TICKET_CODE = ColumnName::DISCOUNT_TICKET_CODE; // サービス券コード
const COL_NAME_PUBLISHING_TERMINAL_CODE = ColumnName::PUBLISHING_TERMINAL_CODE;
const COL_NAME_PUBLISHING_DATE = ColumnName::PUBLISHING_DATE;
const COL_NAME_PUBLISHING_NO = ColumnName::PUBLISHING_NO;
const COL_NAME_SHOP_ID = ColumnName::SHOP_ID; // 店舗ID
const COL_NAME_SHOP_NO = ColumnName::SHOP_NO; // 店舗番号
const COL_NAME_EXPIRES_AT = "expires_at"; // 有効期限
const COL_NAME_USED_AT = "used_at"; // 利用日時
const COL_NAME_DISCOUNT_AMOUNT = "discount_amount"; // 割引金額

protected $table = "tbl3_qrs_certification_tickets";

protected $casts = [
self::COL_NAME_PUBLISHING_DATE => Cast::DATE,
self::COL_NAME_EXPIRES_AT => Cast::DATETIME,
self::COL_NAME_USED_AT => Cast::DATETIME,
];

public function getHistory(): ?HistoryModel
{
return null;
}

public function getModelName(): string
{
return "認証済みQRサービス券";
}

/**
* 期限切れ判定 切れている場合trueを返却
*/
public function iseExpired(?Carbon $基準時刻 = null): bool
{
if ($this->expires_at === null) return false;

if ($基準時刻 === null) {
$基準時刻 = DateUtil::now();
}

if ($this->expires_at < $基準時刻) {
return true;
}
return false;
}

/**
* 使用済み判定 使用済みの場合trueを返却
*
* @return boolean
*/
public function isUsed(): bool
{
return $this->used_at !== null;
}
}

+ 23
- 0
app/Models/HtpmsCustomer/QRService/ServiceParkingGroup.php View File

@@ -0,0 +1,23 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\ColumnName;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* QRサービス券駐車場グループ
*/
class ServiceParkingGroup extends HtpmsCustomerAppModel
{
const COL_NAME_NAME = 'name'; // グループ名


protected $table = "tbl3_qrs_service_parking_groups";


public function getModelName(): string
{
return "QRサービス券駐車場グループ";
}
}

+ 18
- 0
app/Models/HtpmsCustomer/QRService/ServiceParkingGroupHistory.php View File

@@ -0,0 +1,18 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\HtpmsCustomer\HtpmsCustomerHistoryModel;

/**
* QRサービス券駐車場グループ
*/
class ServiceParkingGroupHistory extends HtpmsCustomerHistoryModel
{
protected $table = "tbl3_qrs_service_parking_group_histories";

public function getModelName(): string
{
return "QRサービス券駐車場グループ履歴";
}
}

+ 22
- 0
app/Models/HtpmsCustomer/QRService/ServiceParkingGroupRelation.php View File

@@ -0,0 +1,22 @@
<?php

namespace App\Models\HtpmsCustomer\QRService;

use App\Models\ColumnName;
use App\Models\HtpmsCustomer\HtpmsCustomerAppModel;

/**
* QRサービス券駐車場グループ紐づけ
*/
class ServiceParkingGroupRelation extends HtpmsCustomerAppModel
{
const COL_NAME_QR_SERVICE_PARKING_GROUP_ID = ColumnName::QR_SERVICE_PARKING_GROUP_ID;
const COL_NAME_PARKING_MANAGEMENT_CODE = ColumnName::PARKING_MANAGEMENT_CODE;

protected $table = "tbl3_qrs_service_parking_group_relations";

public function getModelName(): string
{
return "QRサービス券駐車場グループ紐づけ";
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save