mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-20 09:30:49 +08:00
refactor exam progress + filter donate status
This commit is contained in:
@@ -60,12 +60,19 @@
|
||||
<div style="color: #aaa">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.</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Target User Class" prop="filters.classes">
|
||||
<el-form-item label="Target user class" prop="filters.classes">
|
||||
<el-checkbox-group v-model="formData.filters.classes">
|
||||
<el-checkbox v-for="(item, index) in allClasses" :label="index" :key="index">{{item}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Target user donate" prop="filters.donate_status">
|
||||
<el-checkbox-group v-model="formData.filters.donate_status">
|
||||
<el-checkbox label="no">No</el-checkbox>
|
||||
<el-checkbox label="yes">Yes</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Target User Register Time">
|
||||
<el-date-picker
|
||||
v-model="formData.filters.register_time_range"
|
||||
@@ -119,7 +126,8 @@ export default {
|
||||
indexes: [],
|
||||
filters: {
|
||||
classes: [],
|
||||
register_time_range: []
|
||||
register_time_range: [],
|
||||
donate_status: []
|
||||
},
|
||||
status: '',
|
||||
is_discovered: ''
|
||||
|
||||
@@ -57,6 +57,12 @@ class ExamResource extends JsonResource
|
||||
);
|
||||
}
|
||||
|
||||
$filter = Exam::FILTER_USER_DONATE;
|
||||
if (!empty($currentFilters->{$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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 序列化准备日期。
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateExamIndexInitValuesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('exam_index_init_values', function (Blueprint $table) {
|
||||
$table->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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddInitValueToExamProgressTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('exam_progress', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('exam_progress', 'init_value')) {
|
||||
$table->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');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddCompletedatIndexToSnatchedTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('snatched', function (Blueprint $table) {
|
||||
$table->index('completedat');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('snatched', function (Blueprint $table) {
|
||||
$table->dropIndex('snatched_completedat_index');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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' => "默认界面风格",
|
||||
|
||||
@@ -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' => "預設介面風格",
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user