diff --git a/app/Console/Commands/AttendanceCleanup.php b/app/Console/Commands/AttendanceCleanup.php
index 0b800863..9eed9e9a 100644
--- a/app/Console/Commands/AttendanceCleanup.php
+++ b/app/Console/Commands/AttendanceCleanup.php
@@ -3,6 +3,7 @@
namespace App\Console\Commands;
use App\Models\Attendance;
+use App\Repositories\AttendanceRepository;
use Illuminate\Console\Command;
class AttendanceCleanup extends Command
@@ -33,39 +34,16 @@ class AttendanceCleanup extends Command
/**
* Execute the console command.
- *
- * @return int
*/
public function handle()
{
- $query = Attendance::query()->groupBy('uid')->selectRaw('uid, max(id) as max_id');
- $page = 1;
- $size = 10000;
- while (true) {
- $rows = $query->forPage($page, $size)->get();
- $log = "sql: " . last_query() . ", count: " . $rows->count();
- do_log($log);
- $this->info($log);
- if ($rows->isEmpty()) {
- $log = "no more data....";
- do_log($log);
- $this->info($log);
- break;
- }
- foreach ($rows as $row) {
- do {
- $deleted = Attendance::query()
- ->where('uid', $row->uid)
- ->where('id', '<', $row->max_id)
- ->limit(10000)
- ->delete();
- $log = "delete: $deleted by sql: " . last_query();
- do_log($log);
- $this->info($log);
- } while ($deleted > 0);
- }
- $page++;
- }
- return 0;
+ $rep = new AttendanceRepository();
+ $result = $rep->cleanup();
+ $log = sprintf(
+ '[%s], %s, result: %s',
+ nexus()->getRequestId(), __METHOD__, var_export($result, true)
+ );
+ $this->info($log);
+ do_log($log);
}
}
diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php
index 785fc8dd..fc82bdaa 100644
--- a/app/Console/Commands/Test.php
+++ b/app/Console/Commands/Test.php
@@ -70,21 +70,21 @@ class Test extends Command
*/
public function handle()
{
- $searchRep = new SearchRepository();
- $r = $searchRep->deleteIndex();
- $r = $searchRep->createIndex();
- $r = $searchRep->import();
-
- $arr = [
- 'cat' => 'category',
- 'source' => 'source',
- 'medium' => 'medium',
- 'codec' => 'codec',
- 'audiocodec' => 'audiocodec',
- 'standard' => 'standard',
- 'processing' => 'processing',
- 'team' => 'team',
- ];
+// $searchRep = new SearchRepository();
+// $r = $searchRep->deleteIndex();
+// $r = $searchRep->createIndex();
+// $r = $searchRep->import();
+//
+// $arr = [
+// 'cat' => 'category',
+// 'source' => 'source',
+// 'medium' => 'medium',
+// 'codec' => 'codec',
+// 'audiocodec' => 'audiocodec',
+// 'standard' => 'standard',
+// 'processing' => 'processing',
+// 'team' => 'team',
+// ];
$queryString = 'cat401=1&cat404=1&source2=1&medium2=1&medium3=1&codec3=1&audiocodec3=1&standard2=1&standard3=1&processing2=1&team3=1&team4=1&incldead=1&spstate=0&inclbookmarked=0&search=&search_area=0&search_mode=0';
$userSetting = '[cat401][cat404][sou1][med1][cod1][sta2][sta3][pro2][tea2][aud2][incldead=0][spstate=3][inclbookmarked=2]';
// foreach ($arr as $queryField => $value) {
@@ -116,6 +116,11 @@ class Test extends Command
// $r = $searchRep->deleteBookmark(1);
// $r = $searchRep->addBookmark(1);
+ $rep = new AttendanceRepository();
+ $uid = 1;
+ $attendance = $rep->getAttendance($uid);
+// $r = $rep->migrateAttendanceLogs($uid);
+ $r = $rep->getContinuousDays($attendance);
dd($r);
}
diff --git a/app/Models/Attendance.php b/app/Models/Attendance.php
index 001e8482..17d50c65 100644
--- a/app/Models/Attendance.php
+++ b/app/Models/Attendance.php
@@ -21,4 +21,10 @@ class Attendance extends NexusModel
30 => 1000
];
+
+ public function logs(): \Illuminate\Database\Eloquent\Relations\HasMany
+ {
+ return $this->hasMany(AttendanceLog::class, 'uid', 'uid');
+ }
+
}
diff --git a/app/Models/AttendanceLog.php b/app/Models/AttendanceLog.php
new file mode 100644
index 00000000..ba9728f6
--- /dev/null
+++ b/app/Models/AttendanceLog.php
@@ -0,0 +1,13 @@
+ ['text' => 'Peasant'],
self::CLASS_USER => ['text' => 'User', 'min_seed_points' => 0],
self::CLASS_POWER_USER => ['text' => 'Power User', 'min_seed_points' => 40000],
diff --git a/app/Repositories/AttendanceRepository.php b/app/Repositories/AttendanceRepository.php
index 06d193a7..93c25282 100644
--- a/app/Repositories/AttendanceRepository.php
+++ b/app/Repositories/AttendanceRepository.php
@@ -2,6 +2,7 @@
namespace App\Repositories;
use App\Models\Attendance;
+use App\Models\AttendanceLog;
use App\Models\Setting;
use App\Models\User;
use Carbon\Carbon;
@@ -16,9 +17,6 @@ class AttendanceRepository extends BaseRepository
$today = Carbon::today();
$settings = Setting::get('bonus');
$initialBonus = $settings['attendance_initial'] ?? Attendance::INITIAL_BONUS;
- $stepBonus = $settings['attendance_step'] ?? Attendance::STEP_BONUS;
- $maxBonus = $settings['attendance_max'] ?? Attendance::MAX_BONUS;
- $continuousBonus = $settings['attendance_continuous'] ?? Attendance::CONTINUOUS_BONUS;
$isUpdated = 1;
$initialData = [
'uid' => $uid,
@@ -43,11 +41,12 @@ class AttendanceRepository extends BaseRepository
if ($diffDays == 1) {
//yesterday do it, it's continuous
do_log("[CONTINUOUS]");
- $points = $this->getContinuousPoints($initialBonus, $stepBonus, $attendance->days, $maxBonus, $continuousBonus);
+ $continuousDays = $this->getContinuousDays($attendance);
+ $points = $this->getContinuousPoints($continuousDays);
$update = [
'added' => $now,
'points' => $points,
- 'days' => $attendance->days + 1,
+ 'days' => $continuousDays + 1,
'total_days' => $attendance->total_days + 1,
];
} else {
@@ -61,6 +60,12 @@ class AttendanceRepository extends BaseRepository
}
if ($isUpdated) {
User::query()->where('id', $uid)->increment('seedbonus', $update['points']);
+ $attendanceLog = [
+ 'uid' => $attendance->uid,
+ 'points' => $update['points'],
+ 'date' => $now->format('Y-m-d'),
+ ];
+ AttendanceLog::query()->insert($attendanceLog);
}
$attendance->added_time = $now->toTimeString();
$attendance->is_updated = $isUpdated;
@@ -81,8 +86,13 @@ class AttendanceRepository extends BaseRepository
return $query->first();
}
- private function getContinuousPoints($initial, $step, $days, $max, $extraAwards)
+ private function getContinuousPoints($days)
{
+ $settings = Setting::get('bonus');
+ $initial = $settings['attendance_initial'] ?? Attendance::INITIAL_BONUS;
+ $step = $settings['attendance_step'] ?? Attendance::STEP_BONUS;
+ $max = $settings['attendance_max'] ?? Attendance::MAX_BONUS;
+ $extraAwards = $settings['attendance_continuous'] ?? Attendance::CONTINUOUS_BONUS;
$points = min($initial + $days * $step, $max);
krsort($extraAwards);
foreach ($extraAwards as $key => $value) {
@@ -94,6 +104,11 @@ class AttendanceRepository extends BaseRepository
return $points;
}
+ /**
+ * 将旧的 1 人 1 天 1 条迁移到新版 1 人一条
+ *
+ * @return int
+ */
public function migrateAttendance(): int
{
$page = 1;
@@ -137,4 +152,174 @@ class AttendanceRepository extends BaseRepository
return count($idArr);
}
+
+ /**
+ * 清理签到记录,每人只保留一条
+ *
+ * @return int
+ */
+ public function cleanup(): int
+ {
+ $query = Attendance::query()->groupBy('uid')->havingRaw("count(*) > 1")->selectRaw('uid, max(id) as max_id');
+ $page = 1;
+ $size = 10000;
+ $deleteCounts = 0;
+ while (true) {
+ $rows = $query->forPage($page, $size)->get();
+ $log = "sql: " . last_query() . ", count: " . $rows->count();
+ do_log($log, 'info', app()->runningInConsole());
+ if ($rows->isEmpty()) {
+ $log = "no more data....";
+ do_log($log, 'info', app()->runningInConsole());
+ break;
+ }
+ foreach ($rows as $row) {
+ do {
+ $deleted = Attendance::query()
+ ->where('uid', $row->uid)
+ ->where('id', '<', $row->max_id)
+ ->limit(10000)
+ ->delete();
+ $log = "delete: $deleted by sql: " . last_query();
+ $deleteCounts += $deleted;
+ do_log($log, 'info', app()->runningInConsole());
+ } while ($deleted > 0);
+ }
+ $page++;
+ }
+ return $deleteCounts;
+ }
+
+ /**
+ * 为 1.7 新的补签功能回写当前连续签到记录
+ *
+ * @param int $uid
+ * @return int
+ */
+ public function migrateAttendanceLogs($uid = 0): int
+ {
+ $cleanUpCounts = $this->cleanup();
+ do_log("cleanup count: $cleanUpCounts", 'info', app()->runningInConsole());
+
+ $page = 1;
+ $size = 10000;
+ $insert = [];
+ $table = 'attendance_logs';
+ while (true) {
+ $logPrefix = "[MIGRATE_ATTENDANCE_LOGS], page: $page, size: $size";
+ $query = Attendance::query()
+ ->where('added', '>=', Carbon::yesterday())
+ ->forPage($page, $size);
+ if ($uid) {
+ $query->where('uid', $uid);
+ }
+ $result = $query->get();
+ do_log("$logPrefix, " . last_query() . ", count: " . $result->count(), 'info', app()->runningInConsole());
+ if ($result->isEmpty()) {
+ do_log("$logPrefix, no more data...");
+ break;
+ }
+ foreach ($result as $row) {
+ $interval =\DateInterval::createFromDateString("-1 day");
+ $period = new \DatePeriod($row->added->addDay(1), $interval, $row->days, \DatePeriod::EXCLUDE_START_DATE);
+ $i = 0;
+ foreach ($period as $periodValue) {
+ $insert[] = [
+ 'uid' => $row->uid,
+ 'points' => ($i == 0 ? $row->points : 0),
+ 'date' => $periodValue->format('Y-m-d'),
+ ];
+ $i++;
+ }
+ }
+ $page++;
+ }
+ if (empty($insert)) {
+ do_log("no data to insert...", 'info', app()->runningInConsole());
+ return 0;
+ }
+ NexusDB::table($table)->insert($insert);
+ $insertCount = count($insert);
+ do_log("[MIGRATE_ATTENDANCE_LOGS] DONE! insert count: " . $insertCount, 'info', app()->runningInConsole());
+
+ return $insertCount;
+ }
+
+ public function getContinuousDays(Attendance $attendance, $start = ''): int
+ {
+ if (!empty($start)) {
+ $start = Carbon::parse($start);
+ } else {
+ $start = $attendance->added;
+ }
+ $logQuery = $attendance->logs()->where('created_at', '<=', $start)->orderBy('date', 'desc');
+ $attendanceLogs = $logQuery->get(['date'])->keyBy('date');
+ $counts = $attendanceLogs->count();
+ do_log(sprintf('user: %s, log counts: %s from query: %s', $attendance->uid, $counts, last_query()));
+ if ($counts == 0) {
+ return 0;
+ }
+ $interval =\DateInterval::createFromDateString("-1 day");
+ $period = new \DatePeriod($start->clone()->addDay(1), $interval, $counts, \DatePeriod::EXCLUDE_START_DATE);
+ $days = 0;
+ foreach ($period as $value) {
+ $checkDate = $value->format('Y-m-d');
+ if ($attendanceLogs->has($checkDate)) {
+ $days++;
+ do_log(sprintf('user: %s, date: %s, has attendance, now days: %s', $attendance->uid, $checkDate, $days));
+ } else {
+ do_log(sprintf('user: %s, date: %s, not attendance, now days: %s', $attendance->uid, $checkDate, $days));
+ break;
+ }
+ }
+ return $days;
+
+ }
+
+ public function retroactive($user, $timestampMs)
+ {
+ if (!$user instanceof User) {
+ $user = User::query()->findOrFail((int)$user);
+ }
+ $attendance = $this->getAttendance($user->id);
+ if (!$attendance) {
+ throw new \LogicException("Haven't attendance yet");
+ }
+ $date = Carbon::createFromTimestampMs($timestampMs);
+ return NexusDB::transaction(function () use ($user, $attendance, $date) {
+ if (AttendanceLog::query()->where('uid', $user->id)->where('date', $date->format('Y-m-d'))->exists()) {
+ throw new \RuntimeException("Already attendance");
+ }
+ if ($user->attendance_card < 1) {
+ throw new \RuntimeException("Attendance card not enough");
+ }
+ $log = sprintf('user: %s, card: %s, retroactive date: %s', $user->id, $user->attendance_card, $date->format('Y-m-d'));
+ $continuousDays = $this->getContinuousDays($attendance, $date->clone()->subDay(1));
+ $log .= ", continuousDays: $continuousDays";
+ $points = $this->getContinuousPoints($continuousDays);
+ $log .= ", points: $points";
+ do_log($log);
+ $userUpdates = [
+ 'attendance_card' => NexusDB::raw('attendance_card - 1'),
+ 'seedbonus' => NexusDB::raw("seedbonus + $points"),
+ ];
+ $affectedRows = User::query()
+ ->where('id', $user->id)
+ ->where('attendance_card', $user->attendance_card)
+ ->update($userUpdates);
+ $msg = "Decrement user attendance_card and increment bonus";
+ if ($affectedRows != 1) {
+ do_log("$msg fail, query: " . last_query());
+ throw new \RuntimeException("$msg fail");
+ }
+ do_log("$msg success, query: " . last_query());
+ $insert = [
+ 'uid' => $user->id,
+ 'points' => $points,
+ 'date' => $date,
+ 'is_retroactive' => 1,
+ ];
+ return AttendanceLog::query()->create($insert);
+ });
+ }
}
diff --git a/app/Repositories/SearchRepository.php b/app/Repositories/SearchRepository.php
index 8cda8f55..3628a11d 100644
--- a/app/Repositories/SearchRepository.php
+++ b/app/Repositories/SearchRepository.php
@@ -71,6 +71,9 @@ class SearchRepository extends BaseRepository
//tag
'tag_id' => ['type' => 'long', ],
+ //use for category.mode
+ 'mode' => ['type' => 'long', ],
+
//relations
'torrent_relations' => [
'type' => 'join',
@@ -194,7 +197,7 @@ class SearchRepository extends BaseRepository
$fields = $this->getTorrentBaseFields();
array_unshift($fields, 'id');
$query = Torrent::query()
- ->with(['user', 'torrent_tags', 'bookmarks'])
+ ->with(['user', 'torrent_tags', 'bookmarks', 'basic_category'])
->select($fields);
if (!is_null($torrentId)) {
$idArr = preg_split('/[,\s]+/', $torrentId);
@@ -299,6 +302,7 @@ class SearchRepository extends BaseRepository
'routing' => $torrent->owner,
];
$data = Arr::only($torrent->toArray(), $baseFields);
+ $data['mode'] = $torrent->basic_category->mode;
$body = array_merge($data, [
'_doc_type' => $docType,
'torrent_id' => $torrent->id,
@@ -433,6 +437,9 @@ class SearchRepository extends BaseRepository
$must = $must_not = [];
$mustBoolShould = [];
$must[] = ['match' => ['_doc_type' => self::DOC_TYPE_TORRENT]];
+ if (!empty($params['mode'])) {
+ $must[] = ['match' => ['mode' => $params['mode']]];
+ }
foreach (self::$queryFieldToTorrentFieldMaps as $queryField => $torrentField) {
if (isset($params[$queryField]) && $params[$queryField] !== '') {
diff --git a/database/migrations/2022_04_02_163930_create_attendance_logs_table.php b/database/migrations/2022_04_02_163930_create_attendance_logs_table.php
new file mode 100644
index 00000000..3e782cf6
--- /dev/null
+++ b/database/migrations/2022_04_02_163930_create_attendance_logs_table.php
@@ -0,0 +1,36 @@
+id();
+ $table->integer('uid')->index();
+ $table->integer('points');
+ $table->date('date')->index();
+ $table->smallInteger('is_retroactive')->default(0);
+ $table->dateTime('created_at')->useCurrent();
+ $table->dateTime('updated_at')->useCurrent()->useCurrentOnUpdate();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('attendance_logs');
+ }
+};
diff --git a/database/migrations/2022_04_03_041642_add_attendance_card_to_users_table.php b/database/migrations/2022_04_03_041642_add_attendance_card_to_users_table.php
new file mode 100644
index 00000000..7decc7f0
--- /dev/null
+++ b/database/migrations/2022_04_03_041642_add_attendance_card_to_users_table.php
@@ -0,0 +1,32 @@
+integer('attendance_card')->default(0);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('attendance_card');
+ });
+ }
+};
diff --git a/include/functions.php b/include/functions.php
index f28b98fc..e3274b78 100644
--- a/include/functions.php
+++ b/include/functions.php
@@ -2375,6 +2375,11 @@ if ($CURUSER){
+
@@ -2490,7 +2495,7 @@ else {
= UC_SYSOP) { ?> []
[]
[]:
- points); }else{ printf(' %s', $lang_functions['text_attendance']);}?>
+ '.$lang_functions['text_attended'].'', $attendance->points, $CURUSER['attendance_card']); }else{ printf(' %s', $lang_functions['text_attendance']);}?>
:
[]:
@@ -2699,6 +2704,9 @@ function stdfoot() {
print("");
if ($analyticscode_tweak)
print("\n".$analyticscode_tweak."\n");
+ foreach (\Nexus\Nexus::getAppendFooters() as $value) {
+ print($value);
+ }
print("