From fb281607d1842f56f547d6e53d243c3bb2e8a240 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Mon, 27 May 2024 12:22:50 +0700 Subject: [PATCH] feat: api for frontend> tv > new tv request --- .env.example | 5 + app/Helper/STS/Indokargo.php | 112 ++++++++++++ .../superadmin/tv/NewTvRequestController.php | 31 ++++ app/Models/NewTvRequest.php | 171 ++++++++++++++++++ app/Models/StsLog.php | 35 ++++ app/Models/Tv.php | 23 +-- app/Providers/AppServiceProvider.php | 14 +- routes/api/superadmin.php | 6 + 8 files changed, 376 insertions(+), 21 deletions(-) create mode 100644 app/Helper/STS/Indokargo.php create mode 100644 app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php diff --git a/.env.example b/.env.example index 4885627..ff84703 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,11 @@ MOBILE_TOKEN=Gaobd5OKPdGARLGTD03vSFStrADAxmQ9 # for CORS SANCTUM_STATEFUL_DOMAINS=[http://localhost:3000] +# for Indokargo +IK_BE_DOMAIN= +IK_SESSION_ID= +IK_OAUTH_ID= + LOG_CHANNEL=stack LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug diff --git a/app/Helper/STS/Indokargo.php b/app/Helper/STS/Indokargo.php new file mode 100644 index 0000000..597777f --- /dev/null +++ b/app/Helper/STS/Indokargo.php @@ -0,0 +1,112 @@ +partner = StsLog::PARTNER_INDOKARGO; + $stsLog->is_outgoing = false; + $stsLog->module = StsLog::MODULE_TV; + $stsLog->service_name = StsLog::SERVICE_CREATE_TV_ADDRESS; + $stsLog->local_id = $tvFk; + $stsLog->partner_id = null; + $stsLog->seq = $seq; + $stsLog->request_data = $request->all(); + $stsLog->request_time = Carbon::now(); + + try { + $request->validate(['code' => 'required|string']); + + $result = Http::indokargo()->post('tv/address/create', $request->all()); + $res = $result->throw()->json(); + + // update stsLog + $stsLog->response_data = $res; + $stsLog->response_time = Carbon::now(); + + self::_checkIkResponse($res); + + $ikAddress = $res['data']['0']; + $ikAddressId = $ikAddress['id']; + $stsLog->partner_id = $ikAddressId; + + // check ik address id is exist (almost impossible to happen, 0.01%) + $isDuplicateIkAddressId = TV::where([ + ['ik_address_id', '=', $ikAddressId], + ['id', '!=', $tvFk] + ])->first(); + if($isDuplicateIkAddressId) throw new \Exception("IK Address ID Already exist in current db ($ikAddressId)"); + + DB::beginTransaction(); + $tv = Tv::findOrFail($tvFk); + $tv->ik_address_id = $ikAddressId; + $tv->save(); + + $stsLog->result = StsLog::STATUS_SUCCESS; + $stsLog->save(); + DB::commit(); + return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Save Data']); + } catch(ConnectionException $e) { + // if error cause by connection error, try again later + $stsLog->error_info = [ + 'message' => $e->getMessage(), + 'errors' => $e->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->is_retry = true; + $stsLog->save(); + return JSONResponse::Success(['is_warning' => true, + 'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]); + } catch(\Throwable $th) { + DB::rollBack(); + $stsLog->error_info = [ + 'message' => $th->getMessage(), + 'errors' => $th->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->save(); + throw $th; + } + } +} + +?> \ No newline at end of file diff --git a/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php b/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php new file mode 100644 index 0000000..8eca953 --- /dev/null +++ b/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php @@ -0,0 +1,31 @@ +validate([ + 'perPage' => 'nullable|integer|min:1', + ...DatabaseHelper::getOrderBysValidations(), + 'search' => DatabaseHelper::getSearchValidation() + ]); + + $newTvRequests = NewTvRequest::addColumnCanApprove() + ->addColumnCanReject() + ->multiSearch($request->search, ['code']) + ->multiOrderBy($request->orderBys, 'created_at desc') + ->paginate($request->perPage ?? 10); + + return JSONResponse::Success(['data' => $newTvRequests ]); + } + + public function approve(Request $request) { return NewTvRequest::approveFromRequest($request); } + public function reject(Request $request) { return NewTvRequest::rejectFromRequest($request); } +} diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index abb20aa..aeb1e13 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -2,11 +2,182 @@ namespace App\Models; +use App\Helper\JSONResponse; +use App\Helper\STS\Indokargo; +use App\Helper\Traits\Models\CanMultiOrderBy; +use App\Helper\Traits\Models\CanMultiSearch; +use Carbon\Carbon; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\AsArrayObject; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Str; class NewTvRequest extends Model { use HasFactory; + use CanMultiSearch; + use CanMultiOrderBy; protected $table = 'new_tv_requests'; + protected $casts = ['device_info'=>AsArrayObject::class]; + + //------------------------------------------------------------ + // -- RELATED TO RELATIONSHIP + // HAS ONE + public function self(): HasOne { return $this->hasOne(self::class, 'id', 'id'); } + /// BELONGS TO + public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); } + // -- END RELATED TO RELATIONSHIP + //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO SCOPE + public function scopeAddColumnCanApprove(Builder $query) { + $query->withCount(['self as can_approve' => function($q) { + $q->whereNull('activated_at'); + }]); + } + public function scopeAddColumnCanReject(Builder $query) { + $query->withCount(['self as can_reject' => function($q) { + $q->whereNull('activated_at'); + }]); + } + // -- END RELATED TO SCOPE + //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO DATA FUNCTION + private static function _generateRandomCode(): string { + $totalDigit = 6; + $randomDigit = ''; + while(true) { + $randomDigit = strtoupper(Str::random($totalDigit)); + $isExist = NewTvRequest::where('code', $randomDigit)->first(); + if(!$isExist) break; + } + return $randomDigit; + } + // -- END RELATED TO DATA FUNCTION + //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO FROM REQUEST FUNCTION + private static function _checkOrCreateResponse(NewTvRequest $newTvReq) { + if($newTvReq->is_activated) { + // make sure waiting tv_device responded before deleted + if(empty($newTvReq->responded_at)) { + $newTvReq->responded_at = now(); + $newTvReq->save(); + } + + return JSONResponse::Success([ + 'message' => 'activated', + 'is_active' => true, + 'new_tv_request' => $newTvReq, + 'tv' => $newTvReq->tv + ]); + } + + return JSONResponse::Success([ + 'message' => 'not activate', + 'is_active' => false, + 'new_tv_request' => $newTvReq, + 'tv' => null + ]); + } + + public static function approveFromRequest(Request $request) { + $request->validate([ + 'id' => 'required|integer|exists:App\Models\NewTvRequest', + 'tv' => 'required|array', + ]); + + $tvRequest = new Request($request->tv); + if($tvRequest->filled('code')) $tvRequest->code = strtoupper($tvRequest->code); + + $tvRequest->validate([ + 'code' => 'required|string|unique:App\Models\Tv,code', + 'col1' => 'nullable|string', + 'col2' => 'nullable|string', + 'col3' => 'nullable|string', + 'col4' => 'nullable|string', + 'col5' => 'nullable|string', + 'col6' => 'nullable|string', + 'col7' => 'nullable|string', + 'col8' => 'nullable|string', + 'col9' => 'nullable|string', + 'col10' => 'nullable|string', + 'notes' => 'nullable|string', + ]); + + $newTvReq = NewTvRequest::addColumnCanApprove() + ->findOrFail($request->id); + if(!$newTvReq->can_approve) throw new \Exception('Cannot approve current request'); + + try { + DB::beginTransaction(); + $tv = new Tv(); + $tv->code = $tvRequest->code; + $tv->col1 = $tvRequest->col1; + $tv->col2 = $tvRequest->col2; + $tv->col3 = $tvRequest->col3; + $tv->col4 = $tvRequest->col4; + $tv->col5 = $tvRequest->col5; + $tv->col6 = $tvRequest->col6; + $tv->col7 = $tvRequest->col7; + $tv->col8 = $tvRequest->col8; + $tv->col9 = $tvRequest->col9; + $tv->col10 = $tvRequest->col10; + $tv->notes = $tvRequest->notes; + $tv->device_info = $newTvReq->device_info; + $tv->save(); + + $newTvReq->activated_at = now(); + $newTvReq->tv_fk = $tv->id; + $newTvReq->save(); + + // try to sys_to_sys with indokargo + $jsonResponse = Indokargo::createTVAddress($tvRequest, $tv->id); + DB::commit(); + return $jsonResponse; + } catch(\Throwable $th) { + DB::rollback(); + throw $th; + } + } + + public static function rejectFromRequest(Request $request) { + $request->validate([ + 'id' => 'required|integer|exists:App\Models\NewTvRequest', + ]); + $newTvRequest = NewTvRequest::addColumnCanReject() + ->findOrFail($request->id); + if(!$newTvRequest->can_reject) throw new \Exception('Cannot reject current request'); + $newTvRequest->delete(); + + return JSONResponse::Success(['data' => $newTvRequest ]); + } + + /** + * Rule: + * 1. if it has not been activated for more than the expiry date, deleted it + * 2. if it has been activated & has been responded more than expiry date, delete it + * - case: when has been activated, but intenet connection missing, how can device know + * that the new request tv has been activated? + */ + private static function _getMaxExpiredTime() :Carbon { return Carbon::now()->subHour(); } + public static function deleteExpiredRequests() { + $expiredTime = self::_getMaxExpiredTime()->toString(); + NewTvRequest::where(function($q) use ($expiredTime) { + $q->whereNull('activated_at')->where('created_at', '<=', $expiredTime); + })->orWhere(function($q) use ($expiredTime) { + $q->where('responded_at', '<=', $expiredTime ); + })->delete(); + } + // -- END RELATED TO FROM REQUEST FUNCTION + //------------------------------------------------------------ } diff --git a/app/Models/StsLog.php b/app/Models/StsLog.php index b32f0b6..458ab7f 100644 --- a/app/Models/StsLog.php +++ b/app/Models/StsLog.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\AsArrayObject; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -9,4 +10,38 @@ class StsLog extends Model { use HasFactory; protected $table = 'sts_logs'; + + protected $fillable = [ + 'partner', + 'is_outgoing', + 'module', + 'service_name', + 'local_id', + 'partner_id', + 'seq', + 'result', + 'error_info', + 'request_data', + 'request_time', + 'response_data', + 'response_time', + 'is_retry', + 'created_at', + 'updated_at' + ]; + protected $casts = [ + 'error_info' => AsArrayObject::class, + 'request_data' => AsArrayObject::class, + 'response_data' => AsArrayObject::class, + ]; + + const STATUS_SUCCESS = 'success'; + const STATUS_FAILED = 'failed'; + const STATUS_WARNING = 'warning'; + + const PARTNER_INDOKARGO = 'indokargo'; + + // related to indokargo + const MODULE_TV = 'tv'; + const SERVICE_CREATE_TV_ADDRESS = 'create-tv-addrass'; } diff --git a/app/Models/Tv.php b/app/Models/Tv.php index 4af51b5..f116808 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -4,44 +4,31 @@ namespace App\Models; use App\Helper\JSONResponse; use AWS\CRT\HTTP\Request; +use Illuminate\Database\Eloquent\Casts\AsArrayObject; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; -use Illuminate\Support\Str; class Tv extends Model { use HasFactory; protected $table = 'tvs'; protected $hidden = ['ik_cust_id', 'ik_address_id']; + protected $casts = ['device_info'=>AsArrayObject::class]; //------------------------------------------------------------ - // -- RELATED TO MIGRATION + // -- RELATED TO RELATIONSHIP /// HAS ONE + public function new_tv_request(): HasOne { return $this->hasOne(NewTvRequest::class, 'tv_fk', 'id'); } public function tv_app_info(): HasOne { return $this->hasOne(TvAppInfo::class, 'tv_fk', 'id'); } /// HAS MANY public function tv_connect_logs(): HasMany { return $this->hasMany(TvConnectLog::class, 'tv_fk', 'id'); } public function tv_sessions(): HasMany { return $this->hasMany(TvSession::class, 'tv_fk', 'id'); } /// BELONGS TO public function outlet(): BelongsTo { return $this->belongsTo(Outlet::class, 'outlet_fk', 'id'); } - // -- END RELATED TO MIGRATION - //------------------------------------------------------------ - - //------------------------------------------------------------ - // -- RELATED TO DATA FUNCTION - public static function generateRandomCode(): string { - $totalDigit = 6; - $randomDigit = ''; - while(true) { - $randomDigit = strtoupper(Str::random($totalDigit)); - $isExist = Tv::where('code', $randomDigit)->first(); - if(!$isExist) break; - } - return $randomDigit; - } - // -- END RELATED TO DATA FUNCTION + // -- END RELATED TO RELATIONSHIP //------------------------------------------------------------ //------------------------------------------------------------ diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..a8cc324 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use Illuminate\Support\Facades\Http; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -17,8 +18,15 @@ class AppServiceProvider extends ServiceProvider /** * Bootstrap any application services. */ - public function boot(): void - { - // + public function boot(): void { + Http::macro('indokargo', function() { + $headers = [ + 'oauth' => env('INDOKARGO_API_OAUTH', ''), + 'session' => env('INDOKARGO_API_SESSION_ID', '') + ]; + + return Http::withHeaders($headers) + ->baseUrl(env('INDOKARGO_API_URL', '').'/v1/'); + }); } } diff --git a/routes/api/superadmin.php b/routes/api/superadmin.php index 4f74481..2c383d4 100644 --- a/routes/api/superadmin.php +++ b/routes/api/superadmin.php @@ -1,6 +1,7 @@ group(function() { Route::post('/user-management/delete', 'delete'); }); +Route::controller(NewTvRequestController::class)->group(function() { + Route::post('/tv/new-tv-request', 'init'); + Route::post('/tv/new-tv-request/approve', 'approve'); + Route::post('/tv/new-tv-request/reject', 'reject'); +}); ?> \ No newline at end of file