增加积分系统

This commit is contained in:
WilliamColton
2025-08-07 16:18:54 +08:00
parent 227269c639
commit 105f7781b3
13 changed files with 306 additions and 93 deletions

View File

@@ -7,6 +7,7 @@ import com.openisle.mapper.CommentMapper;
import com.openisle.service.CaptchaService;
import com.openisle.service.CommentService;
import com.openisle.service.LevelService;
import com.openisle.service.PointService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
@@ -26,6 +27,7 @@ public class CommentController {
private final LevelService levelService;
private final CaptchaService captchaService;
private final CommentMapper commentMapper;
private final PointService pointService;
@Value("${app.captcha.enabled:false}")
private boolean captchaEnabled;
@@ -45,6 +47,7 @@ public class CommentController {
Comment comment = commentService.addComment(auth.getName(), postId, req.getContent());
CommentDto dto = commentMapper.toDto(comment);
dto.setReward(levelService.awardForComment(auth.getName()));
dto.setPointReward(pointService.awardForComment(auth.getName(),postId));
log.debug("createComment succeeded for comment {}", comment.getId());
return ResponseEntity.ok(dto);
}

View File

@@ -5,12 +5,7 @@ import com.openisle.dto.PostRequest;
import com.openisle.dto.PostSummaryDto;
import com.openisle.mapper.PostMapper;
import com.openisle.model.Post;
import com.openisle.service.CaptchaService;
import com.openisle.service.DraftService;
import com.openisle.service.LevelService;
import com.openisle.service.PostService;
import com.openisle.service.SubscriptionService;
import com.openisle.service.UserVisitService;
import com.openisle.service.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
@@ -25,12 +20,12 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
private final SubscriptionService subscriptionService;
private final LevelService levelService;
private final CaptchaService captchaService;
private final DraftService draftService;
private final UserVisitService userVisitService;
private final PostMapper postMapper;
private final PointService pointService;
@Value("${app.captcha.enabled:false}")
private boolean captchaEnabled;
@@ -48,6 +43,7 @@ public class PostController {
draftService.deleteDraft(auth.getName());
PostDetailDto dto = postMapper.toDetailDto(post, auth.getName());
dto.setReward(levelService.awardForPost(auth.getName()));
dto.setPointReward(pointService.awardForPost(auth.getName()));
return ResponseEntity.ok(dto);
}

View File

@@ -6,8 +6,8 @@ import com.openisle.mapper.ReactionMapper;
import com.openisle.model.Reaction;
import com.openisle.model.ReactionType;
import com.openisle.service.LevelService;
import com.openisle.service.PointService;
import com.openisle.service.ReactionService;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
@@ -20,6 +20,7 @@ public class ReactionController {
private final ReactionService reactionService;
private final LevelService levelService;
private final ReactionMapper reactionMapper;
private final PointService pointService;
/**
* Get all available reaction types.
@@ -31,27 +32,29 @@ public class ReactionController {
@PostMapping("/posts/{postId}/reactions")
public ResponseEntity<ReactionDto> reactToPost(@PathVariable Long postId,
@RequestBody ReactionRequest req,
Authentication auth) {
@RequestBody ReactionRequest req,
Authentication auth) {
Reaction reaction = reactionService.reactToPost(auth.getName(), postId, req.getType());
if (reaction == null) {
return ResponseEntity.noContent().build();
}
ReactionDto dto = reactionMapper.toDto(reaction);
dto.setReward(levelService.awardForReaction(auth.getName()));
pointService.awardForReactionOfPost(auth.getName(), postId);
return ResponseEntity.ok(dto);
}
@PostMapping("/comments/{commentId}/reactions")
public ResponseEntity<ReactionDto> reactToComment(@PathVariable Long commentId,
@RequestBody ReactionRequest req,
Authentication auth) {
@RequestBody ReactionRequest req,
Authentication auth) {
Reaction reaction = reactionService.reactToComment(auth.getName(), commentId, req.getType());
if (reaction == null) {
return ResponseEntity.noContent().build();
}
ReactionDto dto = reactionMapper.toDto(reaction);
dto.setReward(levelService.awardForReaction(auth.getName()));
pointService.awardForReactionOfComment(auth.getName(), commentId);
return ResponseEntity.ok(dto);
}
}

View File

@@ -17,5 +17,6 @@ public class CommentDto {
private List<CommentDto> replies;
private List<ReactionDto> reactions;
private int reward;
private int pointReward;
}

View File

@@ -27,5 +27,6 @@ public class PostSummaryDto {
private List<AuthorDto> participants;
private boolean subscribed;
private int reward;
private int pointReward;
}

View File

@@ -25,6 +25,7 @@ public class UserDto {
private long likesReceived;
private boolean subscribed;
private int experience;
private int point;
private int currentLevel;
private int nextLevelExp;
}

View File

@@ -53,6 +53,7 @@ public class UserMapper {
dto.setLikesSent(reactionService.countLikesSent(user.getUsername()));
dto.setLikesReceived(reactionService.countLikesReceived(user.getUsername()));
dto.setExperience(user.getExperience());
dto.setPoint(user.getPoint());
dto.setCurrentLevel(levelService.getLevel(user.getExperience()));
dto.setNextLevelExp(levelService.nextLevelExp(user.getExperience()));
if (viewer != null) {

View File

@@ -0,0 +1,37 @@
package com.openisle.model;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDate;
/** Daily experience gain counts for a user. */
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "point_logs",
uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "log_date"}))
public class PointLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id")
private User user;
@Column(name = "log_date", nullable = false)
private LocalDate logDate;
@Column(name = "post_count", nullable = false)
private int postCount;
@Column(name = "comment_count", nullable = false)
private int commentCount;
@Column(name = "reaction_count", nullable = false)
private int reactionCount;
}

View File

@@ -5,9 +5,8 @@ import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
import com.openisle.model.Role;
import java.time.LocalDateTime;
/**
* Simple user entity with basic fields and a role.
@@ -44,6 +43,9 @@ public class User {
@Column(nullable = false)
private int experience = 0;
@Column(nullable = false)
private int point = 0;
@Column(length = 1000)
private String introduction;

View File

@@ -0,0 +1,12 @@
package com.openisle.repository;
import com.openisle.model.PointLog;
import com.openisle.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate;
import java.util.Optional;
public interface PointLogRepository extends JpaRepository<PointLog, Long> {
Optional<PointLog> findByUserAndLogDate(User user, LocalDate logDate);
}

View File

@@ -0,0 +1,115 @@
package com.openisle.service;
import com.openisle.model.PointLog;
import com.openisle.model.User;
import com.openisle.repository.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
@Service
@RequiredArgsConstructor
public class PointService {
private final UserRepository userRepository;
private final PointLogRepository pointLogRepository;
private final PostRepository postRepository;
private final CommentRepository commentRepository;
public int awardForPost(String userName) {
User user = userRepository.findByUsername(userName).orElseThrow();
PointLog log = getTodayLog(user);
if (log.getPostCount() > 1) return 0;
log.setPostCount(log.getPostCount() + 1);
pointLogRepository.save(log);
return addPoint(user, 30);
}
private PointLog getTodayLog(User user) {
LocalDate today = LocalDate.now();
return pointLogRepository.findByUserAndLogDate(user, today)
.orElseGet(() -> {
PointLog log = new PointLog();
log.setUser(user);
log.setLogDate(today);
log.setPostCount(0);
log.setCommentCount(0);
log.setReactionCount(0);
return pointLogRepository.save(log);
});
}
private int addPoint(User user, int amount) {
user.setPoint(user.getPoint() + amount);
userRepository.save(user);
return amount;
}
// 同时为评论者和发帖人增加积分,返回值为评论者增加的积分数
// 注意需要考虑发帖和回复是同一人的场景
public int awardForComment(String commenterName, Long postId) {
// 标记评论者是否已达到积分奖励上限
boolean isTheRewardCapped = false;
// 根据帖子id找到发帖人
User poster = postRepository.findById(postId).orElseThrow().getAuthor();
// 获取评论者的加分日志
User commenter = userRepository.findByUsername(commenterName).orElseThrow();
PointLog log = getTodayLog(commenter);
if (log.getCommentCount() > 3) {
isTheRewardCapped = true;
}
// 如果发帖人与评论者是同一个,则只计算发帖加分
if (poster.getId().equals(commenter.getId())) {
if (isTheRewardCapped) {
return 0;
} else {
log.setCommentCount(log.getCommentCount() + 1);
pointLogRepository.save(log);
return addPoint(commenter, 10);
}
}
// 如果不是同一个,则为发帖人和评论者同时加分
addPoint(poster, 10);
return addPoint(commenter, 10);
}
// 考虑点赞者和发帖人是同一个的情况
public int awardForReactionOfPost(String reactionerName, Long postId) {
// 根据帖子id找到发帖人
User poster = postRepository.findById(postId).orElseThrow().getAuthor();
// 获取点赞者信息
User reactioner = userRepository.findByUsername(reactionerName).orElseThrow();
// 如果发帖人与点赞者是同一个,则不加分
if (poster.getId().equals(reactioner.getId())) {
return 0;
}
// 如果不是同一个,则为发帖人加分
return addPoint(poster, 10);
}
// 考虑点赞者和评论者是同一个的情况
public int awardForReactionOfComment(String reactionerName, Long commentId) {
// 根据帖子id找到评论者
User commenter = commentRepository.findById(commentId).orElseThrow().getAuthor();
// 获取点赞者信息
User reactioner = userRepository.findByUsername(reactionerName).orElseThrow();
// 如果评论者与点赞者是同一个,则不加分
if (commenter.getId().equals(reactioner.getId())) {
return 0;
}
// 如果不是同一个,则为发帖人加分
return addPoint(commenter, 10);
}
}