Commit f64cf0d1 authored by 전성희's avatar 전성희

[REFACTOR] docs 모듈 개편(임시) - guide db 구조 개편(+ 관련 ui, api도 수정중)

- 재귀 컴포넌트 적용
- 목차 계층 구조를 path column에 경로 나열 방식으로 처리(ex : 'organization/dashboard/' )
parent 04ca0d0b
package com.vazil.vridge.docs.controller;
import com.vazil.vridge.docs.model.Guide;
import com.vazil.vridge.docs.model.GuideContent;
import com.vazil.vridge.docs.model.GuideTest;
import com.vazil.vridge.docs.service.GuideService;
import com.vazil.vridge.docs.service.GuideTestService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@AllArgsConstructor
@Log4j2
@RequestMapping("/test")
@CrossOrigin
@Api("가이드 API V1")
public class GuideTestController {
GuideService guideService;
GuideTestService guideTestService;
@ApiOperation(value="모든 가이드 목차 조회")
@GetMapping("/index")
public ResponseEntity<?> findIndex()throws Exception{
return new ResponseEntity<>(guideTestService.findGuideIndex(), HttpStatus.OK);
}
@ApiOperation(value="모든 가이드 목차 검색")
@GetMapping("/search")
public ResponseEntity<?> search(@RequestParam(value = "keyword", required = false) String keyword) throws Exception{
return new ResponseEntity<>(guideService.search(keyword), HttpStatus.OK);
}
@ApiOperation(value="가이드 타이틀 추가")
@PostMapping
public ResponseEntity<?> createGuideIndex(@RequestBody GuideTest guide) throws Exception{
return new ResponseEntity<>(guideTestService.createGuide(guide), HttpStatus.CREATED);
}
@PutMapping("/index")
public ResponseEntity<?> updateGuideIndex(@RequestBody Guide guide) throws Exception{
return new ResponseEntity<>(guideService.updateGuide(guide), HttpStatus.OK);
}
@PutMapping("/keyword")
public ResponseEntity<?> updateGuideKeyword(@RequestBody Guide guide) throws Exception{
return new ResponseEntity<>(guideService.updateGuideKeyword(guide), HttpStatus.OK);
}
@GetMapping
public ResponseEntity<?> getGuide(@RequestParam(value = "guideId") String guideId) throws Exception{
return new ResponseEntity<>(guideTestService.findGuideById(guideId), HttpStatus.OK);
}
@DeleteMapping
public ResponseEntity<?> deleteGuideIndex(@RequestParam(value = "guideId") String guideId, @RequestParam(value = "cascade", required = false) boolean cascade) throws Exception{
return new ResponseEntity<>(guideService.deleteGuide(guideId, cascade), HttpStatus.OK);
}
@PutMapping
public ResponseEntity<?> updateGuideContent(@RequestBody GuideContent guideContent) throws Exception{
return new ResponseEntity<>(guideService.updateGuideContent(guideContent), HttpStatus.OK);
}
}
package com.vazil.vridge.docs.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import nonapi.io.github.classgraph.json.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
import java.time.LocalDateTime;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Document(value= "guide-test")
@Data
public class GuideTest implements Serializable {
private static final long serialVersionUID = 1928367242L;
@Id
private String id;
private String locale;
private String title;
private Integer order;
private String path;
private Integer view;
private Integer like;
// @Transient
// private Guide prevContent;
// @Transient
// private Guide nextContent;
private LocalDateTime createDate;
@LastModifiedDate
private LocalDateTime updateDate;
private LocalDateTime deleteDate;
}
package com.vazil.vridge.docs.repository;
import com.vazil.vridge.docs.model.Guide;
import com.vazil.vridge.docs.model.GuideTest;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface GuideTestRepository extends CrudRepository<GuideTest, String> {
List<GuideTest> findAllByOrderByOrder();
// List<GuideTest> findAllByParentIdOrderByOrderAsc(String parentId);
// List<GuideTest> findAllByKeywordSetContaining(String keyword);
int countAllBy();
// int countAllByParentId(String parentId);
}
package com.vazil.vridge.docs.service;
import com.vazil.vridge.docs.model.Guide;
import com.vazil.vridge.docs.model.GuideContent;
import com.vazil.vridge.docs.model.GuideTest;
import com.vazil.vridge.docs.repository.GuideContentRepository;
import com.vazil.vridge.docs.repository.GuideRepository;
import com.vazil.vridge.docs.repository.GuideTestRepository;
import com.vazil.vridge.docs.utils.TimeManager;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
@Service
@AllArgsConstructor
@Log4j2
public class GuideTestService {
GuideRepository guideRepository;
GuideTestRepository guideTestRepository;
GuideContentRepository guideContentRepository;
public List<GuideTest> findGuideIndex() throws Exception{
// List<GuideTest> guides = new ArrayList<>();
List<GuideTest> guideList = guideTestRepository.findAllByOrderByOrder();
// List<GuideTest> sortedGuideList = guideTestRepository.findAllByParentIdOrderByOrderAsc("");
// for(GuideTest guide : sortedGuideList) {
// guide.setChildren(getGuideTreeByParentId(guide.getId()));
// guides.add(guide);
// }
return guideList;
}
@Transactional
public GuideTest findGuideById(String guideId) throws Exception{
GuideTest targetGuide = guideTestRepository.findById(guideId).orElseThrow(NoSuchElementException::new);
// List<Guide> sortedGuideList = new ArrayList<>();
// for(Guide guide : guideRepository.findAllByParentIdOrderByOrderAsc("")) {
// sortedGuideList.add(guide);
// sortedGuideList.addAll(getGuideListByParentId(guide.getId()));
// }
//
// for(int i = 0; i < sortedGuideList.size(); i++) {
// if(sortedGuideList.get(i).getId().equals(guideId)) {
// if(i > 0) {
// targetGuide.setPrevContent(sortedGuideList.get(i-1));
// }
// if(i < sortedGuideList.size() - 1) {
// targetGuide.setNextContent(sortedGuideList.get(i+1));
// }
// break;
// }
// }
return targetGuide;
}
public List<Guide> search(String keyword) throws Exception{
return guideRepository.findAllByKeywordSetContaining(keyword);
}
@Transactional
public GuideTest createGuide(GuideTest guide) throws Exception{
// guide.setOrder(guide.getOrder() == null ? getLastOrder(guide.getParentId()) : 0);
guide.setLike(0);
guide.setView(0);
guide.setCreateDate(TimeManager.now());
return guideTestRepository.save(guide);
}
@Transactional
public Guide updateGuide(Guide guide) throws Exception{
Guide savedGuide = guideRepository.findById(guide.getId()).orElseThrow(NoSuchElementException::new);
savedGuide.setParentId(guide.getParentId());
savedGuide.setOrder(guide.getOrder());
savedGuide.setUpdateDate(TimeManager.now());
savedGuide.setContentKey(guide.getContentKey());
return guideRepository.save(savedGuide);
}
@Transactional
public Guide updateGuideKeyword(Guide guide) throws Exception{
Guide savedGuide = guideRepository.findById(guide.getId()).orElseThrow(NoSuchElementException::new);
savedGuide.setKeywordSet(guide.getKeywordSet());
return guideRepository.save(savedGuide);
}
@Transactional
public Guide deleteGuide(String guideId, boolean cascade) throws Exception{
Guide guide = guideRepository.findById(guideId).orElseThrow(NoSuchElementException::new);
String targetId = guide.getId();
String parentId = guide.getParentId();
List<Guide> children = getGuideListByParentId(targetId);
if(cascade) {
for(Guide g : children) {
guideRepository.delete(g);
//guideContentRepository.deleteByGuideId(g.getId());
}
} else {
for(Guide g : guideRepository.findAllByParentIdOrderByOrderAsc(targetId)) {
g.setParentId(parentId);
g.setOrder(getLastOrder(parentId));
guideRepository.save(g);
}
}
guideRepository.delete(guide);
//guideContentRepository.deleteByGuideId(guide.getId());
return guide;
}
///////////////////////////////////////////////////////
// Guide Content CRUD
@Transactional
public GuideContent findContentById(String guideId) throws Exception{
GuideContent targetContent = guideContentRepository.findByGuideId(guideId).orElseThrow(NoSuchElementException::new);
List<Guide> sortedGuideList = new ArrayList<>();
for(Guide guide : guideRepository.findAllByParentIdOrderByOrderAsc("")) {
sortedGuideList.add(guide);
sortedGuideList.addAll(getGuideListByParentId(guide.getId()));
}
for(int i = 0; i < sortedGuideList.size(); i++) {
if(sortedGuideList.get(i).getId().equals(guideId)) {
if(i > 0) {
targetContent.setPrevContent(sortedGuideList.get(i-1));
}
if(i < sortedGuideList.size() - 1) {
targetContent.setNextContent(sortedGuideList.get(i+1));
}
}
}
return targetContent;
}
@Transactional
public GuideContent updateGuideContent(GuideContent guideContent) throws Exception{
GuideContent savedGuideContent = guideContentRepository.findById(guideContent.getId()).orElseThrow(NoSuchElementException::new);
savedGuideContent.setContent(guideContent.getContent());
savedGuideContent.setUpdateDate(TimeManager.now());
return guideContentRepository.save(savedGuideContent);
}
/////////////////////////////////////////////////
// Common
// 트리 구조로 자식 guide를 가져옴
private List<Guide> getGuideTreeByParentId(String parentId) {
List<Guide> children = new ArrayList<>();
for(Guide childGuide : guideRepository.findAllByParentIdOrderByOrderAsc(parentId)) {
childGuide.setChildren(guideRepository.findAllByParentIdOrderByOrderAsc(childGuide.getId()));
children.add(childGuide);
}
return children;
}
//트리 구조의 guide를 1차원 리스트로 가져옴
private List<Guide> getGuideListByParentId(String parentId) {
List<Guide> children = new ArrayList<>();
for(Guide childGuide : guideRepository.findAllByParentIdOrderByOrderAsc(parentId)) {
children.add(childGuide);
children.addAll(guideRepository.findAllByParentIdOrderByOrderAsc(childGuide.getId()));
}
return children;
}
//현재 뎁스의 마지막 순번을 가져옴
private Integer getLastOrder(String parentId) {
return guideRepository.countAllByParentId(parentId);
}
}
<template>
<div v-if="items">
<v-list
v-for="(item, i) in items"
:key="`guide-${item.title}-${i}`"
:class="depth > 1 ? `ml-${depth*2}` : ''"
class="pa-0"
>
<v-list-item
v-if="item.children.length === 0"
@click="clickChildEvent(item)"
v-model="item.active"
:ripple="false"
active-class="active"
:style="isActiveMenu(item.id) ? 'border-left:2px solid #1E88E5;' : 'border-left:1px solid rgba(0,0,0,0.15);'"
>
<v-list-item-title v-text="item.title" />
</v-list-item>
<v-list-group
v-else
:ripple="false"
v-model="item.active"
active-class="active"
:style="isActiveMenu(item.id) ? 'border-left:2px solid #1E88E5;' : 'border-left:1px solid rgba(0,0,0,0.15);'"
>
<template v-slot:activator>
<v-list-item-content
v-if="item"
v-model="item.active"
@click="clickParentEvent(item)"
active-class="active"
>
<v-list-item-title v-text="item.title" />
</v-list-item-content>
</template>
<tree
:items="item.children"
:depth="depth+1"
@clickParentEvent="clickParentEvent"
@clickChildEvent="clickChildEvent"
/>
</v-list-group>
</v-list>
</div>
</template>
<script>
export default {
name: 'tree',
props: {
items: {
type: Array,
required: true,
},
depth: {
type: [String, Number],
default: 1
},
},
data: () => ({
active: [],
}),
methods: {
clickParentEvent(item){
this.$emit('clickParentEvent', item)
},
clickChildEvent(item){
this.$emit('clickChildEvent', item)
},
isActiveMenu(id) {
return this.$route.path.indexOf(id) > -1
},
},
mounted() {
},
}
</script>
This diff is collapsed.
......@@ -14,7 +14,7 @@
v-if="loading"
style="width:100%;"
></v-skeleton-loader>
<span v-if="!loading && guide !== null">
<span v-if="!loading && guide">
<v-breadcrumbs
class="ma-0 px-0 py-2 "
:items="getBreadcrumbs()"
......@@ -135,16 +135,16 @@
export default {
layout:'guide',
data:() => ({
guide:null,
loading: true,
guide: null,
loading: false,
newKeyword:'',
}),
methods:{
async getGuide(){
async getGuide(id){
this.loading = true
await this.$axios.get('http://docs.vridgeai.com/api',{
await this.$axios.get('http://localhost:5000/test',{
params:{
guideId: this.$store.state.guideId
guideId: id
}
})
.then(res=>{
......@@ -154,22 +154,22 @@ export default {
console.log(err)
})
await this.getGuideContents(this.guide.contentKey)
await this.getGuideContents()
this.loading = false
this.generateNavitator()
// this.generateNavitator()
},
async getGuideContents(contentKey) {
await this.$axios.get("https://api.github.com/repos/vazilcompany/vridge-docs/contents/guide/ko/" + contentKey,{
async getGuideContents() {
const contentKey = `${this.guide.path.substring(0,this.guide.path.length-1)}.md`
await this.$axios.get("https://api.github.com/repos/vazilcompany/vridge-docs/contents/guide/ko/" + contentKey, {
headers:{
Authorization: 'token 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'
}
})
.then(res=>{
let content = this.b64DecodeUnicode(res.data.content)
this.guide.content = content
this.parsingContentTitle(this.guide)
})
.catch(err=>{
this.guide.content = ''
......@@ -193,8 +193,9 @@ export default {
getBreadcrumbs(){
let items = []
let paths = this.$route.path.substring(1).split('_')
let paths = this.guide.path.substring(0, this.guide.path.length-1).split('/')
let pathTemp = ''
for(let i = 0; i < paths.length; i++) {
pathTemp += paths[i]
let item = {
......@@ -305,8 +306,7 @@ export default {
},
},
mounted(){
if(this.$store.state.guideId)
this.getGuide();
this.getGuide(this.$route.params.guide)
},
watch:{
}
......
......@@ -16,7 +16,7 @@
<v-treeview :items="guideIndex" item-text="title">
<template v-slot:label="{ item }">
<a @click="openGuide(item)">{{parsingContentTitle(item)}}</a>
<a @click="openGuide(item)">{{item.title}}</a>
</template>
</v-treeview>
</v-card>
......@@ -38,7 +38,7 @@ export default {
getGuideIndex() {
this.$store.state.guideId = null
this.$store.commit('setGuide', {guideId:null, flag:false})
this.$axios.get('http://docs.vridgeai.com/api/index')
this.$axios.get('http://localhost:5000/test/index')
.then(res => {
this.guideIndex = res.data
})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment