基于JQuery、SQL和PHP的点赞点踩系统:单用户单次操作实现方案
实现单用户单帖仅能点赞/点踩一次的解决方案
核心思路
要解决重复点赞/点踩的问题,关键是记录用户对每个帖子的投票行为,通过数据库约束和业务逻辑确保同一用户对同一帖子只能产生一次有效投票。需要从数据库设计、后端逻辑、前端交互三个层面修改。
1. 新增投票记录数据库表
首先创建post_votes表存储用户投票记录,通过唯一索引阻止重复投票:
CREATE TABLE post_votes ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, -- 当前登录用户ID(若支持匿名,可替换为session_id) post_id INT NOT NULL, -- 对应problems表的pro_id vote_type ENUM('like', 'dislike') NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 唯一索引:同一用户对同一帖子只能投一次票 UNIQUE KEY unique_user_post_vote (user_id, post_id) );
注:若系统允许匿名投票,用
session_id替代user_id,但无法完全阻止用户清空session后重复投票,建议优先要求用户登录后操作。
2. 修改后端PHP逻辑(setrw3.php)
原代码直接更新计数未做权限校验,现在需先检查用户是否已投票,再执行更新:
session_start(); // 验证用户登录状态 if (!isset($_SESSION['user_id'])) { echo json_encode(['status' => 'error', 'msg' => '请先登录']); exit; } $user_id = $_SESSION['user_id']; $type = $_POST['type']; $id = $_POST['pro_id']; // 检查用户是否已对该帖子投过票 $check_sql = "SELECT vote_type FROM post_votes WHERE user_id = ? AND post_id = ?"; $stmt = mysqli_prepare($con, $check_sql); mysqli_stmt_bind_param($stmt, "ii", $user_id, $id); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); $existing_vote = mysqli_fetch_assoc($result); if ($existing_vote) { echo json_encode(['status' => 'error', 'msg' => '你已经投过票了']); exit; } // 执行计数更新并记录投票 $update_sql = ""; if ($type == 'like') { $update_sql = "UPDATE problems SET right_count = right_count + 1 WHERE pro_id = ?"; } elseif ($type == 'dislike') { $update_sql = "UPDATE problems SET wrong_count = wrong_count + 1 WHERE pro_id = ?"; } if ($update_sql) { // 事务保证计数更新与投票记录插入一致性 mysqli_begin_transaction($con); try { // 更新帖子计数 $stmt = mysqli_prepare($con, $update_sql); mysqli_stmt_bind_param($stmt, "i", $id); mysqli_stmt_execute($stmt); // 插入投票记录 $insert_sql = "INSERT INTO post_votes (user_id, post_id, vote_type) VALUES (?, ?, ?)"; $stmt = mysqli_prepare($con, $insert_sql); mysqli_stmt_bind_param($stmt, "iis", $user_id, $id, $type); mysqli_stmt_execute($stmt); mysqli_commit($con); // 返回更新后的计数 $count_sql = $type == 'like' ? "SELECT right_count FROM problems WHERE pro_id = ?" : "SELECT wrong_count FROM problems WHERE pro_id = ?"; $stmt = mysqli_prepare($con, $count_sql); mysqli_stmt_bind_param($stmt, "i", $id); mysqli_stmt_execute($stmt); $count_result = mysqli_fetch_assoc(mysqli_stmt_get_result($stmt)); $new_count = $type == 'like' ? $count_result['right_count'] : $count_result['wrong_count']; echo json_encode(['status' => 'success', 'count' => $new_count]); } catch (Exception $e) { mysqli_rollback($con); echo json_encode(['status' => 'error', 'msg' => '投票失败']); } } else { echo json_encode(['status' => 'error', 'msg' => '无效的投票类型']); }
代码使用预处理语句防止SQL注入,事务确保数据一致性。
3. 修改前端JQuery代码
原前端未处理重复点击和后端返回状态,现在优化交互逻辑:
// 点赞按钮事件 $(document).on("click", "#usrcmmntyes", function (e) { e.preventDefault(); const $btn = $(this); const postId = $btn.data("pro-id"); // 用data属性存储帖子ID,替代onclick // 禁用按钮防止重复点击 $btn.prop("disabled", true).addClass("voted"); like_update(postId, $btn); }); function like_update(id, $btn){ jQuery.ajax({ url:'setrw3.php', type:'post', data: {type: 'like', pro_id: id}, dataType: 'json', success:function(response){ if(response.status === 'success'){ jQuery('.like_loop_'+id).html(response.count); $btn.text("已点赞"); // 修改按钮状态提示 } else { alert(response.msg); $btn.prop("disabled", false).removeClass("voted"); // 恢复按钮 } }, error:function(){ alert("网络错误,请重试"); $btn.prop("disabled", false).removeClass("voted"); } }); } // 点踩按钮事件 $(document).on("click", "#usrcmmntno", function (e) { e.preventDefault(); const $btn = $(this); const postId = $btn.data("pro-id"); $btn.prop("disabled", true).addClass("voted"); dislike_update(postId, $btn); }); function dislike_update(id, $btn){ jQuery.ajax({ url:'setrw3.php', type:'post', data: {type: 'dislike', pro_id: id}, dataType: 'json', success:function(response){ if(response.status === 'success'){ jQuery('.dislike_loop_'+id).html(response.count); $btn.text("已点踩"); } else { alert(response.msg); $btn.prop("disabled", false).removeClass("voted"); } }, error:function(){ alert("网络错误,请重试"); $btn.prop("disabled", false).removeClass("voted"); } }); }
需同步修改按钮HTML,添加
data-pro-id属性:<button id="usrcmmntyes" data-pro-id="1">点赞</button>
移除原onclick属性,用事件绑定更规范。
额外优化建议
- 页面加载时,查询用户已有投票记录,提前设置按钮状态(如已点赞按钮变灰),避免用户产生操作误解。
- 若需支持取消投票,可扩展逻辑:允许用户删除投票记录并回退帖子计数,或修改投票类型(如从点赞改点踩)。
内容的提问来源于stack exchange,提问作者BHAVYA OBEROI
相关产品推荐
相关产品推荐

