fix(admin): 让后台「销量浮动」设置真正生效

资源 metabox 和快捷编辑都能设置「销量浮动」(pay_count),但设了完全
不起作用——所有读取方用的是另一个键 virtual_sales(§3.8 电商的虚拟
销量加成),pay_count 只被写、从未被读。

新增统一读取口径 aksrt_get_content_sales_count($post, $pay_meta):
真实订单销量 + pay_count,并把四处各自拼装销量的地方全部接到这个
口径上:
- aksrt_get_download_box_meta() 的 sales_count(内容帖)
- aksrt_get_resource_meta() 的 sales_count(资源帖)
- 付费墙 aksrt_get_paywall_html() 的已售徽标
- 付费盒 aksrt_get_paid_box_html() 的已售徽标
- 后台「所有文章」销售数据列的销量

此前资源帖的 sales_count 走的是「虚拟销量 + 下载量」这条与内容帖
不一致的口径,现统一。

快捷编辑的 pay_count 运算本是整数加减 + max(0,...) 归零,与读取口径
一致,无需改动。

_aksrt_sales_volume 保持不变,它仍是订单流水累加的副本;列表列取
订单表统计与 meta 副本的较大值再加浮动量,避免两条链路数字打架。

新增 sales-volume-float-contract.test.cjs 4 项,锁住「pay_count 必须
被销量读取方消费」这一契约,防止以后又被改成摆设。

验证:node --test tests/widget-system/*.test.cjs 449 项全部通过
(原有 445 项 + 新增 4 项);php -l 三个 PHP 文件无语法错误。
This commit is contained in:
kate522 committed 2026-10-06 19:52:03 +08:00
1 parent cf6dc24193
commit 8e1fd0a30f
4 files changed
+90 -8

No files matched your search

+28 -6
View File
@@ -296,8 +296,8 @@ if (!function_exists('aksrt_get_download_box_meta')) {
'view_count' => max(0, $view_count),
'virtual_sales' => $virtual_sales,
'sales_volume' => max(0, $sales_volume),
// legacy: virtual (pay_cuont) + real sales_volume; fall back to downloads when no sales yet.
'sales_count' => $show_sales ? ($virtual_sales + max(0, $sales_volume, $download_count)) : 0,
// 展示销量 = 真实订单 + 后台「销量浮动」(pay_count)。
'sales_count' => $show_sales ? aksrt_get_content_sales_count($p, $pay_meta) : 0,
'show_sales' => $show_sales,
'download_limit_over_price' => $over_price,
'require_login' => !empty($pay_meta['require_login']),
@@ -352,6 +352,30 @@ if (!function_exists('aksrt_bump_content_sales_volume')) {
}
}
/**
* Total sales shown to visitors: real paid orders plus the configured 销量浮动.
*
* pay_count is the only field behind the 后台「销量浮动」输入框(资源 metabox 与
* 快捷编辑都写这个键),此前没有任何读取方用它,导致设了不生效。所有展示销量
* 的地方都必须走这个口径,不要再各自拼 virtual_sales / sales_volume。
*
* @param array<string,mixed> $pay_meta 已取得的付费 meta,传空则按 $post_id 现取。
*/
if (!function_exists('aksrt_get_content_sales_count')) {
function aksrt_get_content_sales_count($post = null, array $pay_meta = []): int {
$post_id = $post instanceof \WP_Post ? (int) $post->ID : (int) $post;
if ($post_id <= 0) {
return 0;
}
if ($pay_meta === []) {
$pay_meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : [];
}
$real = max(0, (int) get_post_meta($post_id, \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
$float = max(0, (int) ($pay_meta['pay_count'] ?? 0));
return max(0, $real + $float);
}
}
/**
* Whether guest cash purchase is allowed (legacy pay_no_logged_in + pay_limit=0).
*/
@@ -1348,8 +1372,7 @@ if (!function_exists('aksrt_get_paywall_html')) {
if ($doc === '') {
$doc = sprintf(__('此内容为%s,请付费后查看', 'aksrt'), $type_label);
}
$sales = max(0, (int) ($meta['virtual_sales'] ?? 0));
$sales += max(0, (int) get_post_meta($post->ID, \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
$sales = aksrt_get_content_sales_count($post, $meta);
$tag = $is_widget
? '<badge class="pay-tag abs-center">' . esc_html($type_label) . '</badge>'
: '<div class="pay-tag abs-center">' . esc_html($type_label) . '</div>';
@@ -1537,8 +1560,7 @@ if (!function_exists('aksrt_get_paid_box_html')) {
$order_type = (int) ($order_types[$pay_type] ?? 1);
$type_label = (string) ($type_labels[$pay_type] ?? __('付费内容', 'aksrt'));
$title = get_the_title($post);
$sales = max(0, (int) ($meta['virtual_sales'] ?? 0));
$sales += max(0, (int) get_post_meta($post->ID, \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
$sales = aksrt_get_content_sales_count($post, $meta);
$sales_label = function_exists('aksrt_format_number') ? aksrt_format_number($sales) : number_format_i18n($sales);
$count = $sales > 0 ? '<badge class="img-badge hot jb-blue px12">' . esc_html(sprintf(__('已售 %s', 'aksrt'), $sales_label)) . '</badge>' : '';
$order = aksrt_get_current_content_paid_order($post);