From 47b4f12dfd186c22da42b18c40812cc1f0de18b3 Mon Sep 17 00:00:00 2001 From: amirghasempoor Date: Sun, 10 Aug 2025 14:57:53 +0330 Subject: [PATCH] create performance for supervisor --- .env.example | 1 + .../SendSupervisorCheckStatsCommand.php | 32 ++++++++ ...veSupervisorPerformanceStatsController.php | 41 ++++++++++ .../LoginToProvinceController.php | 2 +- ...php => VerifyAuthenticationController.php} | 10 +-- app/Http/Middleware/VerifySetadIp.php | 12 +-- app/Models/ExpertPerformance.php | 12 --- app/Models/SupervisorPerformance.php | 15 ++++ .../SendSupervisorCheckStatsReportService.php | 80 +++++++++++++++++++ .../Dashboard/Province/ReportService.php | 67 ++++++++-------- config/globalVariables.php | 1 + config/logging.php | 7 +- ...y.php => SupervisorPerformanceFactory.php} | 4 +- ...61700_create_expert_performances_table.php | 27 ------- ...5_create_supervisor_performances_table.php | 34 ++++++++ ...er.php => SupervisorPerformanceSeeder.php} | 2 +- routes/web.php | 21 ++--- 17 files changed, 264 insertions(+), 104 deletions(-) create mode 100644 app/Console/Commands/SendSupervisorCheckStatsCommand.php create mode 100644 app/Http/Controllers/HeadquartersDashboard/CumulativeSupervisorPerformanceStatsController.php rename app/Http/Controllers/ProvinceDashboard/{CheckSetadController.php => VerifyAuthenticationController.php} (67%) delete mode 100644 app/Models/ExpertPerformance.php create mode 100644 app/Models/SupervisorPerformance.php create mode 100644 app/Services/Commands/SendSupervisorCheckStatsReportService.php rename database/factories/{ExpertPerformanceFactory.php => SupervisorPerformanceFactory.php} (81%) delete mode 100644 database/migrations/2025_07_23_061700_create_expert_performances_table.php create mode 100644 database/migrations/2025_08_10_064355_create_supervisor_performances_table.php rename database/seeders/{ExpertPerformanceSeeder.php => SupervisorPerformanceSeeder.php} (82%) diff --git a/.env.example b/.env.example index ce7f61b..e9ba913 100644 --- a/.env.example +++ b/.env.example @@ -70,3 +70,4 @@ TRUSTED_IP="127.0.0.1" NOTIFICATION_SERVER_URL= LOGIN_HASH_VALUE= +SETAD_IP= diff --git a/app/Console/Commands/SendSupervisorCheckStatsCommand.php b/app/Console/Commands/SendSupervisorCheckStatsCommand.php new file mode 100644 index 0000000..2743ef1 --- /dev/null +++ b/app/Console/Commands/SendSupervisorCheckStatsCommand.php @@ -0,0 +1,32 @@ +execute(); + $this->info('Done!'); + } +} diff --git a/app/Http/Controllers/HeadquartersDashboard/CumulativeSupervisorPerformanceStatsController.php b/app/Http/Controllers/HeadquartersDashboard/CumulativeSupervisorPerformanceStatsController.php new file mode 100644 index 0000000..cf2b528 --- /dev/null +++ b/app/Http/Controllers/HeadquartersDashboard/CumulativeSupervisorPerformanceStatsController.php @@ -0,0 +1,41 @@ +selectRaw(" + province_id, + province_name, + SUM(calls) AS total, + SUM(reviews) AS reviews, + SUM(good) AS good, + SUM(bad) AS bad + ") + ->groupBy('province_id', 'province_name') + ->unionAll(DB::table('expert_performances')->selectRaw(" + 0 as province_id, + 'کشوری' as province_name, + SUM(calls) AS total, + SUM(reviews) AS reviews, + SUM(good) AS good, + SUM(bad) AS bad + ")); + + return response()->json( + DataTableFacade::run( + $query, + $request, + allowedFilters: ['*'], + allowedSortings: ['*']) + ); + } +} diff --git a/app/Http/Controllers/HeadquartersDashboard/LoginToProvinceController.php b/app/Http/Controllers/HeadquartersDashboard/LoginToProvinceController.php index 746d348..144ed54 100644 --- a/app/Http/Controllers/HeadquartersDashboard/LoginToProvinceController.php +++ b/app/Http/Controllers/HeadquartersDashboard/LoginToProvinceController.php @@ -10,7 +10,7 @@ use Illuminate\Support\Facades\Hash; class LoginToProvinceController extends Controller { - public function loginToProvince(Request $request) + public function authenticate(Request $request) { $province_ip = Province::query() ->Where('id', '=', $request->province_id) diff --git a/app/Http/Controllers/ProvinceDashboard/CheckSetadController.php b/app/Http/Controllers/ProvinceDashboard/VerifyAuthenticationController.php similarity index 67% rename from app/Http/Controllers/ProvinceDashboard/CheckSetadController.php rename to app/Http/Controllers/ProvinceDashboard/VerifyAuthenticationController.php index da94bbb..ab4ce3f 100644 --- a/app/Http/Controllers/ProvinceDashboard/CheckSetadController.php +++ b/app/Http/Controllers/ProvinceDashboard/VerifyAuthenticationController.php @@ -9,21 +9,17 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; -class CheckSetadController extends Controller +class VerifyAuthenticationController extends Controller { use ApiResponse; - public function verifyToken(Request $request) + public function login(Request $request) { if (!Hash::check(config('globalVariables.LOGIN_HASH_VALUE'), $request->token)) { return $this->errorResponse('Invalid token'); } - $user = User::query()->find($request->user_id); - - if (!$user) { - return $this->errorResponse('User not found'); - } + $user = User::query()->findOrFail($request->user_id); Auth::login($user); diff --git a/app/Http/Middleware/VerifySetadIp.php b/app/Http/Middleware/VerifySetadIp.php index a6d3bac..00f3b45 100644 --- a/app/Http/Middleware/VerifySetadIp.php +++ b/app/Http/Middleware/VerifySetadIp.php @@ -11,18 +11,14 @@ class VerifySetadIp /** * Handle an incoming request. * - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + * @param Closure(Request): (Response) $next */ public function handle(Request $request, Closure $next): Response { - $masterServerIp = env('MASTER_SERVER_IP'); - if (is_null($masterServerIp)) { - return response('Master server IP not configured.', 500); + if ($request->ip() == config('globalVariables.SETAD_IP')) { + return $next($request); } - if ($request->ip() !== $masterServerIp) { - return response('Unauthorized.', 401); - } - return $next($request); + abort(Response::HTTP_FORBIDDEN); } } diff --git a/app/Models/ExpertPerformance.php b/app/Models/ExpertPerformance.php deleted file mode 100644 index a6aa1f8..0000000 --- a/app/Models/ExpertPerformance.php +++ /dev/null @@ -1,12 +0,0 @@ - */ - use HasFactory; -} diff --git a/app/Models/SupervisorPerformance.php b/app/Models/SupervisorPerformance.php new file mode 100644 index 0000000..0003ff1 --- /dev/null +++ b/app/Models/SupervisorPerformance.php @@ -0,0 +1,15 @@ + */ + use HasFactory; + + protected $guarded = []; +} diff --git a/app/Services/Commands/SendSupervisorCheckStatsReportService.php b/app/Services/Commands/SendSupervisorCheckStatsReportService.php new file mode 100644 index 0000000..759dcfa --- /dev/null +++ b/app/Services/Commands/SendSupervisorCheckStatsReportService.php @@ -0,0 +1,80 @@ +url = config('ProvinceInfo.center_ip'); + $this->channelName = 'send_supervisor_check_stats_report'; + $this->provinceId = config('ProvinceInfo.id'); + $this->provinceName = config('ProvinceInfo.name'); + } + + /** + * @throws ConnectionException + * @throws RequestException + */ + public function execute(): void + { + try + { + $data = $this->prepare(); + Http::post($this->url, $data)->throw(); + Log::channel($this->channelName)->info(SendSupervisorCheckStatsReportService::class, [$this->provinceName, array_sum($data), 'successful']); + } + catch (Exception $e) + { + Log::channel($this->channelName)->error(SendSupervisorCheckStatsReportService::class, [$e->getMessage()]); + throw $e; + } + } + + private function prepare(): array + { + $query = " + SELECT + {$this->provinceId} AS province_id, + '{$this->provinceName}' AS province_name, + od.date, + COUNT(*) AS reviews, + COUNT(CASE WHEN is_good = 1 THEN 1 END) AS good, + COUNT(CASE WHEN is_good = 0 THEN 1 END) AS bad, + COALESCE(cdr_data.calls, 0) AS calls + FROM ( + SELECT date, is_good + FROM operator_dialogues + WHERE supervisor_id IS NOT NULL + AND create_at > NOW() - INTERVAL 3 DAY + ) AS od + LEFT JOIN ( + SELECT + DATE(calldate) AS d, + COUNT(CASE + WHEN dcontext = 'from-internal' + AND disposition = 'ANSWERED' + AND dst > 100 AND dst < 105 + THEN 1 END) AS calls + FROM asteriskcdrdb.cdr + WHERE calldate > NOW() - INTERVAL 3 DAY + GROUP BY d + ) AS cdr_data + ON od.date = cdr_data.d + GROUP BY od.date, cdr_data.calls + ORDER BY od.date DESC"; + + return DB::select($query); + } +} diff --git a/app/Services/Dashboard/Province/ReportService.php b/app/Services/Dashboard/Province/ReportService.php index 18bef68..f3952d6 100644 --- a/app/Services/Dashboard/Province/ReportService.php +++ b/app/Services/Dashboard/Province/ReportService.php @@ -13,18 +13,18 @@ class ReportService $query = DB::table('asteriskcdrdb.cdr') ->selectRaw(" DATE(calldate) AS d, - COUNT(CASE WHEN (recordingfile = '' OR recordingfile LIKE 'q-%') THEN 1 END) AS c_all, - COUNT(CASE WHEN lastdata = 'IAX2/serverpeer/100' THEN 1 END) AS c_teh4, - COUNT(CASE WHEN lastdata = 'IAX2/242serverpeer/200' THEN 1 END) AS c_teh5, - COUNT(CASE WHEN dst = '500' THEN 1 END) AS c_os4, - COUNT(CASE WHEN dst = '600' THEN 1 END) AS c_os5, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 THEN 1 END) AS c_ok, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'NO ANSWER' AND dst BETWEEN 101 AND 104 AND duration > 14 THEN 1 END) AS c_nok, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 AND billsec > 9 THEN 1 END) AS c_b10, - COUNT(CASE WHEN lastapp = 'Busy' THEN 1 END) AS c_dnd, - COUNT(CASE WHEN dst BETWEEN 502 AND 599 THEN 1 END) AS c_trans, - COUNT(CASE WHEN disposition <> 'BUSY' AND dst BETWEEN 101 AND 105 THEN 1 END) AS ch_all, - AVG(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' THEN billsec END) AS c_avg + COUNT(CASE WHEN (recordingfile = '' OR recordingfile LIKE 'q-%') THEN 1 END) AS total, + COUNT(CASE WHEN lastdata = 'IAX2/serverpeer/100' THEN 1 END) AS teh4, + COUNT(CASE WHEN lastdata = 'IAX2/242serverpeer/200' THEN 1 END) AS teh5, + COUNT(CASE WHEN dst = '500' THEN 1 END) AS os4, + COUNT(CASE WHEN dst = '600' THEN 1 END) AS os5, + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 THEN 1 END) AS ok, + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'NO ANSWER' AND dst BETWEEN 101 AND 104 AND duration > 14 THEN 1 END) AS nok, + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 AND billsec > 9 THEN 1 END) AS b10, + COUNT(CASE WHEN lastapp = 'Busy' THEN 1 END) AS dnd, + COUNT(CASE WHEN dst BETWEEN 502 AND 599 THEN 1 END) AS trans, + COUNT(CASE WHEN disposition <> 'BUSY' AND dst BETWEEN 101 AND 105 THEN 1 END) AS all, + AVG(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' THEN billsec END) AS avg "); // ->whereBetween('calldate', [$from, $to]) // use Carbon or date strings // ->groupBy(DB::raw('DATE(calldate)')) @@ -44,8 +44,8 @@ class ReportService { $query = DB::table('people_messages') ->selectRaw(' - COUNT(CASE WHEN is_listen = 1 THEN 1 END) AS c_l, - COUNT(CASE WHEN is_listen = 0 THEN 1 END) AS c_nl + COUNT(CASE WHEN is_listen = 1 THEN 1 END) AS l, + COUNT(CASE WHEN is_listen = 0 THEN 1 END) AS nl '); // ->whereBetween('date', [$date_from, $date_to]) // ->first(); @@ -85,21 +85,20 @@ class ReportService { $query = DB::table('asteriskcdrdb.cdr') ->selectRaw(" - SELECT * - (ch_all - (c_ok + c_nok)) AS c_fail + (all - (ok + nok)) AS fail FROM ( SELECT DATE(calldate) AS d, dst, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 THEN 1 END) AS c_ok, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'NO ANSWER' AND dst BETWEEN 101 AND 104 AND duration > 14 THEN 1 END) AS c_nok, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 AND billsec > 9 THEN 1 END) AS c_b10, - COUNT(CASE WHEN lastapp = 'Busy' THEN 1 END) AS c_dnd, - COUNT(CASE WHEN dst BETWEEN 502 AND 599 THEN 1 END) AS c_trans, - COUNT(CASE WHEN disposition <> 'BUSY' AND dst BETWEEN 101 AND 105 THEN 1 END) AS ch_all + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 THEN 1 END) AS ok, + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'NO ANSWER' AND dst BETWEEN 101 AND 104 AND duration > 14 THEN 1 END) AS nok, + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 AND billsec > 9 THEN 1 END) AS b10, + COUNT(CASE WHEN lastapp = 'Busy' THEN 1 END) AS dnd, + COUNT(CASE WHEN dst BETWEEN 502 AND 599 THEN 1 END) AS trans, + COUNT(CASE WHEN disposition <> 'BUSY' AND dst BETWEEN 101 AND 105 THEN 1 END) AS all FROM asteriskcdrdb.cdr - WHERE dst BETWEEN 101 AND 105 AND calldate BETWEEN ? AND ? + WHERE dst BETWEEN 101 AND 105 GROUP BY d, dst ORDER BY d, dst ); @@ -127,24 +126,22 @@ class ReportService { $query = DB::table('asteriskcdrdb.cdr') ->selectRaw(" - SELECT * - (ch_all - (c_ok + c_nok)) AS c_fail + (all - (ok + nok)) AS fail FROM ( SELECT DATE(calldate) AS d, dst, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 THEN 1 END) AS c_ok, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'NO ANSWER' AND dst BETWEEN 101 AND 104 AND duration > 14 THEN 1 END) AS c_nok, - COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 AND billsec > 9 THEN 1 END) AS c_b10, - COUNT(CASE WHEN lastapp = 'Busy' THEN 1 END) AS c_dnd, - COUNT(CASE WHEN dst BETWEEN 502 AND 599 THEN 1 END) AS c_trans, - COUNT(CASE WHEN disposition <> 'BUSY' AND dst BETWEEN 101 AND 105 THEN 1 END) AS ch_all + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 THEN 1 END) AS ok, + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'NO ANSWER' AND dst BETWEEN 101 AND 104 AND duration > 14 THEN 1 END) AS nok, + COUNT(CASE WHEN dcontext = 'from-internal' AND disposition = 'ANSWERED' AND dst BETWEEN 101 AND 104 AND billsec > 9 THEN 1 END) AS b10, + COUNT(CASE WHEN lastapp = 'Busy' THEN 1 END) AS dnd, + COUNT(CASE WHEN dst BETWEEN 502 AND 599 THEN 1 END) AS trans, + COUNT(CASE WHEN disposition <> 'BUSY' AND dst BETWEEN 101 AND 105 THEN 1 END) AS all FROM asteriskcdrdb.cdr - WHERE dst BETWEEN 101 AND 105 AND calldate BETWEEN ? AND ? - GROUP dst - ORDER dst - ); + WHERE dst BETWEEN 101 AND 105 + GROUP BY dst + ORDER BY dst); "); return DataTableFacade::run( diff --git a/config/globalVariables.php b/config/globalVariables.php index 65c5e42..ec26ab5 100644 --- a/config/globalVariables.php +++ b/config/globalVariables.php @@ -4,4 +4,5 @@ return [ 'TRUSTED_IP' => explode(',', env("TRUSTED_IP", '127.0.0.1')), 'NOTIFICATION_SERVER_URL' => env('NOTIFICATION_SERVER_URL', '127.0.0.1'), 'LOGIN_HASH_VALUE' => env('LOGIN_HASH_VALUE'), + 'SETAD_IP' => env('SETAD_IP'), ]; diff --git a/config/logging.php b/config/logging.php index ca78f2e..ddd3970 100644 --- a/config/logging.php +++ b/config/logging.php @@ -150,6 +150,11 @@ return [ 'path' => storage_path('logs/send_system_message_stats_report.log'), 'level' => 'debug', ], - ], + 'send_supervisor_check_stats_report' => [ + 'driver' => 'single', + 'path' => storage_path('logs/send_supervisor_check_stats_report.log'), + 'level' => 'debug', + ], + ], ]; diff --git a/database/factories/ExpertPerformanceFactory.php b/database/factories/SupervisorPerformanceFactory.php similarity index 81% rename from database/factories/ExpertPerformanceFactory.php rename to database/factories/SupervisorPerformanceFactory.php index 2ee7b2e..c878543 100644 --- a/database/factories/ExpertPerformanceFactory.php +++ b/database/factories/SupervisorPerformanceFactory.php @@ -5,9 +5,9 @@ namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\ExpertPerformance> + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\SupervisorPerformance> */ -class ExpertPerformanceFactory extends Factory +class SupervisorPerformanceFactory extends Factory { /** * Define the model's default state. diff --git a/database/migrations/2025_07_23_061700_create_expert_performances_table.php b/database/migrations/2025_07_23_061700_create_expert_performances_table.php deleted file mode 100644 index 2a0b866..0000000 --- a/database/migrations/2025_07_23_061700_create_expert_performances_table.php +++ /dev/null @@ -1,27 +0,0 @@ -id(); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('expert_performances'); - } -}; diff --git a/database/migrations/2025_08_10_064355_create_supervisor_performances_table.php b/database/migrations/2025_08_10_064355_create_supervisor_performances_table.php new file mode 100644 index 0000000..0f6718d --- /dev/null +++ b/database/migrations/2025_08_10_064355_create_supervisor_performances_table.php @@ -0,0 +1,34 @@ +id(); + $table->date('date'); + $table->foreignId('province_id')->constrained('provinces'); + $table->string('province_name'); + $table->integer('calls'); + $table->integer('reviews'); + $table->integer('good'); + $table->integer('bad'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('supervisor_performances'); + } +}; diff --git a/database/seeders/ExpertPerformanceSeeder.php b/database/seeders/SupervisorPerformanceSeeder.php similarity index 82% rename from database/seeders/ExpertPerformanceSeeder.php rename to database/seeders/SupervisorPerformanceSeeder.php index ea2f8ea..1efcbbb 100644 --- a/database/seeders/ExpertPerformanceSeeder.php +++ b/database/seeders/SupervisorPerformanceSeeder.php @@ -5,7 +5,7 @@ namespace Database\Seeders; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; -class ExpertPerformanceSeeder extends Seeder +class SupervisorPerformanceSeeder extends Seeder { /** * Run the database seeds. diff --git a/routes/web.php b/routes/web.php index 73a7790..7f1a5c6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -13,7 +13,7 @@ use App\Http\Controllers\HeadquartersDashboard\CumulativeCentersStatsController; use App\Http\Controllers\HeadquartersDashboard\CumulativeKeypressStatsController; use App\Http\Controllers\HeadquartersDashboard\CumulativeMessageRatingStatsController; use App\Http\Controllers\HeadquartersDashboard\CumulativeVoiceRatingStatsController; -use App\Http\Controllers\ProvinceDashboard\CheckSetadController; +use App\Http\Controllers\ProvinceDashboard\VerifyAuthenticationController; use App\Http\Middleware\SuperAdminCheck; use Illuminate\Support\Facades\Route; @@ -203,19 +203,20 @@ Route::prefix('server')->group(function () { Route::get('/{peopleMessage}', 'show')->name('show'); }); }); - Route::middleware(['auth', 'permission:setat_ip']) + + Route::middleware(['auth', 'role:admin']) ->prefix('login_to_province') ->name('loginToProvince.') ->controller(LoginToProvinceController::class) ->group(function () { - Route::post('/', 'loginToProvince')->name('loginToProvince'); - }); - Route::middleware('auth') - ->prefix('check_setad') - ->name('checkSetad.') - ->controller(CheckSetadController::class) - ->group(function () { - Route::get('/', 'verifyToken')->name('verifyToken'); + Route::post('/', 'authenticate')->name('authenticate'); }); + Route::middleware(['auth', 'setadIp']) + ->prefix('verify_authentication') + ->name('verifyAuthentication.') + ->controller(VerifyAuthenticationController::class) + ->group(function () { + Route::get('/', 'login')->name('login'); + }); });