需求:點贊 (讚/取消)、踩 (踩/取消),互斥 (贊→踩 變踩,踩→贊 變贊),顯示即時計數。
CREATE TABLE PostVote (
PostId BIGINT NOT NULL,
UserId BIGINT NOT NULL,
VoteType TINYINT NOT NULL, -- 1=贊, -1=踩, 0=取消(軟刪除或不存)
CreatedAt DATETIME DEFAULT GETDATE(),
PRIMARY KEY (PostId, UserId)
);
-- 統計視圖
CREATE VIEW PostVoteStats AS
SELECT PostId,
SUM(CASE WHEN VoteType=1 THEN 1 ELSE 0 END) AS LikeCount,
SUM(CASE WHEN VoteType=-1 THEN 1 ELSE 0 END) AS DislikeCount
FROM PostVote
GROUP BY PostId;
[HttpPost("api/posts/{postId}/vote")]
public async Task Vote(long postId, [FromBody] VoteDto dto) {
var userId = GetCurrentUserId();
using var tx = await _db.Database.BeginTransactionAsync();
var existing = await _db.PostVotes.FindAsync(postId, userId);
int newType = dto.Type; // 1 or -1
if (existing != null) {
if (existing.VoteType == newType) {
_db.PostVotes.Remove(existing); // 取消同類型
newType = 0;
} else {
existing.VoteType = newType; // 切換類型
}
} else {
_db.PostVotes.Add(new PostVote { PostId=postId, UserId=userId, VoteType=newType });
}
await _db.SaveChangesAsync();
await tx.CommitAsync();
var stats = await _db.PostVoteStats.FindAsync(postId);
return Ok(new { like = stats.LikeCount, dislike = stats.DislikeCount, myVote = newType });
}
這套「資料庫唯一約束 + 事務切換狀態 + 統計視圖」模式,支援高併發、邏輯清晰、易擴充 (如加入「愛心、笑臉、驚訝」多表情)。