feat: 게시판 추천/비추천 + 작성자 아바타 + 추천자 + 활동 포인트
CI / build (push) Failing after 12m4s

- 작성자 아바타: post/comment 조회 시 member JOIN(google_picture/profile_image),
  목록·상세·댓글 응답에 노출
- 추천/비추천: post_vote/comment_vote 테이블 + 토글 API
  (POST /board/posts|comments/{id}/vote), 게시글/댓글 응답에 up/down/myVote
- 추천자 목록: GET /board/posts/{id}/recommenders + PostDetail.recommenders
- 포인트: member.points + point_history. 글/댓글 작성 시 10P, 하루 3회 한도(합산).
  PointService, GET /auth/points, MemberResponse.points 노출
- @MapperScan 에 point.mapper 추가, AuthController WebMvc 테스트 MockBean 보강

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-28 12:27:35 +09:00
parent 98f5f51112
commit ef9dace1df
27 changed files with 489 additions and 25 deletions
+75
View File
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sb.web.board.mapper.VoteMapper">
<!-- ===== 게시글 ===== -->
<select id="findPostVote" resultType="string">
SELECT vote_type FROM post_vote WHERE post_id = #{postId} AND member_id = #{memberId}
</select>
<insert id="upsertPostVote">
INSERT INTO post_vote (post_id, member_id, vote_type, created_at)
VALUES (#{postId}, #{memberId}, #{type}, NOW())
ON DUPLICATE KEY UPDATE vote_type = #{type}, created_at = NOW()
</insert>
<delete id="deletePostVote">
DELETE FROM post_vote WHERE post_id = #{postId} AND member_id = #{memberId}
</delete>
<select id="countPostVotes" resultType="int">
SELECT COUNT(*) FROM post_vote WHERE post_id = #{postId} AND vote_type = #{type}
</select>
<select id="findPostRecommenders" parameterType="long" resultType="com.sb.web.board.dto.Recommender">
SELECT m.id AS member_id, m.name, m.google_picture, m.profile_image
FROM post_vote v
JOIN member m ON m.id = v.member_id
WHERE v.post_id = #{postId} AND v.vote_type = 'UP'
ORDER BY v.created_at DESC, v.member_id DESC
</select>
<delete id="deletePostVotesByPost" parameterType="long">
DELETE FROM post_vote WHERE post_id = #{postId}
</delete>
<!-- ===== 댓글 ===== -->
<select id="findCommentVote" resultType="string">
SELECT vote_type FROM comment_vote WHERE comment_id = #{commentId} AND member_id = #{memberId}
</select>
<insert id="upsertCommentVote">
INSERT INTO comment_vote (comment_id, member_id, vote_type, created_at)
VALUES (#{commentId}, #{memberId}, #{type}, NOW())
ON DUPLICATE KEY UPDATE vote_type = #{type}, created_at = NOW()
</insert>
<delete id="deleteCommentVote">
DELETE FROM comment_vote WHERE comment_id = #{commentId} AND member_id = #{memberId}
</delete>
<select id="countCommentVotes" resultType="int">
SELECT COUNT(*) FROM comment_vote WHERE comment_id = #{commentId} AND vote_type = #{type}
</select>
<select id="findCommentVoteSummary" resultType="com.sb.web.board.dto.CommentVoteSummary">
SELECT cv.comment_id,
SUM(cv.vote_type = 'UP') AS up_count,
SUM(cv.vote_type = 'DOWN') AS down_count,
MAX(CASE WHEN cv.member_id = #{memberId} THEN cv.vote_type END) AS my_vote
FROM comment_vote cv
JOIN comment c ON c.id = cv.comment_id
WHERE c.post_id = #{postId}
GROUP BY cv.comment_id
</select>
<delete id="deleteCommentVotesByComment" parameterType="long">
DELETE FROM comment_vote WHERE comment_id = #{commentId}
</delete>
<delete id="deleteCommentVotesByPost" parameterType="long">
DELETE FROM comment_vote WHERE comment_id IN (SELECT id FROM comment WHERE post_id = #{postId})
</delete>
</mapper>