feat: 收口 1:1 剩余欠账(论坛壳/圈子/501/私信默认/资源死码)

- 论坛 board/topic/plate/tag/plate-cat 接入 content-wrap/content-layout 主站流壳
- 公共主页 plate 圈子 tab 接关注/管理版块真实数据并计数
- forum-topic 渲染封面/付费扩展位 meta(§7.3)
- 后台删除消息/背景图/评论图 REST 从 501 补齐为真实实现
- 私信 private_s 缺省 true(完整 1:1,不再兼容默认关)
- 清理 aksrt_get_resource_meta 不可达旧实现;挂载 AKSRT_AI 配置门控实例
- 登录 modal 启用 colorful-header;订单页粉边 focus 软化;侧栏次级入口视觉弱化

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-07-12 13:06:47 +08:00
1 parent 0941c7d23f
commit fb68b3f8c6
16 files changed
+313 -82

No files matched your search

@@ -577,7 +577,26 @@ class AKSRT_Admin_User_Controller extends AKSRT_Admin_Controller_Base {
}
public function rest_delete_notification_item(WP_REST_Request $request): WP_REST_Response {
return $this->success(['message' => __('当前版本暂未提供后台删除单条消息能力', 'aksrt')]);
$user_id = get_current_user_id();
if ($user_id <= 0) {
return $this->error(__('请先登录', 'aksrt'), 401);
}
$params = $this->get_json_params($request);
$id = sanitize_text_field((string) ($params['id'] ?? $request->get_param('id') ?? ''));
if ($id === '') {
return $this->error(__('参数错误', 'aksrt'), 400);
}
if (function_exists('aksrt_delete_user_notification')) {
aksrt_delete_user_notification($user_id, $id);
} elseif (class_exists('AKSRT_Message')) {
AKSRT_Message::get_instance()->mark_read($user_id, $id);
}
return $this->success([
'message' => __('消息已删除', 'aksrt'),
'unread' => function_exists('aksrt_get_user_unread_notification_count')
? aksrt_get_user_unread_notification_count($user_id)
: 0,
]);
}
public function rest_run_notification_cleanup(WP_REST_Request $request): WP_REST_Response {
@@ -638,11 +657,65 @@ class AKSRT_Admin_User_Controller extends AKSRT_Admin_Controller_Base {
}
public function rest_upload_user_background(WP_REST_Request $request): WP_REST_Response {
return $this->error(__('当前版本暂未提供后台 REST 背景图上传,请使用标准媒体上传流程。', 'aksrt'), 501);
$user_id = get_current_user_id();
if ($user_id <= 0) {
return $this->error(__('请先登录', 'aksrt'), 401);
}
if (empty($_FILES['background']) && empty($_FILES['file'])) {
return $this->error(__('请选择要上传的图片', 'aksrt'), 400);
}
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
$file_key = !empty($_FILES['background']) ? 'background' : 'file';
$attachment_id = media_handle_upload($file_key, 0);
if (is_wp_error($attachment_id)) {
return $this->error($attachment_id->get_error_message(), 400);
}
$url = wp_get_attachment_url((int) $attachment_id);
if (!$url) {
return $this->error(__('上传失败', 'aksrt'), 500);
}
update_user_meta($user_id, 'aksrt_background_image', esc_url_raw($url));
update_user_meta($user_id, 'aksrt_background_attachment_id', (int) $attachment_id);
return $this->success([
'message' => __('背景图已更新', 'aksrt'),
'data' => [
'url' => $url,
'attachment_id' => (int) $attachment_id,
],
]);
}
public function rest_upload_comment_image(WP_REST_Request $request): WP_REST_Response {
return $this->error(__('当前版本暂未提供后台 REST 评论图片上传,请使用标准媒体上传流程。', 'aksrt'), 501);
$user_id = get_current_user_id();
if ($user_id <= 0) {
return $this->error(__('请先登录', 'aksrt'), 401);
}
if (empty($_FILES['image']) && empty($_FILES['file'])) {
return $this->error(__('请选择要上传的图片', 'aksrt'), 400);
}
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
$file_key = !empty($_FILES['image']) ? 'image' : 'file';
$attachment_id = media_handle_upload($file_key, 0);
if (is_wp_error($attachment_id)) {
return $this->error($attachment_id->get_error_message(), 400);
}
$url = wp_get_attachment_url((int) $attachment_id);
if (!$url) {
return $this->error(__('上传失败', 'aksrt'), 500);
}
return $this->success([
'message' => __('图片上传成功', 'aksrt'),
'data' => [
'url' => $url,
'attachment_id' => (int) $attachment_id,
],
]);
}
public function rest_get_credits_me(WP_REST_Request $request): WP_REST_Response {
@@ -401,7 +401,7 @@ class AKSRT_Settings_Integration {
$quick_lines = array_filter(array_map('trim', preg_split('/\r\n|\r|\n/', $quick_often)));
return [
'enabled' => !empty($settings['private_s']),
'enabled' => !array_key_exists('private_s', $settings) || !empty($settings['private_s']),
'smilie' => !empty($settings['private_smilie_s']),
'code' => !empty($settings['private_code_s']),
'image' => !empty($settings['private_image_s']),
+2 -2
View File
@@ -278,14 +278,14 @@ function aksrt_register_forum_topic_meta() {
// 收藏 (Wave3)
'_aksrt_favorite_count' => ['type' => 'integer', 'default' => 0],
// 封面扩展位 (§7.3 待实现)
// 封面扩展位(论坛帖子封面;前台 forum-topic 渲染)
'_aksrt_cover_type' => ['type' => 'string', 'default' => ''],
'_aksrt_cover_image' => ['type' => 'string', 'default' => ''],
'_aksrt_featured_slide' => ['type' => 'array', 'default' => []],
'_aksrt_featured_video' => ['type' => 'string', 'default' => ''],
'_aksrt_cover_ratio' => ['type' => 'string', 'default' => '16:9'],
// 付费内容扩展位 (§7.3 待实现)
// 付费内容扩展位(论坛帖子付费门槛;前台 forum-topic 渲染)
'_aksrt_allow_view' => ['type' => 'string', 'default' => 'public'],
'_aksrt_pay_price' => ['type' => 'number', 'default' => 0],
'_aksrt_pay_vip_price' => ['type' => 'number', 'default' => 0],
+19 -54
View File
@@ -36,37 +36,12 @@ if (!function_exists('aksrt_get_resource_meta')) {
$resource_view_count = (int) get_post_meta($p->ID, AKSRT_Domain_Model::RESOURCE_VIEW_COUNT_KEY, true);
$common_view_count = (int) get_post_meta($p->ID, AKSRT_Domain_Model::POST_VIEW_COUNT_KEY, true);
return [
'version' => (string) ($pay_meta['version'] ?? ''),
'size' => (string) ($pay_meta['size'] ?? ''),
'price' => (float) ($pay_meta['price'] ?? 0),
'original_price' => (float) ($pay_meta['original_price'] ?? 0),
'vip1_price' => (float) ($pay_meta['vip1_price'] ?? 0),
'vip2_price' => (float) ($pay_meta['vip2_price'] ?? 0),
'pay_type' => (string) ($pay_meta['pay_type'] ?? AKSRT_Domain_Model::PAY_TYPE_DOWNLOAD),
'license' => (string) ($pay_meta['license'] ?? ''),
'compat' => (string) ($pay_meta['compat'] ?? ''),
'demo_url' => (string) ($pay_meta['demo_url'] ?? ''),
'downloads' => array_values(is_array($pay_meta['downloads'] ?? null) ? $pay_meta['downloads'] : []),
'download_count' => max(0, max($download_count, $legacy_download_count)),
'view_count' => max(0, max($resource_view_count, $common_view_count)),
'require_login' => !empty($pay_meta['require_login']),
'require_captcha' => !empty($pay_meta['require_captcha']),
];
// 一次取出全部 post meta(WP 已缓存到 object cache)
$all = get_post_meta($p->ID);
if (!is_array($all)) $all = [];
$get = static function ($key) use ($all) {
if (!isset($all[$key]) || !is_array($all[$key]) || empty($all[$key])) return null;
return maybe_unserialize($all[$key][0]);
};
$downloads = $get('_aksrt_res_downloads');
if (!is_array($downloads)) $downloads = [];
// Normalize downloads extract_code aliases for legacy rows still stored on pay meta.
$downloads = array_values(is_array($pay_meta['downloads'] ?? null) ? $pay_meta['downloads'] : []);
$downloads = array_values(array_filter(array_map(static function ($row) {
if (!is_array($row)) return null;
if (!is_array($row)) {
return null;
}
$extract_keys = [
'extract_code', 'extractCode', 'extract', 'extract_key',
'extraction_code', 'extractionCode',
@@ -85,33 +60,23 @@ if (!function_exists('aksrt_get_resource_meta')) {
$row['extract_code'] = isset($row['extract_code']) ? trim((string) $row['extract_code']) : '';
return $row;
}, $downloads)));
$resource_download_count = (int) ($get('_aksrt_res_download_count') ?? 0);
$legacy_download_count = (int) ($get('download_count') ?? 0);
$resource_view_count = (int) ($get('_aksrt_res_view_count') ?? 0);
$common_view_count = (int) ($get('aksrt_post_views') ?? 0);
$allowed_pay_types = ['download', 'reading', 'gallery', 'video', 'vip'];
$pay_type = (string) ($get('_aksrt_res_pay_type') ?? 'download');
if (!in_array($pay_type, $allowed_pay_types, true)) {
$pay_type = 'download';
}
return [
'version' => (string) ($get('_aksrt_res_version') ?? ''),
'size' => (string) ($get('_aksrt_res_size') ?? ''),
'price' => (float) ($get('_aksrt_res_price') ?? 0),
'original_price' => (float) ($get('_aksrt_res_original_price') ?? 0),
'vip1_price' => (float) ($get('_aksrt_res_vip1_price') ?? 0),
'vip2_price' => (float) ($get('_aksrt_res_vip2_price') ?? 0),
'pay_type' => $pay_type,
'license' => (string) ($get('_aksrt_res_license') ?? ''),
'compat' => (string) ($get('_aksrt_res_compat') ?? ''),
'demo_url' => (string) ($get('_aksrt_res_demo_url') ?? ''),
'downloads' => array_values($downloads),
'download_count' => max(0, max($resource_download_count, $legacy_download_count)),
'version' => (string) ($pay_meta['version'] ?? ''),
'size' => (string) ($pay_meta['size'] ?? ''),
'price' => (float) ($pay_meta['price'] ?? 0),
'original_price' => (float) ($pay_meta['original_price'] ?? 0),
'vip1_price' => (float) ($pay_meta['vip1_price'] ?? 0),
'vip2_price' => (float) ($pay_meta['vip2_price'] ?? 0),
'pay_type' => (string) ($pay_meta['pay_type'] ?? AKSRT_Domain_Model::PAY_TYPE_DOWNLOAD),
'license' => (string) ($pay_meta['license'] ?? ''),
'compat' => (string) ($pay_meta['compat'] ?? ''),
'demo_url' => (string) ($pay_meta['demo_url'] ?? ''),
'downloads' => $downloads,
'download_count' => max(0, max($download_count, $legacy_download_count)),
'view_count' => max(0, max($resource_view_count, $common_view_count)),
'require_login' => (bool) ($get('_aksrt_res_require_login') ?? false),
'require_captcha' => (bool) ($get('_aksrt_res_require_captcha') ?? false),
'require_login' => !empty($pay_meta['require_login']),
'require_captcha' => !empty($pay_meta['require_captcha']),
];
}
}
+26 -1
View File
@@ -92,13 +92,38 @@ if (!function_exists('aksrt_get_user_home_page_context')) {
$follow_count = (int) AKSRT_Follow::get_instance()->get_following_count($user_id);
}
$plate_count = 0;
if ($forum_instance) {
$seen_plates = [];
if (method_exists($forum_instance, 'get_user_followed_plates')) {
foreach ((array) $forum_instance->get_user_followed_plates($user_id) as $plate) {
$pid = (int) ($plate['id'] ?? 0);
if ($pid > 0) {
$seen_plates[$pid] = true;
}
}
}
if (method_exists($forum_instance, 'get_user_managed_plates')) {
$managed = (array) $forum_instance->get_user_managed_plates($user_id);
foreach (['owned', 'moderating'] as $bucket) {
foreach ((array) ($managed[$bucket] ?? []) as $plate) {
$pid = (int) ($plate['id'] ?? 0);
if ($pid > 0) {
$seen_plates[$pid] = true;
}
}
}
}
$plate_count = count($seen_plates);
}
$tab_counts = [
'posts' => $post_count,
'comments' => $comment_count,
'favorites' => $favorite_count,
'credits' => $credits_enabled && $is_own_profile ? (int) round((float) $credit_balance) : 0,
'product' => $product_count,
'plate' => 0,
'plate' => $plate_count,
'forum' => $forum_topic_count,
'follow' => $follow_count,
];