seeding bonus log save to click house

This commit is contained in:
xiaomlove
2025-09-19 21:02:34 +07:00
parent 90c973f608
commit 8bdcacf8e7
15 changed files with 112 additions and 58 deletions

View File

@@ -198,6 +198,18 @@ class EditSetting extends Page implements Forms\Contracts\HasForms
->label(__('label.setting.system.is_invite_pre_email_and_username'))
->helperText(__('label.setting.system.is_invite_pre_email_and_username_help'))
,
Forms\Components\Radio::make('system.is_record_announce_log')
->options(self::$yesOrNo)
->inline(true)
->label(__('label.setting.system.is_record_announce_log'))
->helperText(__('label.setting.system.is_record_announce_log_help'))
,
Forms\Components\Radio::make('system.is_record_seeding_bonus_log')
->options(self::$yesOrNo)
->inline(true)
->label(__('label.setting.system.is_record_seeding_bonus_log'))
->helperText(__('label.setting.system.is_record_seeding_bonus_log_help'))
,
Forms\Components\Select::make('system.access_admin_class_min')
->options(User::listClass(User::CLASS_VIP))
->label(__('label.setting.system.access_admin_class_min'))

View File

@@ -12,6 +12,8 @@ use Filament\Tables\Table;
use Filament\Tables;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\Arr;
use function Filament\Support\get_model_label;
class BonusLogResource extends Resource
{
@@ -28,9 +30,9 @@ class BonusLogResource extends Resource
return __('admin.sidebar.bonus_log');
}
public static function getBreadcrumb(): string
public static function getModelLabel(): string
{
return self::getNavigationLabel();
return sprintf('%s(%s)', get_model_label(static::getModel()), __('bonus-log.exclude_seeding_bonus'));
}
public static function form(Form $form): Form
@@ -85,19 +87,19 @@ class BonusLogResource extends Resource
})
,
Tables\Filters\SelectFilter::make('business_type')
->options(BonusLogs::listStaticProps(BonusLogs::$businessTypes, 'bonus-log.business_types', true))
->options(BonusLogs::listStaticProps(Arr::except(BonusLogs::$businessTypes, BonusLogs::$businessTypeBonus), 'bonus-log.business_types', true))
->label(__('bonus-log.fields.business_type'))
,
Tables\Filters\Filter::make('exclude_seeding_bonus')
->toggle()
->label(__('bonus-log.exclude_seeding_bonus'))
->query(function (Builder $query, array $data) {
if ($data['isActive']) {
$query->whereNotIn("business_type", BonusLogs::$businessTypeBonus);
}
})
->default()
,
// Tables\Filters\Filter::make('exclude_seeding_bonus')
// ->toggle()
// ->label(__('bonus-log.exclude_seeding_bonus'))
// ->query(function (Builder $query, array $data) {
// if ($data['isActive']) {
// $query->whereNotIn("business_type", BonusLogs::$businessTypeBonus);
// }
// })
// ->default()
// ,
])
->actions([
// Tables\Actions\EditAction::make(),

View File

@@ -101,12 +101,14 @@ class CalculateUserSeedBonus implements ShouldQueue
foreach ($results as $userInfo)
{
$uid = $userInfo['id'];
$donorAddition = $officialAddition = $haremAddition = $medalAddition = 0;
$isDonor = is_donor($userInfo);
$seedBonusResult = calculate_seed_bonus($uid);
$bonusLog = "[CLEANUP_CLI_CALCULATE_SEED_BONUS_HANDLE_USER], user: $uid, seedBonusResult: " . nexus_json_encode($seedBonusResult);
$all_bonus = $basicBonus = $seedBonusResult['seed_bonus'];
$oldValue = $userInfo['seedbonus'];
$bonusLog .= ", all_bonus: $all_bonus";
$this->appendBonusLogInsert($bonusLogInsert, $uid, BonusLogs::BUSINESS_TYPE_SEEDING_BASIC, $oldValue, $basicBonus);
$oldValue += $basicBonus;
/**
* BUG: can't add this, case when not include info in where condition $idStr will be reset to 0
* // BUG: 不能添加这部分case when 不包含某些 uid 的数据,而 $idStr 里面又有,会被重置为 0
@@ -120,22 +122,30 @@ class CalculateUserSeedBonus implements ShouldQueue
$donorAddition = $basicBonus * $donortimes_bonus;
$all_bonus += $donorAddition;
$bonusLog .= ", isDonor, donortimes_bonus: $donortimes_bonus, all_bonus: $all_bonus";
$this->appendBonusLogInsert($bonusLogInsert, $uid, BonusLogs::BUSINESS_TYPE_SEEDING_DONOR_ADDITION, $oldValue, $donorAddition);
$oldValue += $donorAddition;
}
if ($officialAdditionFactor > 0) {
$officialAddition = $seedBonusResult['official_bonus'] * $officialAdditionFactor;
$all_bonus += $officialAddition;
$bonusLog .= ", officialAdditionFactor: $officialAdditionFactor, official_bonus: {$seedBonusResult['official_bonus']}, officialAddition: $officialAddition, all_bonus: $all_bonus";
$this->appendBonusLogInsert($bonusLogInsert, $uid, BonusLogs::BUSINESS_TYPE_SEEDING_OFFICIAL_ADDITION, $oldValue, $officialAddition);
$oldValue += $officialAddition;
}
if ($haremAdditionFactor > 0) {
$haremBonus = calculate_harem_addition($uid);
$haremAddition = $haremBonus * $haremAdditionFactor;
$all_bonus += $haremAddition;
$bonusLog .= ", haremAdditionFactor: $haremAdditionFactor, haremBonus: $haremBonus, haremAddition: $haremAddition, all_bonus: $all_bonus";
$this->appendBonusLogInsert($bonusLogInsert, $uid, BonusLogs::BUSINESS_TYPE_SEEDING_HAREM_ADDITION, $oldValue, $haremAddition);
$oldValue += $haremAddition;
}
if ($seedBonusResult['medal_additional_factor'] > 0) {
$medalAddition = $seedBonusResult['medal_bonus'] * $seedBonusResult['medal_additional_factor'];
$all_bonus += $medalAddition;
$bonusLog .= ", medalAdditionFactor: {$seedBonusResult['medal_additional_factor']}, medalBonus: {$seedBonusResult['medal_bonus']}, medalAddition: $medalAddition, all_bonus: $all_bonus";
$this->appendBonusLogInsert($bonusLogInsert, $uid, BonusLogs::BUSINESS_TYPE_SEEDING_MEDAL_ADDITION, $oldValue, $medalAddition);
$oldValue += $medalAddition;
}
do_log($bonusLog);
$dividend = 3600 / $autoclean_interval_one;
@@ -150,14 +160,6 @@ class CalculateUserSeedBonus implements ShouldQueue
$seedingTorrentCountUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['count']);
$seedingTorrentSizeUpdates[] = sprintf("when %d then %f", $uid, $seedBonusResult['size']);
$seedBonusUpdates[] = sprintf("when %d then seedbonus + %f", $uid, $all_bonus);
//here before/after not correct, don't record it, fill with -1
$this->appendBonusLogInsert($bonusLogInsert, $userInfo, [
BonusLogs::BUSINESS_TYPE_SEEDING_BASIC => $basicBonus,
BonusLogs::BUSINESS_TYPE_SEEDING_DONOR_ADDITION => $donorAddition,
BonusLogs::BUSINESS_TYPE_SEEDING_OFFICIAL_ADDITION => $officialAddition,
BonusLogs::BUSINESS_TYPE_SEEDING_HAREM_ADDITION => $haremAddition,
BonusLogs::BUSINESS_TYPE_SEEDING_MEDAL_ADDITION => $medalAddition,
]);
if ($fd) {
$log = sprintf(
'%s|%s|%s|%s|%s|%s|%s|%s',
@@ -180,10 +182,13 @@ class CalculateUserSeedBonus implements ShouldQueue
if ($delIdRedisKey) {
NexusDB::cache_del($this->idRedisKey);
}
if (!empty($bonusLogInsert)) {
BonusLogs::query()->insert($bonusLogInsert);
if ($fd) {
fwrite($fd, $logStr);
}
if (!empty($bonusLogInsert)) {
// BonusLogs::query()->insert($bonusLogInsert);
$this->insertIntoClickHouseBulk($bonusLogInsert);
}
fwrite($fd, $logStr);
$costTime = time() - $beginTimestamp;
do_log(sprintf(
"$logPrefix, [DONE], update user count: %s, result: %s, cost time: %s seconds",
@@ -203,21 +208,35 @@ class CalculateUserSeedBonus implements ShouldQueue
do_log("failed: " . $exception->getMessage() . $exception->getTraceAsString(), 'error');
}
private function appendBonusLogInsert(array &$bonusLogInsert, array $userInfo, array $typeValues): void
private function appendBonusLogInsert(array &$bonusLogInsert, int $uid, int $businessType, $oldValue, $delta): void
{
foreach ($typeValues as $type => $value) {
if ($value > 0) {
$bonusLogInsert[] = [
'business_type' => $type,
'uid' => $userInfo['id'],
'old_total_value' => -1,
'value' => $value,
'new_total_value' => -1,
'comment' => BonusLogs::$businessTypes[$type]['text'],
'created_at' => now()->toDateTimeString(),
'updated_at' => now()->toDateTimeString(),
];
}
if ($delta > 0) {
$bonusLogInsert[] = [
'business_type' => $businessType,
'uid' => $uid,
'old_total_value' => $oldValue,
'value' => $delta,
'new_total_value' => $oldValue + $delta,
'comment' => BonusLogs::$businessTypes[$businessType]['text'] ?? '',
'created_at' => getDtMicro(),
];
}
}
private function insertIntoClickHouseBulk(array $bonusLogInsert): void
{
if (!Setting::getIsRecordSeedingBonusLog()) {
do_log("not enabled");
return;
}
$host = config('clickhouse.connection.host');
if (!$host) {
do_log("clickhouse no host");
return;
}
$client = app(\ClickHouseDB\Client::class);
$fields = ['business_type', 'uid', 'old_total_value', 'value', 'new_total_value', 'comment', 'created_at'];
$client->insert("bonus_logs", $bonusLogInsert, $fields);
do_log("insertIntoClickHouseBulk done, created_at: {$bonusLogInsert[0]['created_at']}");
}
}

View File

@@ -258,11 +258,6 @@ class Setting extends NexusModel
return (int)self::get("backup.retention_count");
}
public static function getIsRecordAnnounceLog(): bool
{
return self::get('security.record_announce_logs') == 'yes';
}
public static function getIsRequireSeedSectionEnabled(): bool
{
return self::get("require_seed_section.enabled") == "yes";
@@ -322,5 +317,13 @@ class Setting extends NexusModel
return (int)self::get("torrent.reward_times_limit", 0);
}
public static function getIsRecordAnnounceLog(): bool
{
return self::get('system.is_record_announce_log') == 'yes';
}
public static function getIsRecordSeedingBonusLog(): bool
{
return self::get('system.is_record_seeding_bonus_log') == 'yes';
}
}

View File

@@ -1,6 +1,6 @@
<?php
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.9.7');
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-09-17');
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-09-19');
defined('IN_TRACKER') || define('IN_TRACKER', false);
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");

View File

@@ -305,7 +305,7 @@ function formatSpoiler($content, $title = '', $defaultCollapsed = true): string
}
// $content = str_replace(['<br>', '<br />'], '', $content);
$contentClass = "";
if ($defaultCollapsed) {
if (!$defaultCollapsed) {
$contentClass .= " open";
}
$HTML = sprintf(

View File

@@ -209,11 +209,9 @@ function do_log($log, $level = 'info', $echo = false)
}
}
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$dt = DateTime::createFromFormat('U.u', sprintf('%.6f', microtime(true)));
$dt->setTimezone(new DateTimeZone(nexus_env('TIMEZONE', 'UTC')));
$content = sprintf(
"[%s] [%s] [%s] [%s] [%s] [%s] %s.%s %s:%s %s%s%s %s%s",
$dt->format("Y-m-d\TH:i:s.vP"),
getDtMillis(true),
nexus() ? nexus()->getRequestId() : 'NO_REQUEST_ID',
nexus() ? nexus()->getLogSequence() : 0,
sprintf('%.3f', microtime(true) - (nexus() ? nexus()->getStartTimestamp() : 0)),
@@ -238,6 +236,20 @@ function do_log($log, $level = 'info', $echo = false)
}
}
function getDtMillis($withTimeZone = false): string {
$dt = DateTime::createFromFormat('U.u', sprintf('%.6f', microtime(true)));
$dt->setTimezone(new DateTimeZone(nexus_env('TIMEZONE', 'UTC')));
$format = $withTimeZone ? 'Y-m-d\TH:i:s.vP' : 'Y-m-d H:i:s.v';
return $dt->format($format);
}
function getDtMicro($withTimeZone = false): string {
$dt = DateTime::createFromFormat('U.u', sprintf('%.6f', microtime(true)));
$dt->setTimezone(new DateTimeZone(nexus_env('TIMEZONE', 'UTC')));
$format = $withTimeZone ? 'Y-m-d\TH:i:s.uP' : 'Y-m-d H:i:s.u';
return $dt->format($format);
}
function getLogFile($append = '')
{
static $logFiles = [];

View File

@@ -819,8 +819,6 @@ $lang_settings = array
'text_use_challenge_response_authentication_note' => '如果启用,登录时将不传输明文密码,建议启用。未来版本会删除此配置且启用此功能。',
'row_complain_enabled' => '启用申诉',
'row_complain_enabled_note' => '默认: "yes"',
'row_record_announce_logs' => '记录汇报日志',
'text_record_announce_logs_note' => '要启用,请先安装并启动 ClickHouse并在 .env 文件中添加配置',
'row_min_size' => '起步体积',
'text_bonus_mini_size' => '要求种子的体积最小为',
'text_bonus_mini_size_help' => '。小于此体积的种子不参与魔力计算。单位:字节(Byte),如 1 Byte = 1024 KiB。',

View File

@@ -819,8 +819,6 @@ $lang_settings = array
'text_use_challenge_response_authentication_note' => '如果啟用,登錄時將不傳輸明文密碼,建議啟用。未來版本會刪除此配置且啟用此功能。',
'row_complain_enabled' => '啟用申訴',
'row_complain_enabled_note' => '默認: "yes"',
'row_record_announce_logs' => '記錄匯報日誌',
'text_record_announce_logs_note' => '要啓用,請先安裝並啓動 ClickHouse並在 .env 文件中添加配置',
'row_min_size' => '起步體積',
'text_bonus_mini_size' => '要求種子的體積最小爲',
'text_bonus_mini_size_help' => '。小於此體積的種子不參與魔力計算。單位:字節(Byte),如 1 Byte = 1024 KiB。',

View File

@@ -819,8 +819,6 @@ $lang_settings = array
'text_use_challenge_response_authentication_note' => 'If enabled, no plaintext passwords will be transmitted at login, recommended. Future releases will remove this configuration and enable this feature.' ,
'row_complain_enabled' => 'Whether to enable complaints',
'row_complain_enabled_note' => 'default: "yes"',
'row_record_announce_logs' => 'Record reporting logs',
'text_record_announce_logs_note' => 'To enable it, first install and start ClickHouse and add the configuration in the .env file',
'row_min_size' => 'Minimum volume',
'text_bonus_mini_size'=> 'The minimum volume required for torrent is',
'text_bonus_mini_size_help' => '. Torrent size smaller than this volume are not included in the bonus calculation. Unit: bytes (Byte), e.g., 1 Byte = 1024 KiB.',

View File

@@ -113,7 +113,7 @@ if ($sort == 'seed_time') {
} else {
$query->orderBy($sort, $order);
}
$list = $query->get();
$list = $query->selectRaw("claims.*")->get();
print($filterForm);
print("<table id='claim-table' width='100%'>");
print("<tr>

View File

@@ -199,7 +199,7 @@ elseif ($action == 'savesettings_security') // save security
$validConfig = array(
'securelogin', 'securetracker', 'https_announce_url','iv','maxip','maxloginattempts','changeemail','cheaterdet','nodetect',
'guest_visit_type', 'guest_visit_value_static_page', 'guest_visit_value_custom_content', 'guest_visit_value_redirect',
'login_type', 'login_secret_lifetime', 'use_challenge_response_authentication', 'record_announce_logs'
'login_type', 'login_secret_lifetime', 'use_challenge_response_authentication',
);
GetVar($validConfig);
$SECURITY = [];
@@ -369,7 +369,6 @@ elseif ($action == 'securitysettings') //security settings
print ("<form method='post' action='".$_SERVER["SCRIPT_NAME"]."' name='securitysettings_form'><input type='hidden' name='action' value='savesettings_security'>");
tr($lang_settings['row_enable_ssl'],"<input type='radio' name='securelogin'" . ($SECURITY["securelogin"] == "yes" ? " checked" : "") . " value='yes'> ".$lang_settings['text_yes']. " <input type='radio' name='securelogin'" . ($SECURITY["securelogin"] == "no" ? " checked" : "") . " value='no'> ".$lang_settings['text_no']. " <input type='radio' name='securelogin'" . ($SECURITY["securelogin"] == "op" ? " checked" : "") . " value='op'> ".$lang_settings['text_optional']."<br />".$lang_settings['text_ssl_note'], 1);
// tr($lang_settings['row_enable_ssl_tracker'],"<input type='radio' name='securetracker'" . ($SECURITY["securetracker"] == "yes" ? " checked" : "") . " value='yes'> ".$lang_settings['text_yes']. " <input type='radio' name='securetracker'" . ($SECURITY["securetracker"] == "no" ? " checked" : "") . " value='no'> ".$lang_settings['text_no']. " <input type='radio' name='securetracker'" . ($SECURITY["securetracker"] == "op" ? " checked" : "") . " value='op'> ".$lang_settings['text_optional']."<br />".$lang_settings['text_ssl_note'], 1);
tr($lang_settings['row_record_announce_logs'],"<input type='radio' name='record_announce_logs'" . ($SECURITY["record_announce_logs"] == "yes" ? " checked" : "") . " value='yes'> ".$lang_settings['text_yes']. " <input type='radio' name='record_announce_logs'" . ($SECURITY["record_announce_logs"] == "no" ? " checked" : "") . " value='no'> ".$lang_settings['text_no']."<br />".$lang_settings['text_record_announce_logs_note'], 1);
// tr($lang_settings['row_https_announce_url'],"<input type='text' style=\"width: 300px\" name=https_announce_url value='".($SECURITY["https_announce_url"] ? $SECURITY["https_announce_url"] : "")."'> ".$lang_settings['text_https_announce_url_note'] . $_SERVER["HTTP_HOST"]."/announce.php", 1);
yesorno($lang_settings['row_enable_image_verification'], 'iv', $SECURITY["iv"], $lang_settings['text_image_verification_note']);
yesorno($lang_settings['row_allow_email_change'], 'changeemail', $SECURITY["changeemail"], $lang_settings['text_email_change_note']);

View File

@@ -126,6 +126,11 @@ return [
'access_admin_class_min_help' => 'Default: administrator, users with a user class greater than or equal to the set value can log into the admin backend',
'alarm_email_receiver' => 'Alarm email receiver',
'alarm_email_receiver_help' => "Fill in the UID of the user, separated by space, and the alarm email will be sent to the corresponding user's email address. If you don't fill it in, it will be written to the runtime log, and the log level will be error",
'is_record_announce_log' => 'Whether to record announce logs',
'is_record_announce_log_help' => 'To record logs, first install ClickHouse according to the documentation and enable this configuration. Default: no',
'is_record_seeding_bonus_log' => 'Whether to record seeding bonus logs',
'is_record_seeding_bonus_log_help' => 'To record, first install ClickHouse according to the documentation and enable this configuration. Default: no',
],
'image_hosting' => [
'driver' => 'Storage location',

View File

@@ -167,6 +167,10 @@ return [
'access_admin_class_min_help' => '默认:管理员,用户等级大于等于设定值的用户可以登录管理后台',
'alarm_email_receiver' => '告警邮件接收者',
'alarm_email_receiver_help' => '填写用户 UID多个空格隔开系统异常告警邮件将会发到对应用户的邮箱。如果不填会写到运行日志中日志级别为 error',
'is_record_announce_log' => '是否记录汇报日志',
'is_record_announce_log_help' => '若要记录,先根据文档安装 ClickHouse, 并启用此配置。默认no',
'is_record_seeding_bonus_log' => '是否记录做种魔力日志',
'is_record_seeding_bonus_log_help' => '若要记录,先根据文档安装 ClickHouse, 并启用此配置。默认no',
],
'image_hosting' => [
'driver' => '存储位置',

View File

@@ -126,6 +126,10 @@ return [
'access_admin_class_min_help' => '默認:管理員,用戶等級大於等於設定值的用戶可以登錄管理後臺',
'alarm_email_receiver' => '告警郵件接收者',
'alarm_email_receiver_help' => '填寫用戶 UID多個空格隔開系統異常告警郵件將會發到對應用戶的郵箱。如果不填會寫到運行日誌中日誌級別為 error',
'is_record_announce_log' => '是否記錄匯報日志',
'is_record_announce_log_help' => '若要記錄,先根據文檔安裝 ClickHouse, 並啓用此配置。默認no',
'is_record_seeding_bonus_log' => '是否記錄做種魔力日志',
'is_record_seeding_bonus_log_help' => '若要記錄,先根據文檔安裝 ClickHouse, 並啓用此配置。默認no',
],
'image_hosting' => [
'driver' => '存儲位置',