Laravel后台用户评论内容重复但ID和日期不同问题求助
你的代码目前仅通过review_submitted_at与数据库中最后一条评论的日期对比来决定是否插入,但App Store Connect API中,用户编辑评论可能会生成新的customerReview条目(新的review_id和createdDate);如果编辑后的评论内容与原评论一致,就会导致数据库中出现内容相同但ID、日期不同的重复条目。另外,仅依赖日期判断也可能因为API分页逻辑(比如单次返回的200条中包含了之前未抓取到的旧评论,但日期仍晚于某次的lastDate)引发重复。
1. 数据库层面添加唯一约束(底层防护)
在reviews表中,给app_id和review_id添加组合唯一索引,确保同一个App下的同一个review_id(App Store返回的评论唯一标识)不会被重复插入:
ALTER TABLE reviews ADD UNIQUE INDEX idx_app_review_id (app_id, review_id);
这是最可靠的底层防护,即使代码逻辑有疏漏,数据库也会直接拒绝重复插入操作。
2. 修改代码逻辑,基于review_id判断是否已存在
替换当前仅靠日期判断的逻辑,改为先检查review_id是否已存在于数据库中:
public function storeReviewsInDB($appId, $reviews) { $customerReviews = $reviews->data ?? []; $app = DB::select('SELECT id, name FROM apps WHERE app_store_id = ?', [$appId]); $appId = $app[0]->id; // 批量获取已存在的review_id,减少数据库查询次数 $existingReviewIds = DB::table('reviews') ->where('app_id', $appId) ->pluck('review_id') ->toArray(); $insertData = []; foreach ($customerReviews as $customerReview) { $reviewId = $customerReview->id; // 先判断当前review_id是否已存在 if (!in_array($reviewId, $existingReviewIds)) { $reviewSubmittedAt = gmdate('Y-m-d H:i:s', strtotime($customerReview->attributes->createdDate)); $responseId = $customerReview->relationships->response->data->id ?? null; $insertData[] = [ 'app_id' => $appId, 'review_id' => $reviewId, 'author_name' => $customerReview->attributes->reviewerNickname, 'title' => $customerReview->attributes->title, 'body' => $customerReview->attributes->body, 'rating' => $customerReview->attributes->rating, 'country' => $customerReview->attributes->territory, 'review_submitted_at' => $reviewSubmittedAt, 'response_id' => $responseId ]; // 将已处理的review_id加入列表,避免循环内重复判断 $existingReviewIds[] = $reviewId; } } if (!empty($insertData)) { DB::table('reviews')->insert($insertData); } }
3. 使用Laravel的upsert方法(支持评论更新)
如果希望在用户编辑评论时,能同步更新数据库中的内容(而不是只插入新评论),可以使用Laravel的upsert方法,它会自动判断记录是否存在:存在则更新,不存在则插入:
public function storeReviewsInDB($appId, $reviews) { $customerReviews = $reviews->data ?? []; $app = DB::select('SELECT id, name FROM apps WHERE app_store_id = ?', [$appId]); $appId = $app[0]->id; $upsertData = []; foreach ($customerReviews as $customerReview) { $reviewSubmittedAt = gmdate('Y-m-d H:i:s', strtotime($customerReview->attributes->createdDate)); $responseId = $customerReview->relationships->response->data->id ?? null; $upsertData[] = [ 'app_id' => $appId, 'review_id' => $customerReview->id, 'author_name' => $customerReview->attributes->reviewerNickname, 'title' => $customerReview->attributes->title, 'body' => $customerReview->attributes->body, 'rating' => $customerReview->attributes->rating, 'country' => $customerReview->attributes->territory, 'review_submitted_at' => $reviewSubmittedAt, 'response_id' => $responseId ]; } if (!empty($upsertData)) { DB::table('reviews')->upsert( $upsertData, ['app_id', 'review_id'], // 唯一标识字段(需对应数据库的唯一索引) ['author_name', 'title', 'body', 'rating', 'country', 'review_submitted_at', 'response_id'] // 需要更新的字段 ); } }
使用upsert必须确保数据库中已经添加了app_id和review_id的唯一索引,否则会报错。
额外优化:处理API分页
当前API请求仅使用limit=200和排序,没有处理分页逻辑,当评论总数超过200条时,可能会漏掉旧评论或者重复抓取。App Store Connect API支持通过cursor参数分页,你可以从API响应的links.next中获取下一页的URL,循环抓取直到没有更多评论,确保所有评论都被正确抓取且不会重复。
内容的提问来源于stack exchange,提问作者Ashish Pithava

