diff --git a/assets/admin/src/schema/sections/post-list/comment.js b/assets/admin/src/schema/sections/post-list/comment.js index 721f3786..bf672280 100644 --- a/assets/admin/src/schema/sections/post-list/comment.js +++ b/assets/admin/src/schema/sections/post-list/comment.js @@ -20,6 +20,7 @@ export const commentSchema = { comment_img: true, comment_upload_img: false, comment_like_s: true, + comment_hot_threshold: 10, comment_quick_s: true, comment_city_s: true, comment_author_tag: true, @@ -78,6 +79,7 @@ export const commentSchema = { title: '互动功能', icon: 'fas fa-heart', fields: [ + { key: 'comment_hot_threshold', label: '神评点赞阈值', type: 'number', default: 10, min: 1, max: 999, help: '点赞数达到该值后显示「神评」徽章', dependsOn: { key: 'comment_like_s', value: true } }, { key: 'comment_like_s', label: '评论点赞', type: 'switch', default: true }, { key: 'comment_quick_s', label: '快捷回复', type: 'switch', default: true }, { key: 'comment_city_s', label: '评论城市', type: 'switch', default: true }, diff --git a/assets/admin/src/schema/sections/users/credits.js b/assets/admin/src/schema/sections/users/credits.js index 0199f2ca..09ff0727 100644 --- a/assets/admin/src/schema/sections/users/credits.js +++ b/assets/admin/src/schema/sections/users/credits.js @@ -22,6 +22,10 @@ export const creditsSchema = { points_free_bbs_posts_new: 5, points_free_bbs_reply: 2, points_free_bbs_essence: 10, + points_free_bbs_adopt: 20, + points_free_bbs_posts_hot: 5, + points_free_bbs_comment_hot: 5, + points_free_bbs_score_extra: 3, points_earn_invite: 10, points_spend_comment: 0, points_spend_download: 0, @@ -73,6 +77,10 @@ export const creditsSchema = { { key: 'points_free_bbs_posts_new', label: '论坛发帖', type: 'number', default: 5, min: 0, max: 1000 }, { key: 'points_free_bbs_reply', label: '论坛回复', type: 'number', default: 2, min: 0, max: 1000 }, { key: 'points_free_bbs_essence', label: '帖子加精', type: 'number', default: 10, min: 0, max: 1000 }, + { key: 'points_free_bbs_adopt', label: '回复被采纳', type: 'number', default: 20, min: 0, max: 1000 }, + { key: 'points_free_bbs_posts_hot', label: '热帖奖励', type: 'number', default: 5, min: 0, max: 1000 }, + { key: 'points_free_bbs_comment_hot', label: '神评奖励', type: 'number', default: 5, min: 0, max: 1000 }, + { key: 'points_free_bbs_score_extra', label: '帖子加分', type: 'number', default: 3, min: 0, max: 1000 }, { key: 'points_earn_invite', label: '邀请注册奖励', type: 'number', default: 10, min: 0, max: 1000, help: '邀请不计入免费日上限' }, ], }, diff --git a/assets/css/frontend/comments.css b/assets/css/frontend/comments.css index 3520487e..783dfbd6 100644 --- a/assets/css/frontend/comments.css +++ b/assets/css/frontend/comments.css @@ -671,3 +671,34 @@ body.dark-theme .comment-login-prompt__inner { margin-bottom: 0; opacity: 0.9; } + + +/* Identity chips + 神评 */ +.aksrt-identity-chip { + display: inline-flex; + align-items: center; + gap: 3px; + vertical-align: middle; + max-width: 9em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.aksrt-identity-chip--comment { + font-size: 11px; + padding: 0 6px; +} +.aksrt-comment__badge--hot { + color: #ea580c; + background: rgba(234, 88, 12, 0.1); + border-radius: 999px; + padding: 0 6px; + margin-left: 4px; + font-size: 11px; +} +.aksrt-comment__header .author-box { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 4px; +} diff --git a/assets/css/frontend/single-post/author-actions.css b/assets/css/frontend/single-post/author-actions.css index b9674b1a..50f919fc 100644 --- a/assets/css/frontend/single-post/author-actions.css +++ b/assets/css/frontend/single-post/author-actions.css @@ -278,6 +278,7 @@ .share-item[data-platform="twitter"]:hover { background: #f0f0f0; color: #000; } .share-item[data-platform="wechat"]:hover { background: #e6f7ec; color: #07c160; } .share-item[data-platform="copy"]:hover { background: #f0f2f5; color: #555; } +.share-item[data-platform="poster"]:hover { background: #fff4e8; color: #f59e0b; } /* 微信二维码浮层 */ #share-qrcode-box { @@ -332,6 +333,7 @@ html.dark .share-item { .theme-dark .share-item[data-platform="twitter"]:hover { background: rgba(255,255,255,0.08); color: #fff; } .theme-dark .share-item[data-platform="wechat"]:hover { background: rgba(7,193,96,0.12); color: #07c160; } .theme-dark .share-item[data-platform="copy"]:hover { background: rgba(255,255,255,0.06); color: #ccc; } +.theme-dark .share-item[data-platform="poster"]:hover { background: rgba(245,158,11,0.12); color: #f59e0b; } .theme-dark .post-drop-meta__menu, html.dark .post-drop-meta__menu { diff --git a/assets/css/frontend/widgets/part-2.css b/assets/css/frontend/widgets/part-2.css index 14337910..9cc4840d 100644 --- a/assets/css/frontend/widgets/part-2.css +++ b/assets/css/frontend/widgets/part-2.css @@ -298,3 +298,15 @@ } /* ============================================================ + + +.aksrt-null--widget { + padding: 12px 0 !important; +} +.aksrt-null--widget .aksrt-null__img { + margin: 0 auto; + display: block; +} +.aksrt-null .separator { + border: 0; +} diff --git a/assets/img/null.svg b/assets/img/null.svg new file mode 100644 index 00000000..d6dd55b1 --- /dev/null +++ b/assets/img/null.svg @@ -0,0 +1,9 @@ + diff --git a/assets/js/reward.js b/assets/js/reward.js index 6090367f..d6455f7c 100644 --- a/assets/js/reward.js +++ b/assets/js/reward.js @@ -40,6 +40,13 @@ } btn.addEventListener('click', openReward); + document.querySelectorAll('[data-aksrt-reward-open]').forEach(function (el) { + if (el === btn) return; + el.addEventListener('click', function (e) { + e.preventDefault(); + openReward(); + }); + }); if (closeBtn) closeBtn.addEventListener('click', closeReward); if (overlay) overlay.addEventListener('click', closeReward); diff --git a/inc/bootstrap.php b/inc/bootstrap.php index e5573087..9be8898c 100644 --- a/inc/bootstrap.php +++ b/inc/bootstrap.php @@ -43,6 +43,8 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-resource.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-pay.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-coupon-order.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-credits.php'; +require_once AKSRT_DIR . '/inc/helpers/aksrt-null.php'; +require_once AKSRT_DIR . '/inc/helpers/aksrt-identity.php'; require_once AKSRT_DIR . '/inc/helpers/menu-decoration.php'; require_once AKSRT_DIR . '/inc/helpers/class-aksrt-nav-walker.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-transactions.php'; diff --git a/inc/classes/admin/schema/class-aksrt-admin-schema-post-list.php b/inc/classes/admin/schema/class-aksrt-admin-schema-post-list.php index 2ca5ce6b..57febfbf 100644 --- a/inc/classes/admin/schema/class-aksrt-admin-schema-post-list.php +++ b/inc/classes/admin/schema/class-aksrt-admin-schema-post-list.php @@ -250,6 +250,7 @@ class AKSRT_Admin_Schema_Post_List { ['key' => 'comment_upload_img', 'label' => '评论图片上传', 'type' => 'switch', 'default' => false], ]], ['key' => 'interaction', 'title' => '互动功能', 'fields' => [ + ['key' => 'comment_hot_threshold', 'label' => '神评点赞阈值', 'type' => 'number', 'default' => 10, 'min' => 1, 'max' => 999], ['key' => 'comment_like_s', 'label' => '评论点赞', 'type' => 'switch', 'default' => true], ['key' => 'comment_quick_s', 'label' => '快捷回复', 'type' => 'switch', 'default' => true], ['key' => 'comment_city_s', 'label' => '评论城市', 'type' => 'switch', 'default' => true], diff --git a/inc/classes/admin/schema/class-aksrt-admin-schema-users.php b/inc/classes/admin/schema/class-aksrt-admin-schema-users.php index 86055c6a..6edd2ed8 100644 --- a/inc/classes/admin/schema/class-aksrt-admin-schema-users.php +++ b/inc/classes/admin/schema/class-aksrt-admin-schema-users.php @@ -428,6 +428,10 @@ class AKSRT_Admin_Schema_Users { ['key' => 'points_free_bbs_posts_new', 'label' => '论坛发帖', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 1000], ['key' => 'points_free_bbs_reply', 'label' => '论坛回复', 'type' => 'number', 'default' => 2, 'min' => 0, 'max' => 1000], ['key' => 'points_free_bbs_essence', 'label' => '帖子加精', 'type' => 'number', 'default' => 10, 'min' => 0, 'max' => 1000], + ['key' => 'points_free_bbs_adopt', 'label' => '回复被采纳', 'type' => 'number', 'default' => 20, 'min' => 0, 'max' => 1000], + ['key' => 'points_free_bbs_posts_hot', 'label' => '热帖奖励', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 1000], + ['key' => 'points_free_bbs_comment_hot', 'label' => '神评奖励', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 1000], + ['key' => 'points_free_bbs_score_extra', 'label' => '帖子加分', 'type' => 'number', 'default' => 3, 'min' => 0, 'max' => 1000], ['key' => 'points_earn_invite', 'label' => '邀请注册奖励', 'type' => 'number', 'default' => 10, 'min' => 0, 'max' => 1000, 'help' => '邀请奖励不走免费日上限'], ]], ['key' => 'spend', 'title' => '积分消费', 'fields' => [ diff --git a/inc/classes/class-aksrt-credits-earn.php b/inc/classes/class-aksrt-credits-earn.php index 877bafe9..b872857f 100644 --- a/inc/classes/class-aksrt-credits-earn.php +++ b/inc/classes/class-aksrt-credits-earn.php @@ -234,22 +234,14 @@ class AKSRT_Credits_Earn { } } - public function on_forum_topic_featured(int $topic_id, int $operator_id): void { - unset($operator_id); - $post = get_post($topic_id); - if (!$post || $post->post_type !== 'aksrt_forum_topic') { - return; - } - $user_id = (int) $post->post_author; - if ($user_id <= 0 || !class_exists('AKSRT_Points_Free')) { - return; - } - if (get_post_meta($topic_id, 'aksrt_credit_essence_rewarded', true)) { - return; - } - $granted = AKSRT_Points_Free::grant($user_id, 'bbs_essence', 0, ['type' => 'post', 'id' => $topic_id]); - if (!is_wp_error($granted) && $granted > 0) { - update_post_meta($topic_id, 'aksrt_credit_essence_rewarded', 1); - } + /** + * Essence reward is owned by AKSRT_Forum::reward_essence() (free-day-cap). + * Keep hook for external integrations only — do not double-grant. + * + * @param int $topic_id + * @param int $author_or_operator Author id from forum (2nd arg) or operator. + */ + public function on_forum_topic_featured(int $topic_id, int $author_or_operator = 0): void { + unset($topic_id, $author_or_operator); } } diff --git a/inc/classes/class-aksrt-points-free.php b/inc/classes/class-aksrt-points-free.php index b4d1bae7..6bdf767b 100644 --- a/inc/classes/class-aksrt-points-free.php +++ b/inc/classes/class-aksrt-points-free.php @@ -33,6 +33,10 @@ class AKSRT_Points_Free { 'bbs_posts_new' => [__('论坛发帖', 'aksrt'), __('发布论坛主题', 'aksrt'), 5], 'bbs_reply' => [__('论坛回复', 'aksrt'), __('回复论坛主题', 'aksrt'), 2], 'bbs_essence' => [__('帖子加精', 'aksrt'), __('主题被设为精华', 'aksrt'), 10], + 'bbs_adopt' => [__('回复被采纳', 'aksrt'), __('问答帖回复被采纳', 'aksrt'), 20], + 'bbs_posts_hot' => [__('热帖奖励', 'aksrt'), __('主题成为热帖', 'aksrt'), 5], + 'bbs_comment_hot'=> [__('神评奖励', 'aksrt'), __('回复成为神评', 'aksrt'), 5], + 'bbs_score_extra'=> [__('帖子加分', 'aksrt'), __('主题被加分时奖励', 'aksrt'), 3], ]; } @@ -276,6 +280,36 @@ class AKSRT_Points_Free { return max(0, (int) ($f['credits']['comment_new'] ?? $default)); } return $default; + case 'bbs_essence': + if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) { + $f = AKSRT_Settings::get_forum_settings(); + return max(0, (int) ($f['credits']['essence'] ?? $default)); + } + return $default; + case 'bbs_adopt': + if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) { + $f = AKSRT_Settings::get_forum_settings(); + return max(0, (int) ($f['credits']['adopted'] ?? $default)); + } + return $default; + case 'bbs_posts_hot': + if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) { + $f = AKSRT_Settings::get_forum_settings(); + return max(0, (int) ($f['credits']['posts_hot'] ?? $default)); + } + return $default; + case 'bbs_comment_hot': + if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) { + $f = AKSRT_Settings::get_forum_settings(); + return max(0, (int) ($f['credits']['comment_hot'] ?? $f['credits']['comment_like'] ?? $default)); + } + return $default; + case 'bbs_score_extra': + if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_forum_settings')) { + $f = AKSRT_Settings::get_forum_settings(); + return max(0, (int) ($f['credits']['score_extra'] ?? $default)); + } + return $default; default: return $default; } diff --git a/inc/classes/class-aksrt-widgets-batch1.php b/inc/classes/class-aksrt-widgets-batch1.php index 22e558c8..50e6d6f4 100644 --- a/inc/classes/class-aksrt-widgets-batch1.php +++ b/inc/classes/class-aksrt-widgets-batch1.php @@ -192,7 +192,7 @@ class AKSRT_Recent_Comments_Widget extends WP_Widget { -

+ ' . esc_html__('暂无评论', 'aksrt') . '

'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> -

+ ' . esc_html__('暂无友情链接', 'aksrt') . '

'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> '; wp_reset_postdata(); } else { - echo '

' . esc_html__('暂无热门文章', 'aksrt') . '

'; + echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无热门文章', 'aksrt')) : '

' . esc_html__('暂无热门文章', 'aksrt') . '

'; } echo $args['after_widget']; @@ -360,7 +360,7 @@ class AKSRT_Tab_Posts_Widget extends WP_Widget { -

+ ' . esc_html__('暂无文章', 'aksrt') . '

'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> diff --git a/inc/classes/class-aksrt-withdrawal.php b/inc/classes/class-aksrt-withdrawal.php index 8c8a363c..ac5b8c87 100644 --- a/inc/classes/class-aksrt-withdrawal.php +++ b/inc/classes/class-aksrt-withdrawal.php @@ -167,7 +167,31 @@ class AKSRT_Withdrawal { } $ok = $wpdb->update($this->table(), $update, ['id' => $withdrawal_id], $format, ['%d']) !== false; - return $ok ? true : new WP_Error('aksrt_withdraw_update', __('提现状态更新失败', 'aksrt')); + if (!$ok) { + return new WP_Error('aksrt_withdraw_update', __('提现状态更新失败', 'aksrt')); + } + + // Notify user on terminal status (Zibll withdraw_reply parity). + if (class_exists('AKSRT_Message') && $user_id > 0 && in_array($new_status, [self::STATUS_COMPLETED, self::STATUS_REJECTED], true)) { + $gross = $this->resolve_gross_amount($row, $meta); + $amount_txt = class_exists('AKSRT_Balance') ? AKSRT_Balance::format($gross) : number_format($gross, 2); + if ($new_status === self::STATUS_REJECTED) { + $title = __('提现已拒绝', 'aksrt'); + $body = sprintf(__('您申请提现 %s 元已被拒绝,冻结金额已退回钱包。', 'aksrt'), $amount_txt); + if ($note !== '') { + $body .= ' ' . sprintf(__('备注:%s', 'aksrt'), $note); + } + } else { + $title = __('提现已完成', 'aksrt'); + $body = sprintf(__('您申请提现 %s 元已处理完成。', 'aksrt'), $amount_txt); + } + AKSRT_Message::get_instance()->send($user_id, 'withdraw', $body, [ + 'title' => $title, + 'target_url' => function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url('credits') : '', + ]); + } + + return true; } /** diff --git a/inc/classes/forum/class-aksrt-forum-comment-service.php b/inc/classes/forum/class-aksrt-forum-comment-service.php index 5a76d038..a934762e 100644 --- a/inc/classes/forum/class-aksrt-forum-comment-service.php +++ b/inc/classes/forum/class-aksrt-forum-comment-service.php @@ -312,17 +312,27 @@ class AKSRT_Forum_Comment_Service { update_post_meta($topic_id, '_aksrt_question_status', 1); // QUESTION_SOLVED $adoptee_id = (int) $comment->user_id; - if ($adoptee_id > 0 && class_exists('AKSRT_Credits')) { + if ($adoptee_id > 0) { $config = $this->forum->get_config(); $amount = (int) ($config['credits']['adopted'] ?? 20); if ($amount > 0) { $already = get_comment_meta($comment_id, '_aksrt_adopt_credited', true); if (!$already) { - \AKSRT_Credits::add($adoptee_id, $amount, 'forum_adopted', [ - 'type' => 'comment', - 'id' => $comment_id, - ]); - update_comment_meta($comment_id, '_aksrt_adopt_credited', '1'); + if (class_exists('AKSRT_Points_Free')) { + $granted = \AKSRT_Points_Free::grant($adoptee_id, 'bbs_adopt', $amount, [ + 'type' => 'comment', + 'id' => $comment_id, + ]); + if (!is_wp_error($granted) && (int) $granted > 0) { + update_comment_meta($comment_id, '_aksrt_adopt_credited', '1'); + } + } elseif (class_exists('AKSRT_Credits')) { + \AKSRT_Credits::add($adoptee_id, $amount, 'forum_adopted', [ + 'type' => 'comment', + 'id' => $comment_id, + ]); + update_comment_meta($comment_id, '_aksrt_adopt_credited', '1'); + } } } @@ -498,15 +508,22 @@ class AKSRT_Forum_Comment_Service { update_comment_meta($comment_id, '_aksrt_is_hot', '1'); - // Credit the comment author + // Credit the comment author (free-task day cap). $author_id = (int) $comment->user_id; - if ($author_id > 0 && class_exists('AKSRT_Credits')) { - $amount = (int) ($config['credits']['comment_like'] ?? 1); + if ($author_id > 0) { + $amount = (int) ($config['credits']['comment_hot'] ?? $config['credits']['comment_like'] ?? 5); if ($amount > 0) { - \AKSRT_Credits::add($author_id, $amount, 'forum_hot_comment', [ - 'type' => 'comment', - 'id' => $comment_id, - ]); + if (class_exists('AKSRT_Points_Free')) { + \AKSRT_Points_Free::grant($author_id, 'bbs_comment_hot', $amount, [ + 'type' => 'comment', + 'id' => $comment_id, + ]); + } elseif (class_exists('AKSRT_Credits')) { + \AKSRT_Credits::add($author_id, $amount, 'forum_hot_comment', [ + 'type' => 'comment', + 'id' => $comment_id, + ]); + } } } diff --git a/inc/classes/forum/class-aksrt-forum-score-service.php b/inc/classes/forum/class-aksrt-forum-score-service.php index 5cd648e0..46a6dddf 100644 --- a/inc/classes/forum/class-aksrt-forum-score-service.php +++ b/inc/classes/forum/class-aksrt-forum-score-service.php @@ -126,16 +126,23 @@ class AKSRT_Forum_Score_Service { $reputation += $amount; update_user_meta($author_id, 'aksrt_reputation', $reputation); - if ($amount > 0 && class_exists('AKSRT_Credits')) { + if ($amount > 0) { $credits_amount = (int) ($config['credits']['score_extra'] ?? 3); if ($credits_amount > 0) { - $daily = $this->today_credits_count($author_id, 'forum_score_extra'); - $daily_max = (int) ($config['credits']['daily_max'] ?? 50); - if ($daily < $daily_max) { - AKSRT_Credits::add($author_id, $credits_amount, 'forum_score_extra', sprintf( - __('帖子「%s」被加分', 'aksrt'), - get_the_title($topic_id) - )); + if (class_exists('AKSRT_Points_Free')) { + AKSRT_Points_Free::grant($author_id, 'bbs_score_extra', $credits_amount, [ + 'type' => 'post', + 'id' => $topic_id, + ]); + } elseif (class_exists('AKSRT_Credits')) { + $daily = $this->today_credits_count($author_id, 'forum_score_extra'); + $daily_max = (int) ($config['credits']['daily_max'] ?? 50); + if ($daily < $daily_max) { + AKSRT_Credits::add($author_id, $credits_amount, 'forum_score_extra', sprintf( + __('帖子「%s」被加分', 'aksrt'), + get_the_title($topic_id) + )); + } } } } @@ -232,10 +239,17 @@ class AKSRT_Forum_Score_Service { update_post_meta($topic_id, '_aksrt_hot_rewarded', '1'); $config = $this->forum->get_config(); $amount = (int) ($config['credits']['posts_hot'] ?? 5); - if ($amount > 0 && class_exists('AKSRT_Credits')) { - AKSRT_Credits::add($author_id, $amount, 'forum_hot_topic', [ - 'type' => 'post', 'id' => $topic_id, - ]); + if ($amount > 0) { + if (class_exists('AKSRT_Points_Free')) { + AKSRT_Points_Free::grant($author_id, 'bbs_posts_hot', $amount, [ + 'type' => 'post', + 'id' => $topic_id, + ]); + } elseif (class_exists('AKSRT_Credits')) { + AKSRT_Credits::add($author_id, $amount, 'forum_hot_topic', [ + 'type' => 'post', 'id' => $topic_id, + ]); + } } $this->notify($author_id, 'hot', sprintf( __('你的帖子「%s」成为热帖!', 'aksrt'), get_the_title($topic_id) diff --git a/inc/classes/forum/topic-service-traits/trait-aksrt-forum-topic-service-part-1.php b/inc/classes/forum/topic-service-traits/trait-aksrt-forum-topic-service-part-1.php index 96c92fe2..29c4715b 100644 --- a/inc/classes/forum/topic-service-traits/trait-aksrt-forum-topic-service-part-1.php +++ b/inc/classes/forum/topic-service-traits/trait-aksrt-forum-topic-service-part-1.php @@ -245,22 +245,32 @@ trait AKSRT_Forum_Topic_Service_Part_1 { return; } - // 积分奖励(§7.7):读 `credits.essence`,带每日上限。 - if (class_exists('AKSRT_Credits') && class_exists('AKSRT_Settings')) { + // 积分奖励(§7.7):统一走免费积分日上限。 + if (class_exists('AKSRT_Settings')) { $cfg = AKSRT_Settings::get_forum_settings(); $amount = (int) ($cfg['credits']['essence'] ?? 0); if ($amount > 0) { - $daily_max = (int) ($cfg['credits']['daily_max'] ?? 50); - if ($daily_max > 0 && $this->today_credits_count($author_id, 'forum_essence') >= $daily_max) { - $amount = 0; - } - - if ($amount > 0) { - AKSRT_Credits::add($author_id, $amount, 'forum_essence', [ + $granted = 0; + if (class_exists('AKSRT_Points_Free')) { + $result = AKSRT_Points_Free::grant($author_id, 'bbs_essence', $amount, [ 'type' => 'post', - 'id' => $topic_id, - ], __('主题被设为精华', 'aksrt')); - + 'id' => $topic_id, + ]); + $granted = is_wp_error($result) ? 0 : (int) $result; + } elseif (class_exists('AKSRT_Credits')) { + $daily_max = (int) ($cfg['credits']['daily_max'] ?? 50); + if ($daily_max > 0 && $this->today_credits_count($author_id, 'forum_essence') >= $daily_max) { + $amount = 0; + } + if ($amount > 0) { + AKSRT_Credits::add($author_id, $amount, 'forum_essence', [ + 'type' => 'post', + 'id' => $topic_id, + ], __('主题被设为精华', 'aksrt')); + $granted = $amount; + } + } + if ($granted > 0) { update_post_meta($topic_id, '_aksrt_essence_rewarded', '1'); update_post_meta($topic_id, '_aksrt_essence_rewarded_at', current_time('mysql')); } diff --git a/inc/classes/widgets/class-aksrt-home-widgets.php b/inc/classes/widgets/class-aksrt-home-widgets.php index aaf55950..78a9e722 100644 --- a/inc/classes/widgets/class-aksrt-home-widgets.php +++ b/inc/classes/widgets/class-aksrt-home-widgets.php @@ -189,7 +189,7 @@ if (!function_exists('aksrt_home_widget_render_post_grid')) { */ function aksrt_home_widget_render_post_grid(WP_Query $query, string $layout = 'card', int $columns = 3, string $context = 'homepage'): void { if (!$query->have_posts()) { - echo '

' . esc_html__('暂无内容', 'aksrt') . '

'; + echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无内容', 'aksrt')) : '

' . esc_html__('暂无内容', 'aksrt') . '

'; wp_reset_postdata(); return; } @@ -464,7 +464,7 @@ class AKSRT_Category_Tabs_Widget extends WP_Widget { echo '
'; if (empty($tabs)) { - echo '

' . esc_html__('暂无可用分类', 'aksrt') . '

'; + echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无可用分类', 'aksrt')) : '

' . esc_html__('暂无可用分类', 'aksrt') . '

'; echo '
'; echo $args['after_widget']; return; @@ -649,7 +649,7 @@ class AKSRT_Category_Recommend_Widget extends WP_Widget { aksrt_home_widget_render_header($title); if (empty($cards)) { - echo '

' . esc_html__('暂无推荐分类', 'aksrt') . '

'; + echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无推荐分类', 'aksrt')) : '

' . esc_html__('暂无推荐分类', 'aksrt') . '

'; echo ''; echo $args['after_widget']; return; diff --git a/inc/helpers/aksrt-comments.php b/inc/helpers/aksrt-comments.php index de3de7eb..cb94a64c 100644 --- a/inc/helpers/aksrt-comments.php +++ b/inc/helpers/aksrt-comments.php @@ -571,12 +571,18 @@ if (!function_exists('aksrt_comments_render_empty')) : * @param array $ctx Comments context. */ function aksrt_comments_render_empty(array $ctx): void { + $text = (string) ($ctx['empty_text'] ?? __('暂无评论内容', 'aksrt')); + if (function_exists('aksrt_get_null')) { + echo aksrt_get_null($text, 40, 'null.svg', 'comment comment-null comments-empty', 220); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo '

' . esc_html__('成为第一个评论者吧', 'aksrt') . '

'; + return; + } ?>
-

+

__('论坛发帖', 'aksrt'), 'free_bbs_reply'=> __('论坛回复', 'aksrt'), 'free_bbs_essence'=> __('帖子加精', 'aksrt'), + 'free_bbs_adopt'=> __('回复被采纳', 'aksrt'), + 'free_bbs_posts_hot'=> __('热帖奖励', 'aksrt'), + 'free_bbs_comment_hot'=> __('神评奖励', 'aksrt'), + 'free_bbs_score_extra'=> __('帖子加分', 'aksrt'), ]; if (isset($map[$reason])) { return $map[$reason]; diff --git a/inc/helpers/aksrt-frontend-tags.php b/inc/helpers/aksrt-frontend-tags.php index 1a3e94d1..065211d3 100644 --- a/inc/helpers/aksrt-frontend-tags.php +++ b/inc/helpers/aksrt-frontend-tags.php @@ -1055,9 +1055,18 @@ if (!function_exists('aksrt_comment_callback')) :
'avatar-img comt-avatar']); ?> + user_id; + if ($comment_user_id > 0 && function_exists('aksrt_render_user_identity_chips')) { + echo aksrt_render_user_identity_chips($comment_user_id, 'comment'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + ?> + + + diff --git a/inc/helpers/aksrt-identity.php b/inc/helpers/aksrt-identity.php new file mode 100644 index 00000000..3c9ac91c --- /dev/null +++ b/inc/helpers/aksrt-identity.php @@ -0,0 +1,103 @@ + + */ + function aksrt_get_user_identity_signals(int $user_id): array { + if ($user_id <= 0) { + return []; + } + if (class_exists('AKSRT_User_Home')) { + $signals = AKSRT_User_Home::get_instance()->get_profile_signals($user_id); + return is_array($signals) ? array_values($signals) : []; + } + return []; + } +} + +if (!function_exists('aksrt_render_user_identity_chips')) { + /** + * Render compact identity chips (verify / level / badges). + * + * @param int $user_id + * @param string $context author|comment|card + */ + function aksrt_render_user_identity_chips(int $user_id, string $context = 'author'): string { + if ($user_id <= 0) { + return ''; + } + $html = ''; + if (function_exists('aksrt_render_vip_badge')) { + $vip = aksrt_render_vip_badge($user_id, 'sm'); + if (is_string($vip) && $vip !== '') { + $html .= $vip; + } + } + $signals = aksrt_get_user_identity_signals($user_id); + $limit = $context === 'comment' ? 3 : 6; + $n = 0; + foreach ($signals as $signal) { + if ($n >= $limit) { + break; + } + $label = trim((string) ($signal['label'] ?? '')); + if ($label === '') { + continue; + } + $tone = sanitize_html_class((string) ($signal['tone'] ?? 'info')); + $icon = sanitize_html_class((string) ($signal['icon'] ?? 'check')); + // Font Awesome: map short names to fa-* if needed. + if ($icon !== '' && !str_contains($icon, 'fa-')) { + $icon = 'fa-' . $icon; + } + $title = (string) ($signal['title'] ?? $label); + $html .= ''; + if ($icon !== '') { + $html .= ' '; + } + $html .= esc_html($label) . ''; + $n++; + } + return $html; + } +} + +if (!function_exists('aksrt_is_blog_comment_hot')) { + /** + * "神评" for ordinary post comments when likes cross threshold. + */ + function aksrt_is_blog_comment_hot($comment): bool { + $comment_id = $comment instanceof WP_Comment ? (int) $comment->comment_ID : (int) $comment; + if ($comment_id <= 0) { + return false; + } + if ('1' === (string) get_comment_meta($comment_id, '_aksrt_comment_hot', true)) { + return true; + } + $threshold = 10; + if (class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'get_comment_display_settings')) { + $display = AKSRT_Settings::get_comment_display_settings(); + if (isset($display['hot_threshold'])) { + $threshold = max(1, (int) $display['hot_threshold']); + } + } + $all = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_all() : []; + if (isset($all['comment_hot_threshold'])) { + $threshold = max(1, (int) $all['comment_hot_threshold']); + } + $likes = function_exists('aksrt_get_comment_likes') ? aksrt_get_comment_likes($comment_id) : (int) get_comment_meta($comment_id, 'aksrt_comment_likes', true); + if ($likes < $threshold) { + return false; + } + update_comment_meta($comment_id, '_aksrt_comment_hot', '1'); + return true; + } +} diff --git a/inc/helpers/aksrt-null.php b/inc/helpers/aksrt-null.php new file mode 100644 index 00000000..60d67a07 --- /dev/null +++ b/inc/helpers/aksrt-null.php @@ -0,0 +1,78 @@ +' . esc_html((string) $text) . '

' + : ''; + if ($height > 0) { + $style = $width > 0 ? 'max-width:' . (int) $width . 'px;' : ''; + $style .= 'height:' . (int) $height . 'px;'; + } else { + $style = $width > 0 ? 'width:' . (int) $width . 'px;' : ''; + } + $style .= 'opacity:.7;'; + $src = aksrt_null_image_url($img); + return '
' + . '' + . $caption + . '
'; + } +} + +if (!function_exists('aksrt_get_widget_null')) { + /** + * Compact null for sidebar widgets. + */ + function aksrt_get_widget_null(string $text = '', int $margin = 24, int $width = 140): string { + if ($text === '') { + $text = __('暂无内容', 'aksrt'); + } + return aksrt_get_null($text, $margin, 'null.svg', 'widget-empty aksrt-null--widget', $width); + } +} diff --git a/inc/helpers/aksrt-single-render.php b/inc/helpers/aksrt-single-render.php index 3de983cf..8a57a170 100644 --- a/inc/helpers/aksrt-single-render.php +++ b/inc/helpers/aksrt-single-render.php @@ -236,6 +236,7 @@ if (!function_exists('aksrt_single_render_actions')) : +
@@ -305,11 +306,9 @@ if (!function_exists('aksrt_single_render_author_card')) : $author_url = get_author_posts_url($author_id); $author_name = get_the_author(); $bio = trim((string) get_the_author_meta('description')); - $vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($author_id, 'sm') : ''; - $signals = []; - if (class_exists('AKSRT_User_Home')) { - $signals = AKSRT_User_Home::get_instance()->get_profile_signals($author_id); - } + $identity_chips = function_exists('aksrt_render_user_identity_chips') + ? aksrt_render_user_identity_chips($author_id, 'card') + : ''; $avatar = function_exists('aksrt_get_avatar_html') ? aksrt_get_avatar_html($author_id, 80, ['class' => 'avatar avatar-lg']) : get_avatar($author_id, 80, '', esc_attr($author_name), ['class' => 'avatar avatar-lg']); @@ -330,13 +329,9 @@ if (!function_exists('aksrt_single_render_author_card')) :
- - - - - - - + + +
@@ -554,6 +549,17 @@ if (!function_exists('aksrt_single_render_related')) : $query = new WP_Query($args); if (!$query->have_posts()) { wp_reset_postdata(); + $empty_wrap = 'theme-box relates' . ($style === 'img' ? ' relates-thumb' : ''); + ?> +
+
+
+
+
+ +
+
+ get_profile_signals($author_id); -} +// Identity chips: VIP + verify/level/badges (shared helper). +$identity_chips = function_exists('aksrt_render_user_identity_chips') + ? aksrt_render_user_identity_chips($author_id, 'author') + : ''; ?>
@@ -71,15 +69,9 @@ if (class_exists('AKSRT_User_Home')) {
- - + + - - - - - -
@@ -100,10 +92,24 @@ if (class_exists('AKSRT_User_Home')) { - + + + + + +