feat: 重构评论点赞系统,支持取消点赞

- 改用 IP + User-Agent 哈希识别用户,登录用户使用 user_id
- 服务端存储点赞用户列表,而非简单计数
- 同一用户只能点赞一次,再次点击可取消点赞
- 移除 Cookie 依赖,避免 IP 变化导致重复点赞
- 已点赞按钮 hover 时显示可点击状态
This commit is contained in:
2026-01-16 22:24:34 +08:00
parent d9aafe2479
commit 25fd3891a3
3 changed files with 183 additions and 64 deletions

View File

@@ -867,6 +867,22 @@ function argon_debug_console_script() {
}
add_action('wp_footer', 'argon_debug_console_script', 999);
// 确保 jQuery easing 函数在所有脚本加载后仍可用
function argon_ensure_jquery_easing() {
?>
<script>
(function(){
if(typeof jQuery==='undefined')return;
var $=jQuery;
if(!$.easing)$.easing={};
if(!$.easing.easeOutCirc)$.easing.easeOutCirc=function(x){return Math.sqrt(1-Math.pow(x-1,2));};
if(!$.easing.easeOutExpo)$.easing.easeOutExpo=function(x){return x===1?1:1-Math.pow(2,-10*x);};
})();
</script>
<?php
}
add_action('wp_footer', 'argon_ensure_jquery_easing', 9999);
//初次使用时发送安装量统计信息 (数据仅用于统计安装量)
function post_analytics_info(){
if(function_exists('file_get_contents')){
@@ -1728,68 +1744,161 @@ function get_comment_upvotes($id) {
if ($comment == null){
return 0;
}
$upvotes = get_comment_meta($comment -> comment_ID, "upvotes", true);
$upvotes = get_comment_meta($comment->comment_ID, 'upvotes', true);
if ($upvotes == null) {
$upvotes = 0;
}
return $upvotes;
}
function set_comment_upvotes($id){
/**
* 获取用户唯一标识(基于 IP + User-Agent 的哈希)
* 用于识别同一用户,防止重复点赞
*/
function get_user_upvote_identifier() {
$ip = '';
// 获取真实 IP考虑代理情况
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// 可能有多个 IP取第一个
$ip_list = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$ip = trim($ip_list[0]);
} elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$ip = $_SERVER['HTTP_X_REAL_IP'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
// 结合 User-Agent 生成设备指纹
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
// 如果用户已登录,使用用户 ID 作为主要标识
if (is_user_logged_in()) {
return 'user_' . get_current_user_id();
}
// 未登录用户使用 IP + UA 哈希
// 使用 sha256 生成较短的哈希,取前 16 位
return substr(hash('sha256', $ip . '|' . $user_agent), 0, 16);
}
/**
* 获取评论的点赞用户列表
*/
function get_comment_upvote_users($id) {
$comment = get_comment($id);
if ($comment == null){
return 0;
return [];
}
$upvotes = get_comment_meta($comment -> comment_ID, "upvotes", true);
if ($upvotes == null) {
$upvotes = 0;
$users = get_comment_meta($comment->comment_ID, 'upvote_users', true);
if (empty($users) || !is_array($users)) {
return [];
}
$upvotes++;
update_comment_meta($comment -> comment_ID, "upvotes", $upvotes);
return $upvotes;
return $users;
}
function is_comment_upvoted($id){
$upvotedList = isset( $_COOKIE['argon_comment_upvoted'] ) ? $_COOKIE['argon_comment_upvoted'] : '';
if (in_array($id, explode(',', $upvotedList))){
return true;
}
return false;
/**
* 检查当前用户是否已点赞该评论
*/
function is_comment_upvoted($id) {
$identifier = get_user_upvote_identifier();
$users = get_comment_upvote_users($id);
return in_array($identifier, $users);
}
/**
* 添加点赞
*/
function add_comment_upvote($id) {
$comment = get_comment($id);
if ($comment == null){
return false;
}
$identifier = get_user_upvote_identifier();
$users = get_comment_upvote_users($id);
if (in_array($identifier, $users)) {
return false; // 已点赞
}
$users[] = $identifier;
update_comment_meta($comment->comment_ID, 'upvote_users', $users);
update_comment_meta($comment->comment_ID, 'upvotes', count($users));
return true;
}
/**
* 取消点赞
*/
function remove_comment_upvote($id) {
$comment = get_comment($id);
if ($comment == null){
return false;
}
$identifier = get_user_upvote_identifier();
$users = get_comment_upvote_users($id);
$key = array_search($identifier, $users);
if ($key === false) {
return false; // 未点赞
}
unset($users[$key]);
$users = array_values($users); // 重新索引
update_comment_meta($comment->comment_ID, 'upvote_users', $users);
update_comment_meta($comment->comment_ID, 'upvotes', count($users));
return true;
}
/**
* 处理评论点赞/取消点赞请求
*/
function upvote_comment(){
if (get_option("argon_enable_comment_upvote", "false") != "true"){
if (get_option('argon_enable_comment_upvote', 'false') != 'true'){
return;
}
header('Content-Type:application/json; charset=utf-8');
$ID = $_POST["comment_id"];
$ID = isset($_POST['comment_id']) ? intval($_POST['comment_id']) : 0;
$comment = get_comment($ID);
if ($comment == null){
exit(json_encode(array(
exit(json_encode([
'status' => 'failed',
'msg' => __('评论不存在', 'argon'),
'total_upvote' => 0
)));
'total_upvote' => 0,
'upvoted' => false
]));
}
$upvotedList = isset( $_COOKIE['argon_comment_upvoted'] ) ? $_COOKIE['argon_comment_upvoted'] : '';
if (in_array($ID, explode(',', $upvotedList))){
exit(json_encode(array(
'status' => 'failed',
'msg' => __('该评论已被赞过', 'argon'),
'total_upvote' => get_comment_upvotes($ID)
)));
$is_upvoted = is_comment_upvoted($ID);
if ($is_upvoted) {
// 已点赞,执行取消点赞
remove_comment_upvote($ID);
exit(json_encode([
'ID' => $ID,
'status' => 'success',
'action' => 'removed',
'msg' => __('已取消点赞', 'argon'),
'total_upvote' => format_number_in_kilos(get_comment_upvotes($ID)),
'upvoted' => false
]));
} else {
// 未点赞,执行点赞
add_comment_upvote($ID);
exit(json_encode([
'ID' => $ID,
'status' => 'success',
'action' => 'added',
'msg' => __('点赞成功', 'argon'),
'total_upvote' => format_number_in_kilos(get_comment_upvotes($ID)),
'upvoted' => true
]));
}
set_comment_upvotes($ID);
setcookie('argon_comment_upvoted', $upvotedList . $ID . "," , array(
'expires' => time() + 3153600000,
'path' => '/',
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax'
));
exit(json_encode(array(
'ID' => $ID,
'status' => 'success',
'msg' => __('点赞成功', 'argon'),
'total_upvote' => format_number_in_kilos(get_comment_upvotes($ID))
)));
}
add_action('wp_ajax_upvote_comment' , 'upvote_comment');
add_action('wp_ajax_nopriv_upvote_comment' , 'upvote_comment');