From 1c00c86bc7c232da193a843345a32cc83893ae68 Mon Sep 17 00:00:00 2001
From: Tim <135014430+nagisa77@users.noreply.github.com>
Date: Sun, 6 Jul 2025 01:56:35 +0800
Subject: [PATCH] feat: add draft post support
---
open-isle-cli/src/views/NewPostPageView.vue | 53 ++++++++++++++-
.../openisle/controller/DraftController.java | 67 +++++++++++++++++++
.../openisle/controller/PostController.java | 3 +
src/main/java/com/openisle/model/Draft.java | 41 ++++++++++++
.../openisle/repository/DraftRepository.java | 12 ++++
.../com/openisle/service/DraftService.java | 58 ++++++++++++++++
6 files changed, 232 insertions(+), 2 deletions(-)
create mode 100644 src/main/java/com/openisle/controller/DraftController.java
create mode 100644 src/main/java/com/openisle/model/Draft.java
create mode 100644 src/main/java/com/openisle/repository/DraftRepository.java
create mode 100644 src/main/java/com/openisle/service/DraftService.java
diff --git a/open-isle-cli/src/views/NewPostPageView.vue b/open-isle-cli/src/views/NewPostPageView.vue
index b78268ef4..27984844f 100644
--- a/open-isle-cli/src/views/NewPostPageView.vue
+++ b/open-isle-cli/src/views/NewPostPageView.vue
@@ -20,7 +20,7 @@
diff --git a/src/main/java/com/openisle/controller/DraftController.java b/src/main/java/com/openisle/controller/DraftController.java
new file mode 100644
index 000000000..b47fa6ff2
--- /dev/null
+++ b/src/main/java/com/openisle/controller/DraftController.java
@@ -0,0 +1,67 @@
+package com.openisle.controller;
+
+import com.openisle.model.Draft;
+import com.openisle.service.DraftService;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/api/drafts")
+@RequiredArgsConstructor
+public class DraftController {
+ private final DraftService draftService;
+
+ @PostMapping
+ public ResponseEntity saveDraft(@RequestBody DraftRequest req, Authentication auth) {
+ Draft draft = draftService.saveDraft(auth.getName(), req.getCategoryId(), req.getTitle(), req.getContent(), req.getTagIds());
+ return ResponseEntity.ok(toDto(draft));
+ }
+
+ @GetMapping("/me")
+ public ResponseEntity getMyDraft(Authentication auth) {
+ return draftService.getDraft(auth.getName())
+ .map(d -> ResponseEntity.ok(toDto(d)))
+ .orElseGet(() -> ResponseEntity.noContent().build());
+ }
+
+ @DeleteMapping("/me")
+ public ResponseEntity> deleteMyDraft(Authentication auth) {
+ draftService.deleteDraft(auth.getName());
+ return ResponseEntity.ok().build();
+ }
+
+ private DraftDto toDto(Draft draft) {
+ DraftDto dto = new DraftDto();
+ dto.setId(draft.getId());
+ dto.setTitle(draft.getTitle());
+ dto.setContent(draft.getContent());
+ if (draft.getCategory() != null) {
+ dto.setCategoryId(draft.getCategory().getId());
+ }
+ dto.setTagIds(draft.getTags().stream().map(com.openisle.model.Tag::getId).collect(Collectors.toList()));
+ return dto;
+ }
+
+ @Data
+ private static class DraftRequest {
+ private String title;
+ private String content;
+ private Long categoryId;
+ private List tagIds;
+ }
+
+ @Data
+ private static class DraftDto {
+ private Long id;
+ private String title;
+ private String content;
+ private Long categoryId;
+ private List tagIds;
+ }
+}
diff --git a/src/main/java/com/openisle/controller/PostController.java b/src/main/java/com/openisle/controller/PostController.java
index 2450b7139..a140ae357 100644
--- a/src/main/java/com/openisle/controller/PostController.java
+++ b/src/main/java/com/openisle/controller/PostController.java
@@ -7,6 +7,7 @@ import com.openisle.service.CommentService;
import com.openisle.service.PostService;
import com.openisle.service.ReactionService;
import com.openisle.service.CaptchaService;
+import com.openisle.service.DraftService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
@@ -26,6 +27,7 @@ public class PostController {
private final CommentService commentService;
private final ReactionService reactionService;
private final CaptchaService captchaService;
+ private final DraftService draftService;
@Value("${app.captcha.enabled:false}")
private boolean captchaEnabled;
@@ -40,6 +42,7 @@ public class PostController {
}
Post post = postService.createPost(auth.getName(), req.getCategoryId(),
req.getTitle(), req.getContent(), req.getTagIds());
+ draftService.deleteDraft(auth.getName());
return ResponseEntity.ok(toDto(post));
}
diff --git a/src/main/java/com/openisle/model/Draft.java b/src/main/java/com/openisle/model/Draft.java
new file mode 100644
index 000000000..32d46fd11
--- /dev/null
+++ b/src/main/java/com/openisle/model/Draft.java
@@ -0,0 +1,41 @@
+package com.openisle.model;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+@Getter
+@Setter
+@NoArgsConstructor
+@Table(name = "drafts", uniqueConstraints = {
+ @UniqueConstraint(columnNames = {"author_id"})
+})
+public class Draft {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ private String title;
+
+ @Column(columnDefinition = "TEXT")
+ private String content;
+
+ @ManyToOne(fetch = FetchType.LAZY, optional = false)
+ @JoinColumn(name = "author_id")
+ private User author;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "category_id")
+ private Category category;
+
+ @ManyToMany(fetch = FetchType.LAZY)
+ @JoinTable(name = "draft_tags",
+ joinColumns = @JoinColumn(name = "draft_id"),
+ inverseJoinColumns = @JoinColumn(name = "tag_id"))
+ private Set tags = new HashSet<>();
+}
diff --git a/src/main/java/com/openisle/repository/DraftRepository.java b/src/main/java/com/openisle/repository/DraftRepository.java
new file mode 100644
index 000000000..536201796
--- /dev/null
+++ b/src/main/java/com/openisle/repository/DraftRepository.java
@@ -0,0 +1,12 @@
+package com.openisle.repository;
+
+import com.openisle.model.Draft;
+import com.openisle.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface DraftRepository extends JpaRepository {
+ Optional findByAuthor(User author);
+ void deleteByAuthor(User author);
+}
diff --git a/src/main/java/com/openisle/service/DraftService.java b/src/main/java/com/openisle/service/DraftService.java
new file mode 100644
index 000000000..804660724
--- /dev/null
+++ b/src/main/java/com/openisle/service/DraftService.java
@@ -0,0 +1,58 @@
+package com.openisle.service;
+
+import com.openisle.model.Category;
+import com.openisle.model.Draft;
+import com.openisle.model.Tag;
+import com.openisle.model.User;
+import com.openisle.repository.CategoryRepository;
+import com.openisle.repository.DraftRepository;
+import com.openisle.repository.TagRepository;
+import com.openisle.repository.UserRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+@Service
+@RequiredArgsConstructor
+public class DraftService {
+ private final DraftRepository draftRepository;
+ private final UserRepository userRepository;
+ private final CategoryRepository categoryRepository;
+ private final TagRepository tagRepository;
+
+ public Draft saveDraft(String username, Long categoryId, String title, String content, List tagIds) {
+ User user = userRepository.findByUsername(username)
+ .orElseThrow(() -> new IllegalArgumentException("User not found"));
+ Draft draft = draftRepository.findByAuthor(user).orElse(new Draft());
+ draft.setAuthor(user);
+ draft.setTitle(title);
+ draft.setContent(content);
+ if (categoryId != null) {
+ Category category = categoryRepository.findById(categoryId)
+ .orElseThrow(() -> new IllegalArgumentException("Category not found"));
+ draft.setCategory(category);
+ } else {
+ draft.setCategory(null);
+ }
+ Set tags = new HashSet<>();
+ if (tagIds != null && !tagIds.isEmpty()) {
+ tags.addAll(tagRepository.findAllById(tagIds));
+ }
+ draft.setTags(tags);
+ return draftRepository.save(draft);
+ }
+
+ public Optional getDraft(String username) {
+ return userRepository.findByUsername(username)
+ .flatMap(draftRepository::findByAuthor);
+ }
+
+ public void deleteDraft(String username) {
+ userRepository.findByUsername(username)
+ .ifPresent(draftRepository::deleteByAuthor);
+ }
+}