Forráskód Böngészése

사용자 상세페이지 퍼블리싱 연동 및 게시글 조회 API

sangwonlee 3 éve
szülő
commit
35b324608d

+ 1 - 0
realworld/src/main/java/com/dbs/realworld/common/Views.java

@@ -5,6 +5,7 @@ public class Views {
     public static final String SIGNUP_FORM = "user/signup";               // 회원가입 폼
     public static final String SIGNIN_FORM = "user/signin";               // 로그인 폼
     public static final String USER_SETTING_FORM = "user/settings";       // 세팅 폼
+    public static final String USER_DETAIL = "user/userDetail";          // 세팅 폼
     public static final String ARTICLE_FORM = "article/article";          // 아티클 폼
     public static final String ARTICLE_DETAIL = "article/articleDetails"; // 아티클 상세
 }

+ 29 - 6
realworld/src/main/java/com/dbs/realworld/controller/ArticleController.java

@@ -165,8 +165,6 @@ public class ArticleController {
 
     /**
      * 게시물 수정
-     */
-    /**
      * https://stackoverflow.com/a/17376670
      * HTTP PATCH Request의 경우 바디에 아무 제약도 없다
      */
@@ -191,7 +189,7 @@ public class ArticleController {
 
 
     /**
-     * 게시물 조회
+     * 게시물 조회 in 메인 페이지
      * 커서 기반 페이징
      * @queryParam feed      your-feed or global-feed(디폴트)
      * @queryParam articleId cursor (디폴트 -1, -1인 경우 최초 데이터 조회)
@@ -212,10 +210,35 @@ public class ArticleController {
         Map<String, Object> data = new HashMap<>();
         data.put("articles", articleDTOs);
 
-        final int size = articleDTOs.size();
-        int lastArticleId = (size != 0) ? articleDTOs.get(size - 1).getId() : -1;
-        data.put("paging", this.articleService.calculatePagingInfo(articleDTOs, lastArticleId, feed, userId));
+        data.put("paging", this.articleService.calculatePagingInfo(articleDTOs, feed, userId));
+        
+        return ResponseEntity.ok().body(data);
+    }
+
+
+    /**
+     * 게시물 조회 in 사용자 상세 페이지
+     * 커서 기반 페이징
+     * @queryParam feed      your-feed or favorite-feed
+     * @queryParam articleId cursor (디폴트 -1, -1인 경우 최초 데이터 조회)
+     */
+    @GetMapping("/page/users/{userId}")
+    public ResponseEntity getMyArticles(HttpServletRequest request, 
+            @RequestParam(defaultValue = "your-feed") String feed,
+            @RequestParam(defaultValue = "-1") int articleId,
+            @PathVariable("userId") int userId) {
+        Map<String, Object> data = new HashMap<>();
         
+        UserDTO userDTO = this.userMapper.selectUserById(userId);
+        userDTO.setPassword("");
+        data.put("user", userDTO);
+
+        // int viewer = (int) request.getSession().getAttribute("ssId");
+        int viewer = 1;
+        List<ArticleDTO> myArticles = this.articleService.findMyArticles(viewer, userId, articleId, feed);
+        data.put("articles", myArticles);
+        data.put("paging", this.articleService.calculatePagingInfo(myArticles, feed, userId));
+
         return ResponseEntity.ok().body(data);
     }
 

+ 39 - 11
realworld/src/main/java/com/dbs/realworld/controller/UserController.java

@@ -1,12 +1,16 @@
 package com.dbs.realworld.controller;
 
+import java.util.List;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
 import com.dbs.realworld.common.Views;
+import com.dbs.realworld.dto.ArticleDTO;
 import com.dbs.realworld.dto.FollowDTO;
 import com.dbs.realworld.dto.UserDTO;
 import com.dbs.realworld.mapper.UserMapper;
+import com.dbs.realworld.service.ArticleService;
 import com.dbs.realworld.service.UserService;
 
 import org.springframework.http.HttpStatus;
@@ -27,10 +31,12 @@ import org.springframework.web.bind.annotation.RequestBody;
 @RequestMapping("/user")
 public class UserController {
 
+    private final ArticleService articleService;
     private final UserService userService;
     private final UserMapper userMapper;
 
-    public UserController(UserService userService, UserMapper userMapper) {
+    public UserController(ArticleService articleService, UserService userService, UserMapper userMapper) {
+        this.articleService = articleService;
         this.userService = userService;
         this.userMapper = userMapper;
     }
@@ -148,16 +154,16 @@ public class UserController {
     @PatchMapping("/settings/{userId}")
     public ResponseEntity updateUserSetting(HttpServletRequest request, @PathVariable("userId") int userId, @RequestBody UserDTO userDTO, ModelMap model) {
         userDTO.setId(userId);
-        
-        /**
-         * 변경하려는 사용자의 이메일이 중복되지는 않는지? 체크해야 함
-         */
-        // UserDTO findedByEmail = this.userMapper.selectUserByEmail(userDTO.getEmail());
-        // if (findedByEmail != null && userId != findedByEmail.getId()) {
-            // 변경하려는 이메일이 이미 있는 경우 처리
-            // TODO 이메일이 중복되었다고 알려줘야 함
-            // return Views.USER_SETTING_FORM;
-        // }
+
+        // 이미 이메일을 사용자가 존재하며 그 이메일 사용자가 내가 아닌 경우
+        UserDTO findedByEmail = this.userMapper.selectUserByEmail(userDTO.getEmail());
+        if (findedByEmail != null && userId != findedByEmail.getId()) {
+            /**
+             * 이메일 중복
+             * https://blog.outsider.ne.kr/1121
+             */
+            return ResponseEntity.status(HttpStatus.CONFLICT).body("이미 사용중인 이메일 입니다.");
+        }
 
         this.userService.update(userDTO);
 
@@ -171,4 +177,26 @@ public class UserController {
 
         return ResponseEntity.ok().build();
     }
+
+
+    /**
+     * 사용자 상세 페이지 진입시
+     * feed는 기본 your-type으로 하여 최초 게시글 조회
+     */
+    @GetMapping("/{userId}")
+    public String getUserInfo(HttpServletRequest request, @PathVariable("userId") int userId, ModelMap model) {
+        UserDTO userDTO = this.userMapper.selectUserById(userId);
+        userDTO.setPassword("");
+        model.addAttribute("user", userDTO);
+        
+        int viewer = (int) request.getSession().getAttribute("ssId");
+        // int viewer = 9;          // delete
+        int articleId = -1;
+        // int articleId = 1000052; // delete
+        List<ArticleDTO> myArticles = this.articleService.findMyArticles(viewer, userId, articleId, "your-feed");
+        model.addAttribute("myArticles", myArticles);
+        model.addAttribute("paging", this.articleService.calculatePagingInfo(myArticles, "your-feed", userId));
+
+        return Views.USER_DETAIL;
+    }
 }

+ 7 - 0
realworld/src/main/java/com/dbs/realworld/mapper/ArticleMapper.java

@@ -12,8 +12,13 @@ import org.springframework.stereotype.Repository;
 @Mapper
 public interface ArticleMapper {
     void insertArticle(ArticleDTO dto);
+
     List<ArticleDTO> select(int articleId, String feed, int userId);
     Map<String, Object> selectPagingInfo(int articleId, String feed, int userId);
+
+    List<ArticleDTO> selectMyArticles(int viewer, int userId, int articleId);
+    List<ArticleDTO> selectFavoriteArticles(int viewer, int userId, int articleId);
+
     ArticleDTO selectArticleDetail(int articleId, int userId);
     ArticleDTO selectByArticleId(int articleId);
     void deleteByArticleId(int articleId);
@@ -22,4 +27,6 @@ public interface ArticleMapper {
     void insertFavorite(int userId, int articleId);
     void deleteFavorite(int userId, int articleId);
     void updateFavoriteNum(int articleId);
+
+    
 }

+ 1 - 0
realworld/src/main/java/com/dbs/realworld/mapper/UserMapper.java

@@ -12,6 +12,7 @@ public interface UserMapper {
     void insertUser(UserDTO dto);
     void update(UserDTO dto);
     UserDTO selectUserByEmail(String email);
+    UserDTO selectUserById(int id);
     void insertFollow(FollowDTO dto);
     void deleteFollow(FollowDTO dto);
 }

+ 28 - 9
realworld/src/main/java/com/dbs/realworld/service/ArticleService.java

@@ -9,7 +9,6 @@ import com.dbs.realworld.mapper.ArticleMapper;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
 @Service
 public class ArticleService {
@@ -23,6 +22,7 @@ public class ArticleService {
         this.commentService = commentService;
     }
 
+
     // default: rollbackFor = {RuntimeExceeption.class, Error.class}
     // uncheckedException과 error 뿐만 아니라 모든 예외에 대해 롤백하고 싶은 경우
     // @Transactional(rollbackFor = Exception.class)
@@ -32,27 +32,48 @@ public class ArticleService {
     }
 
     
+    /**
+     * 사용자 상세 페이지에 배치될 아티클 리스트
+     * 피드 타입이 "your-type"인 경우 해당 사용자가 좋아요 한 게시물 리스트
+     * "favorite-type"인 경우 사용자가 좋아요 한 게시물 리스트
+     * @param viewer    조회자
+     * @param userId    타겟 사용자 아이디
+     * @param articleId 커서
+     * @param feed      피드 타입
+     * @return
+     */
+    public List<ArticleDTO> findMyArticles(int viewer, int userId, int articleId, String feed) {
+        List<ArticleDTO> articles = null;
+        if (feed.equals("your-feed")) {
+            articles = this.articleMapper.selectMyArticles(viewer, userId, articleId);
+        } else if (feed.equals("favorite-feed")) {
+            articles = this.articleMapper.selectFavoriteArticles(viewer, userId, articleId);
+        }
+        return articles;
+    }
+
+
     /**
      * 
      * @param feed  feed 타입, 'your-feed', 'global-feed'
      * @param id    cursor(articleId), 다음 페이지에 필요한 데이터를 위한 기준
      */
-    public List<ArticleDTO>  find(int articleId, String feed, int userId) {
+    public List<ArticleDTO> find(int articleId, String feed, int userId) {
         return this.articleMapper.select(articleId, feed, userId);
     }
 
 
-    public Map<String, Object> calculatePagingInfo(List<ArticleDTO> articleDTOs, int lastArticleId, String feed, int userId) {
+    public Map<String, Object> calculatePagingInfo(List<ArticleDTO> articleDTOs, String feed, int userId) {
+
+        final int size = articleDTOs.size();
+        int lastArticleId = (size != 0) ? articleDTOs.get(size - 1).getId() : -1;
+
         Map<String, Object> paging = this.articleMapper.selectPagingInfo(lastArticleId, feed, userId);
         paging.put("size", articleDTOs.size());
         if (lastArticleId == -1) {
             paging.put("isNext", false);
-            paging.put("firstId", null);
-            paging.put("lastId", null);
         } else {
             paging.put("isNext", ((Long) paging.get("nextItemNum")).intValue() > 0);
-            paging.put("firstId", articleDTOs.get(0).getId());
-            paging.put("lastId", articleDTOs.get(articleDTOs.size() - 1).getId());
         }
         return paging;
     }
@@ -71,8 +92,6 @@ public class ArticleService {
         List<CommentDTO> commentDTOs = this.commentService.findAllByArticleId(articleId);
         articleDTO.setComments(commentDTOs);
 
-
-
         return articleDTO;
     }
 

+ 92 - 13
realworld/src/main/resources/mybatis/mapper/user/ArticleMapper.xml

@@ -93,6 +93,80 @@
       ]]>
     </select>
 
+    
+    <select id="selectMyArticles" parameterType="hashmap" resultType="ArticleDTO">
+      <![CDATA[
+        SELECT
+          AR.id               AS id,
+          AR.title            AS title,
+          AR.subtitle         AS subtitle,
+          AR.content          AS content,
+          AR.tags             AS tags,
+          AR.create_datetime  AS created,
+          AR.favorite_num     AS favoriteNum,
+          AR.user_id          AS writerId,
+          USR.name            AS writerName,
+          USR.email           AS writerEmail,
+          (
+              SELECT COUNT(*) FROM article_like AL 
+              WHERE AL.user_id     = #{viewer}
+              AND   AL.article_id  = AR.id
+          )                   AS favorite
+          FROM article_mst AR
+          LEFT JOIN user_mst USR
+          ON AR.user_id = USR.id
+          WHERE 1=1
+          AND AR.user_id = #{userId}
+      ]]>
+      <if test='articleId != -1'>
+        <![CDATA[
+          AND AR.id < #{articleId}
+        ]]>
+      </if>
+      <![CDATA[
+        ORDER BY AR.create_datetime DESC
+        LIMIT 4
+      ]]>
+    </select>
+
+
+    <select id="selectFavoriteArticles" parameterType="hashmap" resultType="ArticleDTO">
+      <![CDATA[
+        SELECT
+          AR.id                AS id
+          ,AR.title            AS title
+          ,AR.subtitle         AS subtitle
+          ,AR.content          AS content
+          ,AR.tags             AS tags
+          ,AR.create_datetime  AS created
+          ,AR.favorite_num     AS favoriteNum
+          ,AR.user_id          AS writerId
+          ,USR.name            AS writerName
+          ,USR.email           AS writerEmail
+          ,(
+              SELECT COUNT(*) FROM article_like AL2 
+              WHERE AL2.user_id     = #{viewer}
+              AND   AL2.article_id  = AR.id
+          )                   AS favorite
+        FROM      article_mst AR
+        LEFT JOIN article_like AL
+        ON        AR.id = AL.article_id
+        LEFT JOIN user_mst USR
+        ON        AR.user_id = USR.id
+        WHERE 1=1
+        AND   AL.user_id = #{userId}
+      ]]>
+      <if test='articleId != -1'>
+        <![CDATA[
+          AND AR.id < #{articleId}
+        ]]>
+      </if>
+      <![CDATA[
+        ORDER BY AR.create_datetime DESC
+        LIMIT 4
+      ]]>
+    </select>
+
 
      <select id="selectPagingInfo" parameterType="hashmap" resultType="hashmap">
         <![CDATA[
@@ -101,28 +175,33 @@
             COUNT( IF(id < #{articleId}, 1, null) )  AS nextItemNum
           FROM article_mst AR
         ]]>
+        <if test='feed=="favorite-feed"'>
+          <![CDATA[
+            LEFT JOIN article_like AL
+            ON        AR.id = AL.article_id
+            WHERE     AL.user_id = #{userId}
+          ]]>
+        </if>
         <if test='feed == "your-feed"'>
           <![CDATA[
             WHERE     AR.user_id = #{userId}
-            GROUP BY  AR.user_id
           ]]>
         </if>
      </select>
 
-
     <select id="selectArticleDetail" parameterType="hashmap" resultType="ArticleDTO">
       <![CDATA[
         SELECT
-          USR.name            AS writerName,
-          USR.email           AS writerEmail,
-          AR.user_id          AS writerId,
-          AR.id               AS id,
-          AR.title            AS title,
-          AR.subtitle         AS subtitle,
-          AR.content          AS content,
-          AR.tags             AS tags,
-          AR.create_datetime  AS created,
-          AR.favorite_num     AS favoriteNum,
+          USR.name            AS writerName
+          ,USR.email           AS writerEmail
+          ,AR.user_id          AS writerId
+          ,AR.id               AS id
+          ,AR.title            AS title
+          ,AR.subtitle         AS subtitle
+          ,AR.content          AS content
+          ,AR.tags             AS tags
+          ,AR.create_datetime  AS created
+          ,AR.favorite_num     AS favoriteNum
       ]]>
       <if test='userId != -1'>
         <![CDATA[
@@ -130,7 +209,7 @@
               SELECT COUNT(*) FROM article_like AL 
               WHERE AL.user_id     = #{userId}
               AND   AL.article_id  = AR.id
-          )                   AS favorite,
+          )                   AS favorite
           (
               SELECT COUNT(*) FROM user_follow UF
               WHERE  UF.from_user = #{userId}

+ 21 - 5
realworld/src/main/resources/mybatis/mapper/user/UserMapper.xml

@@ -44,17 +44,33 @@
     <select id="selectUserByEmail" parameterType="String" resultType="UserDTO">
       <![CDATA[
         SELECT
-          id         AS id,
-          name       AS username,
-          email      AS email,
-          pwd        AS password,
-          short_bio  AS shortBio
+          id              AS id,
+          name            AS username,
+          email           AS email,
+          pwd             AS password,
+          short_bio       AS shortBio,
+          create_datetime AS created
         FROM user_mst
         WHERE
           email = #{email}
       ]]>
     </select>
 
+    <select id="selectUserById" parameterType="int" resultType="UserDTO">
+      <![CDATA[
+        SELECT
+          id              AS id,
+          name            AS username,
+          email           AS email,
+          pwd             AS password,
+          short_bio       AS shortBio,
+          create_datetime AS created
+        FROM user_mst
+        WHERE
+          id = #{id}
+      ]]>
+    </select>
+
     <insert id="insertFollow" parameterType="FollowDTO">
         <![CDATA[
             INSERT IGNORE INTO user_follow

+ 3 - 2
realworld/src/main/webapp/WEB-INF/jsp/include/header.jsp

@@ -69,9 +69,10 @@
                     </a>
                 </li>
                 <li class="nav-item">
+                    <c:set var="activeCondition" value="/user/${ssId}" />
                     <a
-                        href="...userpage.html"
-                        class="link <c:if test="${ssPath eq '/...'}">active</c:if>"
+                        href="/user/${ssId}"
+                        class="link <c:if test="${ssPath eq activeCondition}">active</c:if>"
                     >
                         <img src="/resources/images/avatar.png" alt=""> 
                         <c:out value="${ssUsername}"></c:out>님

+ 5 - 0
realworld/src/main/webapp/WEB-INF/jsp/user/userInfo.jsp

@@ -15,6 +15,11 @@
 <body>
     <jsp:include page="/WEB-INF/jsp/include/header.jsp"></jsp:include>
     <!-- user-page content -->
+
+    ${myArticles}
+    ${paging}
+    ${user}
+
     <div class="user-page">
         <!-- User Info -->
         <section class="user-info">