diff --git a/.env.example b/.env.example index e020b3b8..d37e0183 100644 --- a/.env.example +++ b/.env.example @@ -62,4 +62,4 @@ GOOGLE_DRIVE_REFRESH_TOKEN= GOOGLE_DRIVE_FOLDER_ID= GEOIP2_DATABASE= -EXAM_PROGRESS_UPDATE_PROBABILITY=60 +EXAM_PROGRESS_UPDATE_PROBABILITY=20 diff --git a/admin/src/utils/api.js b/admin/src/utils/api.js index d38417a5..647ec5d8 100644 --- a/admin/src/utils/api.js +++ b/admin/src/utils/api.js @@ -84,6 +84,9 @@ const api = { avoidExamUser: (id) => { return axios.put('exam-users-avoid', {id}); }, + recoverExamUser: (id) => { + return axios.put('exam-users-recover', {id}); + }, storeExamUser: (params) => { return axios.post('exam-users', params); }, diff --git a/admin/src/views/user/detail.vue b/admin/src/views/user/detail.vue index fb53be77..972777b2 100644 --- a/admin/src/views/user/detail.vue +++ b/admin/src/views/user/detail.vue @@ -125,6 +125,15 @@ Avoid + + + @@ -205,6 +214,12 @@ export default { await fetchPageData() } + const handleRecoverExam = async (id) => { + let res = await api.recoverExamUser(id) + ElMessage.success(res.msg) + await fetchPageData() + } + const handleAssignExam = async () => { assignExam.value.open(id) } @@ -230,6 +245,7 @@ export default { handleRemoveExam, handleAvoidExam, handleAssignExam, + handleRecoverExam, handleEnableUser, handleViewInviteInfo, handleDisableUser, diff --git a/app/Http/Controllers/ExamUserController.php b/app/Http/Controllers/ExamUserController.php index 6b7db3ae..17db4588 100644 --- a/app/Http/Controllers/ExamUserController.php +++ b/app/Http/Controllers/ExamUserController.php @@ -99,4 +99,11 @@ class ExamUserController extends Controller return $this->success($result, 'Avoid user exam success!'); } + public function recover(Request $request) + { + $request->validate(['id' => 'required']); + $result = $this->repository->recoverExamUser($request->id); + return $this->success($result, 'Recover user exam success!'); + } + } diff --git a/app/Models/ExamIndexInitValue.php b/app/Models/ExamIndexInitValue.php new file mode 100644 index 00000000..64dcd653 --- /dev/null +++ b/app/Models/ExamIndexInitValue.php @@ -0,0 +1,10 @@ +findOrFail($id); DB::transaction(function () use ($exam) { - ExamUser::query()->where('exam_id', $exam->id)->delete(); - ExamProgress::query()->where('exam_id', $exam->id)->delete(); + do { + $deleted = ExamUser::query()->where('exam_id', $exam->id)->limit(10000)->delete(); + } while ($deleted > 0); + do { + $deleted = ExamProgress::query()->where('exam_id', $exam->id)->limit(10000)->delete(); + } while ($deleted > 0); $exam->delete(); }); return true; @@ -232,8 +243,9 @@ class ExamRepository extends BaseRepository $data['end'] = $end; } do_log("$logPrefix, data: " . nexus_json_encode($data)); - $result = $user->exams()->create($data); - return $result; + $examUser = $user->exams()->create($data); + $this->initProgress($examUser, $user); + return $examUser; } public function listUser(array $params) @@ -343,6 +355,41 @@ class ExamRepository extends BaseRepository return true; } + public function initProgress($examUser, $user = null) + { + if (!$examUser instanceof ExamUser) { + $examUser = ExamUser::query()->findOrFail((int)$examUser); + } + $exam = $examUser->exam; + if (!$user instanceof User) { + $user = $examUser->user()->select(['id', 'uploaded', 'downloaded', 'seedtime', 'leechtime', 'seedbonus'])->first(); + } + $insert = [ + 'uid' => $user->id, + 'exam_id' => $exam->id, + ]; + foreach ($exam->indexes as $key => $index) { + if (!isset($index['checked']) || !$index['checked']) { + continue; + } + $insert['index'] = $index['index']; + if ($index['index'] == Exam::INDEX_UPLOADED) { + $insert['value'] = $user->uploaded; + } elseif ($index['index'] == Exam::INDEX_DOWNLOADED) { + $insert['value'] = $user->downloaded; + } elseif ($index['index'] == Exam::INDEX_SEED_BONUS) { + $insert['value'] = $user->seedbonus; + } elseif ($index['index'] == Exam::INDEX_SEED_TIME_AVERAGE) { + $insert['value'] = 0; + } else { + throw new \RuntimeException("Unknown index: {$index['index']}"); + } + ExamIndexInitValue::query()->insert($insert); + do_log("insert: " . json_encode($insert)); + } + return true; + } + public function getUserExamProgress($uid, $status = null, $with = ['exam', 'user']) { $logPrefix = "uid: $uid"; @@ -449,7 +496,9 @@ class ExamRepository extends BaseRepository { $examUser = ExamUser::query()->findOrFail($examUserId); $result = DB::transaction(function () use ($examUser) { - $examUser->progresses()->delete(); + do { + $deleted = $examUser->progresses()->limit(10000)->delete(); + } while ($deleted > 0); return $examUser->delete(); }); return $result; @@ -457,11 +506,18 @@ class ExamRepository extends BaseRepository public function avoidExamUser(int $examUserId) { - $examUser = ExamUser::query()->findOrFail($examUserId); + $examUser = ExamUser::query()->where('status',ExamUser::STATUS_NORMAL)->findOrFail($examUserId); $result = $examUser->update(['status' => ExamUser::STATUS_AVOIDED]); return $result; } + public function recoverExamUser(int $examUserId) + { + $examUser = ExamUser::query()->where('status',ExamUser::STATUS_AVOIDED)->findOrFail($examUserId); + $result = $examUser->update(['status' => ExamUser::STATUS_NORMAL]); + return $result; + } + public function cronjonAssign() { $exams = $this->listValid(null, Exam::DISCOVERED_YES); @@ -523,17 +579,16 @@ class ExamRepository extends BaseRepository // do_log("$currentLogPrefix, exam not match this user."); // continue; // } - $insert[] = [ + $insert = [ 'uid' => $user->id, 'exam_id' => $exam->id, 'created_at' => $now, 'updated_at' => $now, ]; do_log("$currentLogPrefix, exam will be assigned to this user."); - } - if (!empty($insert)) { - $result += count($insert); - ExamUser::query()->insert($insert); + $examUser = ExamUser::query()->create($insert); + $this->initProgress($examUser, $user); + $result++; } } return $result; diff --git a/database/migrations/2021_06_11_014259_create_exam_index_init_values_table.php b/database/migrations/2021_06_11_014259_create_exam_index_init_values_table.php new file mode 100644 index 00000000..51733d2b --- /dev/null +++ b/database/migrations/2021_06_11_014259_create_exam_index_init_values_table.php @@ -0,0 +1,37 @@ +id(); + $table->integer('uid')->index(); + $table->integer('exam_user_id'); + $table->integer('exam_id')->index(); + $table->integer('index')->index(); + $table->bigInteger('value'); + $table->timestamps(); + $table->unique(['exam_user_id', 'exam_id', 'index']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('exam_index_init_values'); + } +} diff --git a/routes/api.php b/routes/api.php index bec01c9d..013c0a56 100644 --- a/routes/api.php +++ b/routes/api.php @@ -50,6 +50,7 @@ Route::group(['middleware' => ['auth:sanctum', 'permission', 'locale']], functio Route::resource('exam-users', \App\Http\Controllers\ExamUserController::class); Route::put('exam-users-avoid', [\App\Http\Controllers\ExamUserController::class, 'avoid']); + Route::put('exam-users-recover', [\App\Http\Controllers\ExamUserController::class, 'recover']); Route::get('dashboard/system-info', [\App\Http\Controllers\DashboardController::class, 'systemInfo']); Route::get('dashboard/stat-data', [\App\Http\Controllers\DashboardController::class, 'statData']);