Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f3b5b1d69 | ||
|
|
f505180237 | ||
|
|
ff946959a5 | ||
|
|
b2da21e99c |
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
class SyncSurveyUsers extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'survey:sync-users {--token= : Approagency admin token}';
|
||||||
|
protected $description = 'Sync user profile fields (name, age, gender) from approagency for all meditation users with missing data';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$token = $this->option('token') ?: config('services.approagency.token');
|
||||||
|
|
||||||
|
if (empty($token)) {
|
||||||
|
$this->error('Provide --token=<approagency_admin_token> or set APPROAGENCY_ADMIN_TOKEN in .env');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = User::whereNull('first_name')
|
||||||
|
->whereNotNull('mobile')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$this->info("Found {$users->count()} users with missing first_name. Syncing...");
|
||||||
|
|
||||||
|
$synced = 0;
|
||||||
|
$failed = 0;
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$retries = 0;
|
||||||
|
while ($retries <= 3) {
|
||||||
|
try {
|
||||||
|
$response = Http::withToken($token)
|
||||||
|
->retry(2, 2000)
|
||||||
|
->get('https://api.approagency.ir/api/admin/users', [
|
||||||
|
'mobile' => $user->mobile,
|
||||||
|
'per_page' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($response->status() === 429) {
|
||||||
|
$retries++;
|
||||||
|
$wait = $retries * 3;
|
||||||
|
$this->warn(" rate limited on {$user->mobile}, waiting {$wait}s...");
|
||||||
|
sleep($wait);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response->successful()) {
|
||||||
|
$data = $response->json('data.0');
|
||||||
|
if ($data) {
|
||||||
|
$user->update([
|
||||||
|
'first_name' => $data['first_name'] ?? $user->first_name,
|
||||||
|
'last_name' => $data['last_name'] ?? $user->last_name,
|
||||||
|
'age' => $data['age'] ?? $user->age,
|
||||||
|
'gender' => $data['gender'] ?? $user->gender,
|
||||||
|
'birthday' => $data['birthday'] ?? $user->birthday,
|
||||||
|
'identifier' => $data['uuid'] ?? $user->identifier,
|
||||||
|
]);
|
||||||
|
$name = $data['first_name'] ?? 'null';
|
||||||
|
$this->line(" synced: {$user->mobile} → {$name}");
|
||||||
|
$synced++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->warn(" skip: {$user->mobile} (HTTP {$response->status()})");
|
||||||
|
$failed++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$retries++;
|
||||||
|
if ($retries > 3) {
|
||||||
|
$this->error(" failed: {$user->mobile} — {$e->getMessage()}");
|
||||||
|
$failed++;
|
||||||
|
} else {
|
||||||
|
sleep($retries * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(3); // 3s between requests
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info("Done. Synced: {$synced}, Failed: {$failed}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
class SurveyQuestionController extends Controller
|
class SurveyQuestionController extends Controller
|
||||||
@@ -61,14 +62,22 @@ public function adminShow($id)
|
|||||||
public function adminUsers(Request $request)
|
public function adminUsers(Request $request)
|
||||||
{
|
{
|
||||||
$perPage = max(1, min((int) $request->input('per_page', 20), 100));
|
$perPage = max(1, min((int) $request->input('per_page', 20), 100));
|
||||||
|
$answered = $request->input('answered');
|
||||||
|
|
||||||
$users = User::select('id', 'first_name', 'last_name', 'age', 'gender', 'email', 'mobile', 'created_at', 'identifier')
|
$query = User::select('id', 'first_name', 'last_name', 'age', 'gender', 'email', 'mobile', 'created_at', 'identifier')
|
||||||
->withCount(['surveyAnswers as answers_count'])
|
->withCount(['surveyAnswers as answers_count'])
|
||||||
->withCount(['surveyAnswers as questions_answered' => function ($q) {
|
->withCount(['surveyAnswers as questions_answered' => function ($q) {
|
||||||
$q->select(DB::raw('COUNT(DISTINCT survey_question_id)'));
|
$q->select(DB::raw('COUNT(DISTINCT survey_question_id)'));
|
||||||
}])
|
}])
|
||||||
->orderByDesc('created_at')
|
->orderByDesc('created_at');
|
||||||
->paginate($perPage);
|
|
||||||
|
if ($answered === '1') {
|
||||||
|
$query->has('surveyAnswers');
|
||||||
|
} elseif ($answered === '0') {
|
||||||
|
$query->doesntHave('surveyAnswers');
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = $query->paginate($perPage);
|
||||||
|
|
||||||
return response()->json($users);
|
return response()->json($users);
|
||||||
}
|
}
|
||||||
@@ -119,6 +128,34 @@ public function adminUserShow($identifier)
|
|||||||
return response()->json($user);
|
return response()->json($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ADMIN: update a user's profile fields from approagency data.
|
||||||
|
public function adminUserUpdate(Request $request, $identifier)
|
||||||
|
{
|
||||||
|
if (is_numeric($identifier)) {
|
||||||
|
$user = User::where('id', $identifier)->first();
|
||||||
|
} else {
|
||||||
|
$user = User::where('identifier', $identifier)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $request->validate([
|
||||||
|
'first_name' => 'nullable|string',
|
||||||
|
'last_name' => 'nullable|string',
|
||||||
|
'age' => 'nullable|integer',
|
||||||
|
'gender' => 'nullable|integer',
|
||||||
|
'birthday' => 'nullable|string',
|
||||||
|
'identifier' => 'nullable|string',
|
||||||
|
'email' => 'nullable|email',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user->update(array_filter($data, fn ($v) => !is_null($v)));
|
||||||
|
|
||||||
|
return response()->json($user);
|
||||||
|
}
|
||||||
|
|
||||||
// ADMIN: clean aggregated results — per option vote counts & percentages, no raw rows.
|
// ADMIN: clean aggregated results — per option vote counts & percentages, no raw rows.
|
||||||
// Pass an $id for one question, omit it for all questions.
|
// Pass an $id for one question, omit it for all questions.
|
||||||
public function adminAnalytics($id = null)
|
public function adminAnalytics($id = null)
|
||||||
|
|||||||
@@ -66,21 +66,31 @@ public function loginV2(Request $request)
|
|||||||
'email' => $response['email'],
|
'email' => $response['email'],
|
||||||
'identifier' => $response['uuid'] ?? null,
|
'identifier' => $response['uuid'] ?? null,
|
||||||
'password' => config('app.default_password'),
|
'password' => config('app.default_password'),
|
||||||
// 'name' => trim(($response['first_name'] ?? '') . ' ' . ($response['last_name'] ?? '')),
|
|
||||||
'mobile' => $mobile,
|
'mobile' => $mobile,
|
||||||
'first_name' => $response['first_name'] ?? null,
|
'first_name' => $response['first_name'] ?? null,
|
||||||
'last_name' => $response['last_name'] ?? null,
|
'last_name' => $response['last_name'] ?? null,
|
||||||
|
'age' => $response['age'] ?? null,
|
||||||
|
'gender' => $response['gender'] ?? null,
|
||||||
|
'birthday' => $response['birthday'] ?? null,
|
||||||
'referral_code' => $response['referral_code'] ?? null,
|
'referral_code' => $response['referral_code'] ?? null,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// Only update name and mobile for existing user
|
|
||||||
// Keep the local identifier aligned with the current approagency uuid
|
// Keep the local identifier aligned with the current approagency uuid
|
||||||
// (e.g. when the user was matched by mobile, not identifier).
|
// (e.g. when the user was matched by mobile, not identifier).
|
||||||
$user->identifier = $response['uuid'] ?? $user->identifier;
|
$user->identifier = $response['uuid'] ?? $user->identifier;
|
||||||
$user->first_name = $response['first_name'] ?? $user->first_name;
|
|
||||||
$user->last_name = $response['last_name'] ?? $user->last_name;
|
// Sync names — update if the response has a non-null, non-empty value
|
||||||
// $user->name = trim(($response['first_name'] ?? '') . ' ' . ($response['last_name'] ?? ''));
|
if (!empty($response['first_name'])) {
|
||||||
|
$user->first_name = $response['first_name'];
|
||||||
|
}
|
||||||
|
if (!empty($response['last_name'])) {
|
||||||
|
$user->last_name = $response['last_name'];
|
||||||
|
}
|
||||||
|
|
||||||
$user->mobile = $mobile ?? $user->mobile;
|
$user->mobile = $mobile ?? $user->mobile;
|
||||||
|
$user->age = $response['age'] ?? $user->age;
|
||||||
|
$user->gender = $response['gender'] ?? $user->gender;
|
||||||
|
$user->birthday = $response['birthday'] ?? $user->birthday;
|
||||||
$user->referral_code = $response['referral_code'] ?? $user->referral_code;
|
$user->referral_code = $response['referral_code'] ?? $user->referral_code;
|
||||||
$user->save();
|
$user->save();
|
||||||
}
|
}
|
||||||
@@ -134,15 +144,27 @@ private function syncUserFromStatus(array $response, ?User $user = null): User
|
|||||||
'mobile' => $mobile,
|
'mobile' => $mobile,
|
||||||
'first_name' => $response['first_name'] ?? null,
|
'first_name' => $response['first_name'] ?? null,
|
||||||
'last_name' => $response['last_name'] ?? null,
|
'last_name' => $response['last_name'] ?? null,
|
||||||
|
'age' => $response['age'] ?? null,
|
||||||
|
'gender' => $response['gender'] ?? null,
|
||||||
|
'birthday' => $response['birthday'] ?? null,
|
||||||
'referral_code' => $response['referral_code'] ?? null,
|
'referral_code' => $response['referral_code'] ?? null,
|
||||||
'email_verified_at' => $response['email_verified_at'] ?? null
|
'email_verified_at' => $response['email_verified_at'] ?? null
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// update existing
|
// update existing
|
||||||
$user->identifier = $response['uuid'] ?? $user->identifier;
|
$user->identifier = $response['uuid'] ?? $user->identifier;
|
||||||
$user->first_name = $response['first_name'] ?? $user->first_name;
|
|
||||||
$user->last_name = $response['last_name'] ?? $user->last_name;
|
if (!empty($response['first_name'])) {
|
||||||
|
$user->first_name = $response['first_name'];
|
||||||
|
}
|
||||||
|
if (!empty($response['last_name'])) {
|
||||||
|
$user->last_name = $response['last_name'];
|
||||||
|
}
|
||||||
|
|
||||||
$user->mobile = $mobile ?? $user->mobile;
|
$user->mobile = $mobile ?? $user->mobile;
|
||||||
|
$user->age = $response['age'] ?? $user->age;
|
||||||
|
$user->gender = $response['gender'] ?? $user->gender;
|
||||||
|
$user->birthday = $response['birthday'] ?? $user->birthday;
|
||||||
$user->referral_code = $response['referral_code'] ?? $user->referral_code;
|
$user->referral_code = $response['referral_code'] ?? $user->referral_code;
|
||||||
$user->email_verified_at = $response['email_verified_at'] ?? $user->email_verified_at;
|
$user->email_verified_at = $response['email_verified_at'] ?? $user->email_verified_at;
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|||||||
@@ -169,6 +169,7 @@
|
|||||||
// User list + detail for survey admin
|
// User list + detail for survey admin
|
||||||
Route::get('/admin/survey-users', [SurveyQuestionController::class, 'adminUsers']);
|
Route::get('/admin/survey-users', [SurveyQuestionController::class, 'adminUsers']);
|
||||||
Route::get('/admin/survey-users/{id}', [SurveyQuestionController::class, 'adminUserShow']);
|
Route::get('/admin/survey-users/{id}', [SurveyQuestionController::class, 'adminUserShow']);
|
||||||
|
Route::patch('/admin/survey-users/{id}', [SurveyQuestionController::class, 'adminUserUpdate']);
|
||||||
});
|
});
|
||||||
// User: answer + read questions with their own answers only.
|
// User: answer + read questions with their own answers only.
|
||||||
Route::get('/survey-questions/suggested-media', [SurveyQuestionController::class, 'suggestedMedia']);
|
Route::get('/survey-questions/suggested-media', [SurveyQuestionController::class, 'suggestedMedia']);
|
||||||
|
|||||||
Reference in New Issue
Block a user