fix: User Repository, Service Refactoring.

This commit is contained in:
minseokkang
2022-12-07 16:12:30 +09:00
10 changed files with 105 additions and 33 deletions

View File

@@ -35,4 +35,31 @@ you can See Vue.js + SpringBoot FullStack Web Site Demo
#### **Total 82% Code Coverage** #### **Total 82% Code Coverage**
------ -----
# How it works
- Spring Boot(Java)
- JPA
- Security
- H2
- Vue3
- Vite
- vuerouter
- vuex
- localStorage
# Getting started
## Run Local
```shell
./gradlew bootRun
```
# FrontEnd
**I don't know much about the front end. I wanted to create a visible application, so I adopted and implemented that framework.**
But my codes are simple codes that even beginners can easily see.

View File

@@ -61,9 +61,10 @@ public class CommentServiceImpl implements CommentService {
if (article.isEmpty()) { if (article.isEmpty()) {
throw new CustomException(Error.ARTICLE_NOT_FOUND); throw new CustomException(Error.ARTICLE_NOT_FOUND);
} }
System.out.println(user.get().getUsername()+"!!");
Comment comment = commentRepository.save(Comment.builder().body(commentdto.getBody()).article(article.get()).author(user.get()).build()); Comment comment = commentRepository.save(Comment.builder().body(commentdto.getBody()).article(article.get()).author(user.get()).build());
return convertComment(userAuth, article.get(), comment); return convertComment(userAuth, comment);
} }
@Override @Override
@@ -80,9 +81,9 @@ public class CommentServiceImpl implements CommentService {
commentRepository.delete(comment.get()); commentRepository.delete(comment.get());
} }
private CommentResponse convertComment(UserAuth userAuth, Article article, Comment comment) { private CommentResponse convertComment(UserAuth userAuth, Comment comment) {
ProfileResponse profile = profileService.getProfile(userAuth, article.getAuthor().getUsername()); ProfileResponse profile = profileService.getProfile(userAuth, userAuth.getUsername());
return CommentResponse.builder() return CommentResponse.builder()
.id(comment.getId()) .id(comment.getId())

View File

@@ -2,9 +2,6 @@ package com.io.realworld.domain.aggregate.user.repository;
import com.io.realworld.domain.aggregate.user.entity.User; import com.io.realworld.domain.aggregate.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;

View File

@@ -1,7 +1,6 @@
package com.io.realworld.domain.aggregate.user.service; package com.io.realworld.domain.aggregate.user.service;
import com.io.realworld.domain.aggregate.user.dto.*; import com.io.realworld.domain.aggregate.user.dto.*;
import com.io.realworld.domain.aggregate.user.entity.User;
public interface UserService { public interface UserService {
UserResponse signup(UserSignupRequest userSignupRequest); UserResponse signup(UserSignupRequest userSignupRequest);

View File

@@ -8,7 +8,6 @@ import com.io.realworld.domain.aggregate.user.entity.User;
import com.io.realworld.domain.aggregate.user.repository.UserRepository; import com.io.realworld.domain.aggregate.user.repository.UserRepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -17,7 +16,6 @@ import java.util.Optional;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@Log4j2
public class UserServiceImpl implements UserService { public class UserServiceImpl implements UserService {
private final UserRepository userRepository; private final UserRepository userRepository;

View File

@@ -74,7 +74,7 @@ const createArticle = async (article: object | undefined): Promise<AxiosResponse
}) })
} }
const updateArticle = async (article: object | undefined, slug: string): Promise<AxiosResponse> => { const updateArticle = async (article: object | undefined, slug: string | undefined): Promise<AxiosResponse> => {
let currentToken = localStorage.getItem("token"); let currentToken = localStorage.getItem("token");
return await axiosService.put('/api/articles/' + slug, { article },{ return await axiosService.put('/api/articles/' + slug, { article },{
headers :{ headers :{
@@ -84,6 +84,16 @@ const updateArticle = async (article: object | undefined, slug: string): Promise
}) })
} }
const deleteArticle = async (slug: string | undefined): Promise<AxiosResponse> => {
let currentToken = localStorage.getItem("token");
return await axiosService.delete('/api/articles/' + slug,{
headers :{
Authorization : "TOKEN " + currentToken,
"Content-Type": `application/json`,
}
})
}
const listArticles = async (): Promise<AxiosResponse> => { const listArticles = async (): Promise<AxiosResponse> => {
let currentToken = localStorage.getItem("token"); let currentToken = localStorage.getItem("token");
if(currentToken == null){ if(currentToken == null){
@@ -171,6 +181,17 @@ const getCommentsFromArticle = async (slug: string | undefined): Promise<AxiosRe
} }
} }
const deleteCommentsFromArticle = async (slug: string | undefined, id: number): Promise<AxiosResponse> => {
let currentToken = localStorage.getItem("token");
return await axiosService.delete('/api/articles/' + slug + '/comments/' + id,{
headers:{
Authorization : "TOKEN " + currentToken,
"Content-Type": `application/json`,
}
});
}
const favoriteArticle = async (slug: string | undefined): Promise<AxiosResponse> => { const favoriteArticle = async (slug: string | undefined): Promise<AxiosResponse> => {
let currentToken = localStorage.getItem("token"); let currentToken = localStorage.getItem("token");
return await axiosService.post('/api/articles/' + slug + '/favorite',{}, return await axiosService.post('/api/articles/' + slug + '/favorite',{},
@@ -206,5 +227,6 @@ export { signUp, signIn,
addCommentToArticle, getCommentsFromArticle, addCommentToArticle, getCommentsFromArticle,
favoriteArticle, unFavoriteArticle, favoriteArticle, unFavoriteArticle,
listArticlesByFavorite, updateArticle, listArticlesByFavorite, updateArticle,
deleteArticle, deleteCommentsFromArticle,
getTags getTags
} }

View File

@@ -7,17 +7,21 @@
<a href="javascript:(0)" class="comment-author" @click="viewProfile"> <a href="javascript:(0)" class="comment-author" @click="viewProfile">
<img :src="comment.author.image" class="comment-author-img"/> <img :src="comment.author.image" class="comment-author-img"/>
</a> </a>
&nbsp;
<a href="javascript:(0)" class="comment-author" @click="viewProfile">{{comment.author.username}}</a> <a href="javascript:(0)" class="comment-author" @click="viewProfile">{{comment.author.username}}</a>
<span class="date-posted">{{convertDate(comment.updatedAt)}}</span> <span class="date-posted">{{convertDate(comment.updatedAt)}}</span>
<span v-if="isMe" class="mod-options" @click="deleteComment">
<i class="ion-trash-a"></i>
</span>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent,} from "vue"; import {defineComponent, defineEmits, onMounted, ref,} from "vue";
import convertDate from "@/ts/common"; import convertDate from "@/ts/common";
import router from "@/router"; import router from "@/router";
import {useStore} from "vuex";
export default defineComponent({ export default defineComponent({
name: "commentList", name: "commentList",
props:{ props:{
@@ -36,7 +40,10 @@ export default defineComponent({
} }
} }
}, },
setup(props){ setup(props, {emit}){
const store = useStore();
const isMe = ref(false);
const viewProfile = () => { const viewProfile = () => {
router.push({ router.push({
@@ -44,7 +51,19 @@ export default defineComponent({
params: {username: props.comment.author.username}}) params: {username: props.comment.author.username}})
} }
return { convertDate, viewProfile} const deleteComment = () => {
emit('delete:comment', props.comment.id);
}
onMounted(async () => {
if(store.state.username == props.comment.author.username){
isMe.value = true;
}else{
isMe.value = false;
}
})
return { isMe, convertDate, viewProfile, deleteComment}
} }
}) })

View File

@@ -34,7 +34,7 @@ const routes = [
component: () => import(/* webpackChunkName "inputTag" */ '@/views/TheArticle.vue') component: () => import(/* webpackChunkName "inputTag" */ '@/views/TheArticle.vue')
}, },
{ {
path: "/editor/@:slug", path: "/editor/:slug",
name: "ArticleUpdateEditor", name: "ArticleUpdateEditor",
component: () => import(/* webpackChunkName "inputTag" */ '@/views/ArticleUpdate.vue'), component: () => import(/* webpackChunkName "inputTag" */ '@/views/ArticleUpdate.vue'),
props: true props: true

View File

@@ -31,33 +31,29 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { reactive } from "vue"; import { reactive, defineComponent } from "vue";
import { updateArticle } from "@/api/index.js"; import { updateArticle } from "@/api/index.js";
import router from "@/router"; import router from "@/router";
export default { export default defineComponent({
name: "ArticleUpdate", name: "ArticleUpdate",
props:{ props:{
slug: String, slug: String,
}, },
setup(){ setup(props){
const article = reactive({ const article = reactive({
title: "", title: "",
description: "", description: "",
body: "", body: "",
}) })
const getSlug = (title:string) => {
return title.replace(' ','-');
}
const updateContent = async () => { const updateContent = async () => {
const slug = getSlug(article.title);
try{ try{
await updateArticle(article, slug); const { data } = await updateArticle(article, props.slug);
await router.push({ await router.push({
name:"ArticleDetail", name:"ArticleDetail",
params: {slug} params: {slug: data.article.slug}
}) })
}catch (error: any){ }catch (error: any){
alert(error); alert(error);
@@ -66,7 +62,7 @@ export default {
return { article, updateContent } return { article, updateContent }
} }
} })
</script> </script>
<style scoped> <style scoped>

View File

@@ -16,7 +16,7 @@
<i class="ion-edit"></i> <i class="ion-edit"></i>
Edit Article Edit Article
</button> </button>
<button v-if= "isMe" class="btn btn-outline-danger btn-sm" @click="followUpdate(articleDetail.article.author.following)"> <button v-if= "isMe" class="btn btn-outline-danger btn-sm" @click="articleDelete()">
<i class="ion-trash-a"></i> <i class="ion-trash-a"></i>
Delete Article Delete Article
</button> </button>
@@ -62,11 +62,11 @@
<a href="javascript:void(0)" class="author" @click="viewProfile">{{ articleDetail.article.author.username }}</a> <a href="javascript:void(0)" class="author" @click="viewProfile">{{ articleDetail.article.author.username }}</a>
<span class="date">{{convertDate(articleDetail.article.createdAt)}}</span> <span class="date">{{convertDate(articleDetail.article.createdAt)}}</span>
</div> </div>
<button v-if= "isMe" class="btn btn-sm btn-outline-secondary" @click="followUpdate(articleDetail.article.author.following)"> <button v-if= "isMe" class="btn btn-sm btn-outline-secondary" @click="articleUpdate()">
<i class="ion-edit"></i> <i class="ion-edit"></i>
Edit Article Edit Article
</button> </button>
<button v-if= "isMe" class="btn btn-outline-danger btn-sm" @click="followUpdate(articleDetail.article.author.following)"> <button v-if= "isMe" class="btn btn-outline-danger btn-sm" @click="articleDelete()">
<i class="ion-trash-a"></i> <i class="ion-trash-a"></i>
Delete Article Delete Article
</button> </button>
@@ -112,6 +112,7 @@
<comment-list v-for="(comment,index) in getCommentList.comment" <comment-list v-for="(comment,index) in getCommentList.comment"
:key="comment.id" :key="comment.id"
:comment="comment" :comment="comment"
@delete:comment="deleteComment"
:imgs="comment.author.image"> :imgs="comment.author.image">
</comment-list> </comment-list>
</div> </div>
@@ -130,7 +131,7 @@ import router from "@/router";
import { useStore } from "vuex"; import { useStore } from "vuex";
import convertDate from "@/ts/common"; import convertDate from "@/ts/common";
import { import {
addCommentToArticle, addCommentToArticle, deleteArticle, deleteCommentsFromArticle,
favoriteArticle, favoriteArticle,
followUser, followUser,
getArticle, getArticle,
@@ -157,7 +158,7 @@ export default defineComponent({
}) })
const getCommentList = reactive({ const getCommentList = reactive({
comment: reactive([{id:0,author:{image:""}}]) comment: reactive([{id:0,author:{username:"",image:""}}])
}) })
const articleDetail = reactive({ const articleDetail = reactive({
@@ -188,11 +189,18 @@ export default defineComponent({
const articleUpdate = async () => { const articleUpdate = async () => {
await router.push({ await router.push({
name: 'ArticleEditor', name: 'ArticleUpdateEditor',
params: {slug: articleDetail.article.slug} params: {slug: articleDetail.article.slug}
}) })
} }
const articleDelete = async () => {
await deleteArticle(articleDetail.article.slug);
await router.push({
name: 'Home'
})
}
const followUpdate = async (followState: boolean) => { const followUpdate = async (followState: boolean) => {
if(token == ''){ if(token == ''){
await router.push({name:"Login"}); await router.push({name:"Login"});
@@ -241,6 +249,11 @@ export default defineComponent({
} }
} }
const deleteComment = async (commentId: number) => {
await deleteCommentsFromArticle(articleDetail.article.slug,commentId);
getCommentList.comment.splice(commentId,1);
}
onMounted(async ()=>{ onMounted(async ()=>{
try{ try{
const { data } = await getArticle(props.slug); const { data } = await getArticle(props.slug);
@@ -260,7 +273,7 @@ export default defineComponent({
} }
}) })
return { isMe, articleDetail, comment, getCommentList, convertDate, viewProfile, articleUpdate, followUpdate, favoriteUpdate, sendComment } return { isMe, articleDetail, comment, getCommentList, convertDate, deleteComment, viewProfile, articleUpdate, followUpdate, favoriteUpdate, sendComment, articleDelete }
} }
}) })
</script> </script>