diff --git a/admin/src/views/exam/form.vue b/admin/src/views/exam/form.vue index 00811e89..937fcbd2 100644 --- a/admin/src/views/exam/form.vue +++ b/admin/src/views/exam/form.vue @@ -60,12 +60,19 @@
Unit: days. When assign to user, begin and end are used if they are specified. Otherwise begin time is the time at assignment, and the end time is the time at assignment plus the duration.
- + {{item}} + + + No + Yes + + + {$filter})) { + $donateStatus = $classes = collect(User::$donateStatus)->only($currentFilters->{$filter}); + $arr[] = sprintf('%s: %s', Exam::$filters[$filter]['name'], $donateStatus->pluck('text')->implode(', ')); + } + return implode("\n", $arr); } diff --git a/app/Models/Exam.php b/app/Models/Exam.php index 166f701b..6de29111 100644 --- a/app/Models/Exam.php +++ b/app/Models/Exam.php @@ -45,10 +45,12 @@ class Exam extends NexusModel const FILTER_USER_CLASS = 'classes'; const FILTER_USER_REGISTER_TIME_RANGE = 'register_time_range'; + const FILTER_USER_DONATE = 'donate_status'; public static $filters = [ self::FILTER_USER_CLASS => ['name' => 'User class'], self::FILTER_USER_REGISTER_TIME_RANGE => ['name' => 'User register time range'], + self::FILTER_USER_DONATE => ['name' => 'User donate'], ]; protected static function booted() diff --git a/app/Models/ExamProgress.php b/app/Models/ExamProgress.php index f1f0565f..3e6b3e3e 100644 --- a/app/Models/ExamProgress.php +++ b/app/Models/ExamProgress.php @@ -4,7 +4,7 @@ namespace App\Models; class ExamProgress extends NexusModel { - protected $fillable = ['exam_user_id', 'exam_id', 'uid', 'index', 'value', 'torrent_id']; + protected $fillable = ['exam_user_id', 'exam_id', 'uid', 'index', 'init_value', 'value', 'torrent_id']; public $timestamps = true; } diff --git a/app/Models/User.php b/app/Models/User.php index 1d295480..65e10e2d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -59,6 +59,14 @@ class User extends Authenticatable self::CLASS_STAFF_LEADER => ['text' => 'Staff Leader'], ]; + const DONATE_YES = 'yes'; + const DONATE_NO = 'no'; + + public static $donateStatus = [ + self::DONATE_YES => ['text' => 'Yes'], + self::DONATE_NO => ['text' => 'No'], + ]; + public static $cardTitles = [ 'uploaded_human' => '上传', 'downloaded_human' => '下载', @@ -73,6 +81,14 @@ class User extends Authenticatable return self::$classes[$this->class]['text'] ?? ''; } + public function getDonateStatusAttribute() + { + if (empty($this->donoruntil) || $this->donoruntil == '0000-00-00 00:00:00') { + return self::DONATE_NO; + } + return self::DONATE_YES; + } + /** * 为数组 / JSON 序列化准备日期。 diff --git a/app/Repositories/ExamRepository.php b/app/Repositories/ExamRepository.php index f33fea93..96e0047f 100644 --- a/app/Repositories/ExamRepository.php +++ b/app/Repositories/ExamRepository.php @@ -8,6 +8,7 @@ use App\Models\ExamProgress; use App\Models\ExamUser; use App\Models\Message; use App\Models\Setting; +use App\Models\Snatch; use App\Models\Torrent; use App\Models\User; use App\Models\UserBanLog; @@ -173,22 +174,40 @@ class ExamRepository extends BaseRepository } $logPrefix = sprintf('exam: %s, user: %s', $exam->id, $user->id); $filters = $exam->filters; - if (empty($filters->classes)) { + + $filter = Exam::FILTER_USER_CLASS; + if (empty($filters->{$filter})) { do_log("$logPrefix, exam: {$exam->id} no class"); return false; } - if (!in_array($user->class, $filters->classes)) { - do_log("$logPrefix, user class: {$user->class} not in: " . json_encode($filters)); + if (!in_array($user->class, $filters->{$filter})) { + do_log("$logPrefix, user class: {$user->class} not in: " . json_encode($filters->{$filter})); + return false; + } + + $filter = Exam::FILTER_USER_DONATE; + if (empty($filters->{$filter})) { + do_log("$logPrefix, exam: {$exam->id} no donate"); + return false; + } + if (!in_array($user->donate_status, $filters->{$filter})) { + do_log("$logPrefix, user donate status: {$user->donate_status} not in: " . json_encode($filters->{$filter})); + return false; + } + + + $filter = Exam::FILTER_USER_REGISTER_TIME_RANGE; + if (empty($filters->{$filter})) { + do_log("$logPrefix, exam: {$exam->id} no register time range"); return false; } if (!$user->added) { do_log("$logPrefix, user no added time", 'warning'); return false; } - $added = $user->added->toDateTimeString(); - $registerTimeBegin = isset($filters->register_time_range[0]) ? Carbon::parse($filters->register_time_range[0])->toDateTimeString() : ''; - $registerTimeEnd = isset($filters->register_time_range[1]) ? Carbon::parse($filters->register_time_range[1])->toDateTimeString() : ''; + $registerTimeBegin = isset($filters->{$filter}[0]) ? Carbon::parse($filters->{$filter}[0])->toDateTimeString() : ''; + $registerTimeEnd = isset($filters->{$filter}[1]) ? Carbon::parse($filters->{$filter}[1])->toDateTimeString() : ''; if (empty($registerTimeBegin)) { do_log("$logPrefix, exam: {$exam->id} no register_time_begin"); return false; @@ -244,7 +263,7 @@ class ExamRepository extends BaseRepository } do_log("$logPrefix, data: " . nexus_json_encode($data)); $examUser = $user->exams()->create($data); - $this->initProgress($examUser, $user); + $this->updateProgress($examUser, $user); return $examUser; } @@ -355,7 +374,7 @@ class ExamRepository extends BaseRepository return true; } - public function initProgress($examUser, $user = null) + public function updateProgress($examUser, $user = null) { if (!$examUser instanceof ExamUser) { $examUser = ExamUser::query()->findOrFail((int)$examUser); @@ -364,29 +383,82 @@ class ExamRepository extends BaseRepository if (!$user instanceof User) { $user = $examUser->user()->select(['id', 'uploaded', 'downloaded', 'seedtime', 'leechtime', 'seedbonus'])->first(); } - $insert = [ + $attributes = [ + 'exam_user_id' => $examUser->id, 'uid' => $user->id, 'exam_id' => $exam->id, ]; + $logPrefix = json_encode($attributes); + $begin = $examUser->begin; + if (empty($begin)) { + throw new \InvalidArgumentException("$logPrefix, exam: {$examUser->id} no begin."); + } + $end = $examUser->end; + if (empty($end)) { + throw new \InvalidArgumentException("$logPrefix, exam: {$examUser->id} no end."); + } + $currentProgrss = []; foreach ($exam->indexes as $key => $index) { if (!isset($index['checked']) || !$index['checked']) { continue; } - $insert['index'] = $index['index']; + $attributes['index'] = $index['index']; if ($index['index'] == Exam::INDEX_UPLOADED) { - $insert['value'] = $user->uploaded; + $attributes['value'] = $user->uploaded; } elseif ($index['index'] == Exam::INDEX_DOWNLOADED) { - $insert['value'] = $user->downloaded; + $attributes['value'] = $user->downloaded; } elseif ($index['index'] == Exam::INDEX_SEED_BONUS) { - $insert['value'] = $user->seedbonus; + $attributes['value'] = $user->seedbonus; } elseif ($index['index'] == Exam::INDEX_SEED_TIME_AVERAGE) { - $insert['value'] = 0; + $torrentCountsRes = Snatch::query() + ->where('userid', $user->id) + ->where('completedat', '>=', $begin) + ->where('completedat', '<=', $end) + ->selectRaw("count(distinct(torrentid)) as torrent_counts") + ->first(); + do_log("$logPrefix, torrentCountsRes: " . json_encode($torrentCountsRes)); + if ($torrentCountsRes && $torrentCountsRes->torrent_counts > 0) { + $value = $user->seedtime / $torrentCountsRes->torrent_counts; + } else { + $value = 0; + } + $attributes['value'] = $value; } else { - throw new \RuntimeException("Unknown index: {$index['index']}"); + $msg = "Unknown index: {$index['index']}"; + do_log("$logPrefix, $msg", 'error'); + throw new \RuntimeException($msg); } - ExamIndexInitValue::query()->insert($insert); - do_log("insert: " . json_encode($insert)); + //at the begining, value = init_value, and then value increase. + $progress = ExamProgress::query() + ->where('exam_user_id', $examUser->id) + ->where('torrent_id', -1) + ->where('index', $index['index']) + ->orderBy('id', 'desc') + ->first(); + if ($progress) { + //do update + $progress->update(['value' => $attributes['value']]); + do_log("$logPrefix, doUpdat: " . last_query()); + } else { + //do insert + $attributes['init_value'] = $attributes['value']; + $attributes['torrent_id'] = -1; + ExamProgress::query()->insert($attributes); + do_log("$logPrefix, doInsert with: " . json_encode($attributes)); + } + $currentProgrss[$index['index']] = $attributes['value']; } + $examProgressFormatted = $this->getProgressFormatted($exam, $currentProgrss); + $examNotPassed = array_filter($examProgressFormatted, function ($item) { + return !$item['passed']; + }); + $update = [ + 'progress' => $currentProgrss, + 'is_done' => count($examNotPassed) ? ExamUser::IS_DONE_NO : ExamUser::IS_DONE_YES, + ]; + do_log("[UPDATE_PROGRESS] " . nexus_json_encode($update)); + $examUser->update($update); + return true; } @@ -410,14 +482,15 @@ class ExamRepository extends BaseRepository } $examUser = $examUsers->first(); $exam = $examUser->exam; - $progress = $this->calculateProgress($examUser); +// $progress = $this->calculateProgress($examUser); + $progress = $examUser->progress; do_log("$logPrefix, progress: " . nexus_json_encode($progress)); $examUser->progress = $progress; - $examUser->progress_formatted = $this->getProgressFormatted($exam, $progress); + $examUser->progress_formatted = $this->getProgressFormatted($exam, (array)$progress); return $examUser; } - private function calculateProgress(ExamUser $examUser) + public function calculateProgress(ExamUser $examUser) { $logPrefix = "examUser: " . $examUser->id; $begin = $examUser->begin; @@ -444,6 +517,7 @@ class ExamRepository extends BaseRepository if (isset($progressSum[$index])) { $torrentCount = $examUser->progresses() ->where('index', $index) + ->where('torrent_id', '>=', 0) ->selectRaw('count(distinct(torrent_id)) as torrent_count') ->first() ->torrent_count; @@ -587,7 +661,7 @@ class ExamRepository extends BaseRepository ]; do_log("$currentLogPrefix, exam will be assigned to this user."); $examUser = ExamUser::query()->create($insert); - $this->initProgress($examUser, $user); + $this->updateProgress($examUser, $user); $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 deleted file mode 100644 index 51733d2b..00000000 --- a/database/migrations/2021_06_11_014259_create_exam_index_init_values_table.php +++ /dev/null @@ -1,37 +0,0 @@ -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/database/migrations/2021_06_11_141214_add_init_value_to_exam_progress_table.php b/database/migrations/2021_06_11_141214_add_init_value_to_exam_progress_table.php new file mode 100644 index 00000000..3379a94e --- /dev/null +++ b/database/migrations/2021_06_11_141214_add_init_value_to_exam_progress_table.php @@ -0,0 +1,45 @@ +bigInteger('init_value')->default(0)->after('index'); + } + //@todo open in beta10 +// $table->unique(['exam_user_id', 'index']); +// $table->dropColumn('torrent_id'); +// $table->dropIndex('exam_progress_exam_user_id_index'); +// $table->dropIndex('exam_progress_created_at_index'); + }); +// \Illuminate\Support\Facades\DB::statement('alter table exam_progress modify created_at timestamp default current_timestamp'); +// \Illuminate\Support\Facades\DB::statement('alter table exam_progress modify updated_at timestamp default current_timestamp on update current_timestamp'); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('exam_progress', function (Blueprint $table) { + $table->dropColumn('init_value'); +// $table->dropUnique('exam_progress_exam_user_id_index_unique'); +// $table->integer('torrent_id')->after('uid'); +// $table->index('exam_user_id'); +// $table->index('created_at'); + }); + } +} diff --git a/database/migrations/2021_06_11_161551_add_completedat_index_to_snatched_table.php b/database/migrations/2021_06_11_161551_add_completedat_index_to_snatched_table.php new file mode 100644 index 00000000..5a060266 --- /dev/null +++ b/database/migrations/2021_06_11_161551_add_completedat_index_to_snatched_table.php @@ -0,0 +1,32 @@ +index('completedat'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('snatched', function (Blueprint $table) { + $table->dropIndex('snatched_completedat_index'); + }); + } +} diff --git a/lang/chs/lang_settings.php b/lang/chs/lang_settings.php index 7de03b38..f5abf584 100644 --- a/lang/chs/lang_settings.php +++ b/lang/chs/lang_settings.php @@ -430,7 +430,7 @@ $lang_settings = array 'row_torrents_category_mode' => "种子区分类模式", 'text_torrents_category_mode_note' => "改变种子区的分类模式。", 'row_special_category_mode' => "特别区分类模式", - 'text_special_category_mode_note' => "改变特殊区的分类模式。", + 'text_special_category_mode_note' => "改变特别区的分类模式。", 'row_default_site_language' => "默认站点语言", 'text_default_site_language_note' => "改变登录页面的默认语言。", 'row_default_stylesheet' => "默认界面风格", diff --git a/lang/cht/lang_settings.php b/lang/cht/lang_settings.php index 2c095d9c..9d4ca4ed 100644 --- a/lang/cht/lang_settings.php +++ b/lang/cht/lang_settings.php @@ -431,7 +431,7 @@ $lang_settings = array 'row_torrents_category_mode' => "種子區分類型態", 'text_torrents_category_mode_note' => "改變種子區的分類型態。", 'row_special_category_mode' => "特別區分類型態", - 'text_special_category_mode_note' => "改變特殊區的分類型態。", + 'text_special_category_mode_note' => "改變特别區的分類型態。", 'row_default_site_language' => "預設網站語言", 'text_default_site_language_note' => "改變登入頁面的預設語言。", 'row_default_stylesheet' => "預設介面風格", diff --git a/nexus/Install/Update.php b/nexus/Install/Update.php index b1ae6ea9..0c1c2501 100644 --- a/nexus/Install/Update.php +++ b/nexus/Install/Update.php @@ -3,8 +3,11 @@ namespace Nexus\Install; use App\Models\Category; +use App\Models\Exam; +use App\Models\ExamUser; use App\Models\Icon; use App\Models\Setting; +use App\Repositories\ExamRepository; use Illuminate\Support\Str; use Nexus\Database\NexusDB; @@ -100,4 +103,72 @@ class Update extends Install } + public function migrateExamProgress() + { + if (!NexusDB::schema()->hasColumn('exam_progress', 'init_value')) { + sql_query('alter table exam_progress add column `init_value` bigint(20) NOT NULL after `index`'); + $log = 'add column init_value on table exam_progress.'; + $this->doLog($log); + } else { + $log = 'column init_value already exists on table exam_progress.'; + $this->doLog($log); + } + $examUsersQuery = ExamUser::query()->where('status', ExamUser::STATUS_NORMAL)->with('user'); + $page = 1; + $size = 100; + while (true) { + $examUsers = $examUsersQuery->forPage($page, $size)->get(); + $log = "fetch exam user by: " . last_query(); + $this->doLog($log); + if ($examUsers->isEmpty()) { + $log = "no more exam user to handle..."; + $this->doLog($log); + break; + } + $log = 'get init_vlaue...'; + $this->doLog($log); + foreach ($examUsers as $examUser) { + $oldProgress = $examUser->progress; + $user = $examUser->user; + $currentLogPrefix = "examUser: " . $examUser->toJson(); + $log = sprintf("$currentLogPrefix, progress: %s", json_encode($oldProgress)); + $this->doLog($log); + foreach ($oldProgress as $index => $progressValue) { + if ($index == Exam::INDEX_DOWNLOADED) { + $value = $user->downloaded; + $initValue = $value - $progressValue; + } elseif ($index == Exam::INDEX_UPLOADED) { + $value = $user->uploaded; + $initValue = $value - $progressValue; + } elseif ($index == Exam::INDEX_SEED_BONUS) { + $value = $user->seedbonus; + $initValue = $value - $progressValue; + } elseif ($index == Exam::INDEX_SEED_TIME_AVERAGE) { + $value = $progressValue; + $initValue = 0; + } else { + $log = sprintf("$currentLogPrefix, invalid index: %s, skip!", $index); + $this->doLog($log); + continue; + } + $insert = [ + 'exam_user_id' => $examUser->id, + 'exam_id' => $examUser->exam_id, + 'uid' => $examUser->uid, + 'index' => $index, + 'torrent_id' => -1, + 'value' => $value, + 'init_value' => $initValue, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]; + NexusDB::table('exam_progress')->insert($insert); + $log = "$currentLogPrefix, insert index: $index progress: " . json_encode($insert); + $this->doLog($log); + } + } + $page++; + } + } + }