积分与余额账务系统硬切重构

This commit is contained in:
2026-08-24 15:59:11 +08:00
parent ca12226b39
commit 5cae5b5c93
159 changed files with 7275 additions and 4718 deletions
+3 -3
View File
@@ -154,8 +154,8 @@ trait CreditsPaymentGroups {
}
public static function affiliate_enabled(): bool { return PaymentRuntime::affiliate_enabled(); }
public static function affiliate_ratio_for_user(int $user_id = 0): float { return PaymentRuntime::affiliate_ratio_for_user($user_id > 0 ? $user_id : get_current_user_id()); }
public static function affiliate_ratio_for_level(int $level): float { return PaymentRuntime::affiliate_ratio_for_level($level); }
public static function affiliate_ratio_for_user(int $user_id = 0): int { return PaymentRuntime::affiliate_ratio_for_user($user_id > 0 ? $user_id : get_current_user_id()); }
public static function affiliate_ratio_for_level(int $level): int { return PaymentRuntime::affiliate_ratio_for_level($level); }
public static function get_affiliate_settings(): array { return PaymentRuntime::get_affiliate_settings(); }
public static function get_settlement_settings(): array { return PaymentRuntime::get_settlement_settings(); }
@@ -178,7 +178,7 @@ trait CreditsPaymentGroups {
/**
* 获取充值卡设置
*
* @return array{enabled:bool,desc:string,type:string,prefix:string,length:int,batch_size:int,max_redeem:int,expiry_days:int,amount:int,vip_days:int,credits:int,redeem_page:string,success_msg:string,fail_msg:string}
* @return array{enabled:bool,desc:string,type:string,prefix:string,length:int,batch_size:int,max_redeem:int,expiry_days:int,amount:string,vip_days:int,credits:int,redeem_page:string,success_msg:string,fail_msg:string}
*/
public static function get_card_pass_settings(): array {
return PaymentRuntime::get_card_pass_settings();
+8 -2
View File
@@ -286,8 +286,10 @@ class Integration {
'comment_reply' => true,
'comment_approved' => true,
'download_ready' => true,
'credits_added' => true,
'credits_deducted' => true,
'asset_points_increase' => true,
'asset_points_decrease' => true,
'asset_balance_increase' => true,
'asset_balance_decrease' => true,
'pending_comments' => true,
'upload_moderation' => true,
],
@@ -301,7 +303,11 @@ class Integration {
if (!is_array($saved)) {
$saved = [];
}
if (isset($saved['enabled_types']) && is_array($saved['enabled_types'])) {
$saved['enabled_types'] = array_intersect_key($saved['enabled_types'], $defaults['enabled_types']);
}
$merged = array_merge($defaults, $saved);
$merged['enabled_types'] = array_replace($defaults['enabled_types'], (array) ($saved['enabled_types'] ?? []));
$merged['read_retention_days'] = max(1, min(365, (int) $merged['read_retention_days']));
$merged['unread_retention_days'] = max(7, min(3650, (int) $merged['unread_retention_days']));
$merged['max_per_user'] = max(20, min(5000, (int) $merged['max_per_user']));
+23 -12
View File
@@ -88,13 +88,12 @@ class PaymentRuntime {
return [
'enabled' => !empty($settings['pay_balance_s']),
'desc' => $desc,
'auto_to_balance' => !isset($settings['pay_auto_to_balance']) || !empty($settings['pay_auto_to_balance']),
'packages_raw' => $packages,
'package_count' => self::count_structured_lines($packages),
'help_text' => $desc !== '' ? $desc : __('当前支持人工确认与测试联调;接入真实网关时复用同一组字段。', 'aksrt'),
'product' => [
'name' => sanitize_text_field((string) ($settings['pay_balance_product_name'] ?? '')),
'price' => max(0, (float) ($settings['pay_balance_price'] ?? 0)),
'price' => self::money_decimal($settings['pay_balance_price'] ?? '0.00', '0.00'),
'tag' => sanitize_text_field((string) ($settings['pay_balance_tag'] ?? '元')),
'tag_class' => sanitize_text_field((string) ($settings['pay_balance_tag_class'] ?? '')),
'custom_s' => !isset($settings['pay_balance_product_custom_s']) || !empty($settings['pay_balance_product_custom_s']),
@@ -234,7 +233,7 @@ class PaymentRuntime {
'desc' => sanitize_textarea_field((string) ($settings['vip_renew_desc'] ?? '')),
'price_type' => $renew_price_type,
'discount' => max(1, min(100, (int) ($settings['vip_renew_discount'] ?? 8))),
'reduce' => max(0, min(9999, (int) ($settings['vip_renew_reduce'] ?? 20))),
'reduce' => self::money_decimal($settings['vip_renew_reduce'] ?? '20.00', '20.00', 999900),
],
'upgrade' => [
'enabled' => !isset($settings['vip_upgrade']) || !empty($settings['vip_upgrade']),
@@ -284,7 +283,7 @@ class PaymentRuntime {
*/
public static function get_payment_affiliate_settings(): array {
$settings = Settings::get_all();
$clamp_ratio = static fn ($value): float => max(0, min(100, (float) $value));
$clamp_ratio = static fn($value): int => max(0, min(100, (int) $value));
$types = (array) ($settings['affiliate_order_types'] ?? ['membership', 'content']);
$types = array_values(array_intersect(['membership', 'content', 'credits'], array_map('sanitize_key', $types)));
if (!$types) { $types = ['membership', 'content']; }
@@ -303,14 +302,14 @@ class PaymentRuntime {
}
public static function affiliate_enabled(): bool { return !empty(self::get_payment_affiliate_settings()['enabled']); }
public static function affiliate_ratio_for_user(int $user_id): float {
public static function affiliate_ratio_for_user(int $user_id): int {
$level = function_exists('aksrt_get_user_vip_level') ? (int) aksrt_get_user_vip_level($user_id) : 0;
return self::affiliate_ratio_for_level($level);
}
public static function affiliate_ratio_for_level(int $level): float {
public static function affiliate_ratio_for_level(int $level): int {
$settings = self::get_payment_affiliate_settings();
$key = $level >= 2 ? 'vip_2' : ($level >= 1 ? 'vip_1' : 'user');
return (float) ($settings['ratios'][$key] ?? 0);
return (int) ($settings['ratios'][$key] ?? 0);
}
public static function get_affiliate_settings(): array { return self::get_payment_affiliate_settings(); }
public static function get_settlement_settings(): array {
@@ -433,7 +432,7 @@ class PaymentRuntime {
'batch_size' => max(1, min(500, (int) ($s['card_pass_batch_size'] ?? 50))),
'max_redeem' => max(1, min(100, (int) ($s['card_pass_max_redeem'] ?? 1))),
'expiry_days' => max(0, (int) ($s['card_pass_expiry'] ?? 0)),
'amount' => max(1, min(9999, (int) ($s['card_pass_amount'] ?? 10))),
'amount' => self::money_decimal($s['card_pass_amount'] ?? '10.00', '10.00', 999900),
'vip_days' => max(1, min(3650, (int) ($s['card_pass_vip_days'] ?? 30))),
'credits' => max(0, min(99999, (int) ($s['card_pass_credits'] ?? 0))),
'redeem_page' => esc_url_raw((string) ($s['card_pass_redeem_page'] ?? '')),
@@ -442,16 +441,16 @@ class PaymentRuntime {
];
}
/** @return array{min_amount:float,service_charge_rate:int,daily_count:int,daily_limit:float,reset_time:string,payout_channels:string[]} */
/** @return array{min_amount:string,service_charge_rate:int,daily_count:int,daily_limit:string,reset_time:string,payout_channels:string[]} */
public static function get_withdrawal_settings(): array {
$s = Settings::get_all();
$channels = array_values(array_intersect(['alipay', 'wechat'], array_map('sanitize_key', (array) ($s['affiliate_payout_channels'] ?? ['alipay', 'wechat']))));
if (!$channels) { $channels = ['alipay', 'wechat']; }
return [
'min_amount' => max(0, min(999999, (float) ($s['affiliate_min_withdrawal'] ?? 50))),
'service_charge_rate' => (int) round(max(0, min(100, (float) ($s['affiliate_withdrawal_fee'] ?? 5)))),
'min_amount' => self::money_decimal($s['affiliate_min_withdrawal'] ?? '50.00', '50.00', 99999900),
'service_charge_rate' => max(0, min(100, (int) ($s['affiliate_withdrawal_fee'] ?? 5))),
'daily_count' => max(0, min(99999, (int) ($s['affiliate_daily_withdrawal_count'] ?? 3))),
'daily_limit' => max(0, min(999999, (float) ($s['affiliate_daily_withdrawal_limit'] ?? 500))),
'daily_limit' => self::money_decimal($s['affiliate_daily_withdrawal_limit'] ?? '500.00', '500.00', 99999900),
'reset_time' => preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', (string) ($s['affiliate_withdrawal_reset_time'] ?? '00:00'))
? (string) $s['affiliate_withdrawal_reset_time']
: '00:00',
@@ -516,6 +515,18 @@ class PaymentRuntime {
return $count;
}
private static function money_decimal(mixed $value, string $default, int $max_minor = PHP_INT_MAX): string {
try {
if (!is_string($value)) {
throw new \InvalidArgumentException('money_decimal_must_be_string');
}
$amount = \AKSRT\Ledger\AssetAmount::balance(trim($value));
return $amount->minor() <= $max_minor ? $amount->decimal() : $default;
} catch (\InvalidArgumentException $e) {
return $default;
}
}
/**
* @param array<int,mixed> $products
*/
+44 -10
View File
@@ -152,14 +152,37 @@ class PostDisplay {
if ($rewards_text === '') {
$rewards_text = '赞赏';
}
$rewards_amounts = array_values(array_filter(array_map(static function ($value): float {
return round((float) $value, 2);
}, explode(',', (string) ($settings['post_rewards_amounts'] ?? '5,10,20,50,100'))), static fn(float $value): bool => $value > 0));
if ($rewards_amounts === []) {
$rewards_amounts = [5, 10, 20, 50, 100];
}
$rewards_min = max(0.01, round((float) ($settings['post_rewards_min'] ?? 1), 2));
$rewards_max = max($rewards_min, round((float) ($settings['post_rewards_max'] ?? 1000), 2));
$reward_amount = static function ($value, string $default): \AKSRT\Ledger\AssetAmount {
try {
return \AKSRT\Ledger\AssetAmount::balance(is_string($value) ? trim($value) : '');
} catch (\InvalidArgumentException $e) {
return \AKSRT\Ledger\AssetAmount::balance($default);
}
};
$rewards_amounts = [];
foreach (explode(',', (string) ($settings['post_rewards_amounts'] ?? '5.00,10.00,20.00,50.00,100.00')) as $value) {
try {
$parsed = \AKSRT\Ledger\AssetAmount::balance(trim($value));
if ($parsed->minor() > 0) {
$rewards_amounts[] = $parsed->decimal();
}
} catch (\InvalidArgumentException $e) {
continue;
}
}
if ($rewards_amounts === []) {
$rewards_amounts = ['5.00', '10.00', '20.00', '50.00', '100.00'];
}
$rewards_min_amount = $reward_amount($settings['post_rewards_min'] ?? '1.00', '1.00');
if ($rewards_min_amount->minor() <= 0) {
$rewards_min_amount = \AKSRT\Ledger\AssetAmount::balance('0.01');
}
$rewards_max_amount = $reward_amount($settings['post_rewards_max'] ?? '1000.00', '1000.00');
if ($rewards_max_amount->minor() < $rewards_min_amount->minor()) {
$rewards_max_amount = $rewards_min_amount;
}
$rewards_min = $rewards_min_amount->decimal();
$rewards_max = $rewards_max_amount->decimal();
$author_card_limit = max(0, min(12, (int) ($settings['post_author_card_limit'] ?? 6)));
$author_card_orderby = sanitize_key((string) ($settings['post_author_card_orderby'] ?? 'views'));
if (!in_array($author_card_orderby, ['views', 'date', 'modified', 'comment_count'], true)) {
@@ -277,6 +300,17 @@ class PostDisplay {
public static function get_submit_settings(): array {
$settings = Settings::get_all();
$money_setting = static function ($value, string $default): string {
if (!is_string($value)) {
return $default;
}
try {
return \AKSRT\Ledger\AssetAmount::balance($value)->decimal();
} catch (\InvalidArgumentException $e) {
return $default;
}
};
$title_min = max(1, min(100, (int) ($settings['post_article_title_strlen_limit_min'] ?? 5)));
$title_max = max(5, min(200, (int) ($settings['post_article_title_strlen_limit_max'] ?? 30)));
if ($title_max < $title_min) {
@@ -317,8 +351,8 @@ class PostDisplay {
'draft_save' => !isset($settings['front_submit_draft_save']) || !empty($settings['front_submit_draft_save']),
'leave_warning' => !isset($settings['front_submit_leave_warning']) || !empty($settings['front_submit_leave_warning']),
'pay_free_s' => !isset($settings['post_article_pay_free_s']) || !empty($settings['post_article_pay_free_s']),
'pay_price_min' => max(0, (float) ($settings['post_article_pay_price_min'] ?? 0)),
'pay_price_max' => max(0, (float) ($settings['post_article_pay_price_max'] ?? 100)),
'pay_price_min' => $money_setting($settings['post_article_pay_price_min'] ?? '0.00', '0.00'),
'pay_price_max' => $money_setting($settings['post_article_pay_price_max'] ?? '100.00', '100.00'),
'pay_vip_price_s' => !isset($settings['post_article_pay_vip_price_s']) || !empty($settings['post_article_pay_vip_price_s']),
'pay_vip_1_discount' => max(0, min(100, (int) ($settings['post_article_pay_vip_1_discount'] ?? 80))),
'pay_vip_2_discount' => max(0, min(100, (int) ($settings['post_article_pay_vip_2_discount'] ?? 50))),
+18 -5
View File
@@ -105,7 +105,7 @@ class UserRuntime {
* User identity, level, medal, and report governance settings.
*
* @return array{
* verify:array{enabled:bool,public_display:bool,allow_apply:bool,disable_desc:string,desc:string,types:array<string,array{slug:string,label:string,badge_text:string,fee:float,free_applications:int,require_real_name:bool}>},
* verify:array{enabled:bool,public_display:bool,allow_apply:bool,disable_desc:string,desc:string,types:array<string,array{slug:string,label:string,badge_text:string,fee:string,free_applications:int,require_real_name:bool}>},
* level:array{enabled:bool,public_display:bool,source:string,label_prefix:string,thresholds:array<int,int>},
* medal:array{enabled:bool,public_display:bool,limit:int,categories:array<int,array{cat_name:string,items:array<int,array{slug:string,name:string,desc:string,icon:string,get_type:string,get_val:int}>}>,items_by_slug:array<string,array{slug:string,name:string,desc:string,icon:string,get_type:string,get_val:int,cat_name:string}>},
* badge:array{enabled:bool,public_display:bool,limit:int,dictionary:array<int,array{slug:string,label:string,tone:string,desc:string,icon_url?:string}>},
@@ -120,7 +120,7 @@ class UserRuntime {
'slug' => 'personal',
'label' => __('个人认证', 'aksrt'),
'badge_text' => trim((string) ($settings['user_verify_personal_badge_text'] ?? '')) ?: __('个人认证', 'aksrt'),
'fee' => max(0, round((float) ($settings['user_verify_personal_fee'] ?? 0), 2)),
'fee' => self::identity_fee_setting($settings, 'user_verify_personal_fee'),
'free_applications' => max(0, (int) ($settings['user_verify_personal_free_applications'] ?? 0)),
'require_real_name' => true,
],
@@ -128,7 +128,7 @@ class UserRuntime {
'slug' => 'enterprise',
'label' => __('企业认证', 'aksrt'),
'badge_text' => trim((string) ($settings['user_verify_enterprise_badge_text'] ?? '')) ?: __('企业认证', 'aksrt'),
'fee' => max(0, round((float) ($settings['user_verify_enterprise_fee'] ?? 0), 2)),
'fee' => self::identity_fee_setting($settings, 'user_verify_enterprise_fee'),
'free_applications' => max(0, (int) ($settings['user_verify_enterprise_free_applications'] ?? 0)),
'require_real_name' => true,
],
@@ -182,6 +182,19 @@ class UserRuntime {
];
}
/** @param array<string,mixed> $settings */
private static function identity_fee_setting(array $settings, string $key): string {
$value = array_key_exists($key, $settings) ? $settings[$key] : '0.00';
if (!is_string($value)) {
return '';
}
try {
return \AKSRT\Ledger\AssetAmount::balance(trim($value))->decimal();
} catch (\InvalidArgumentException $e) {
return '';
}
}
/**
* 禁封设置
*/
@@ -245,8 +258,8 @@ class UserRuntime {
'like',
'comment_like',
'views',
'pay_income',
'pay_rebate',
'creator_income',
'affiliate_commission',
'manually_add',
];