mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-21 14:30:59 +08:00
fix: 分类提案简化用户输入
This commit is contained in:
@@ -75,10 +75,7 @@ public class PostController {
|
||||
req.getOptions(),
|
||||
req.getMultiple(),
|
||||
req.getProposedName(),
|
||||
req.getProposedSlug(),
|
||||
req.getProposalDescription(),
|
||||
req.getApproveThreshold(),
|
||||
req.getQuorum()
|
||||
req.getProposalDescription()
|
||||
);
|
||||
draftService.deleteDraft(auth.getName());
|
||||
PostDetailDto dto = postMapper.toDetailDto(post, auth.getName());
|
||||
|
||||
@@ -31,8 +31,5 @@ public class PostRequest {
|
||||
|
||||
// fields for category proposal posts
|
||||
private String proposedName;
|
||||
private String proposedSlug;
|
||||
private String proposalDescription;
|
||||
private Integer approveThreshold;
|
||||
private Integer quorum;
|
||||
}
|
||||
|
||||
@@ -7,59 +7,53 @@ import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* A specialized post type used for proposing new categories.
|
||||
* It reuses poll mechanics (participants, votes, endTime) by extending PollPost.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "category_proposal_posts", indexes = {
|
||||
@Index(name = "idx_category_proposal_posts_status", columnList = "status"),
|
||||
@Index(name = "idx_category_proposal_posts_slug", columnList = "proposed_slug", unique = true)
|
||||
})
|
||||
@Table(
|
||||
name = "category_proposal_posts",
|
||||
indexes = { @Index(name = "idx_category_proposal_posts_status", columnList = "status") }
|
||||
)
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@PrimaryKeyJoinColumn(name = "post_id")
|
||||
public class CategoryProposalPost extends PollPost {
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false)
|
||||
private CategoryProposalStatus proposalStatus = CategoryProposalStatus.PENDING;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false)
|
||||
private CategoryProposalStatus proposalStatus = CategoryProposalStatus.PENDING;
|
||||
|
||||
@Column(name = "proposed_name", nullable = false)
|
||||
private String proposedName;
|
||||
@Column(name = "proposed_name", nullable = false, unique = true)
|
||||
private String proposedName;
|
||||
|
||||
@Column(name = "proposed_slug", nullable = false, unique = true)
|
||||
private String proposedSlug;
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
// Approval threshold as percentage (0-100), default 60
|
||||
@Column(name = "approve_threshold", nullable = false)
|
||||
private int approveThreshold = 60;
|
||||
|
||||
// Approval threshold as percentage (0-100), default 60
|
||||
@Column(name = "approve_threshold", nullable = false)
|
||||
private int approveThreshold = 60;
|
||||
// Minimum number of participants required to meet quorum
|
||||
@Column(name = "quorum", nullable = false)
|
||||
private int quorum = 10;
|
||||
|
||||
// Minimum number of participants required to meet quorum
|
||||
@Column(name = "quorum", nullable = false)
|
||||
private int quorum = 10;
|
||||
// Optional voting start time (end time inherited from PollPost)
|
||||
@Column(name = "start_at")
|
||||
private LocalDateTime startAt;
|
||||
|
||||
// Optional voting start time (end time inherited from PollPost)
|
||||
@Column(name = "start_at")
|
||||
private LocalDateTime startAt;
|
||||
// Snapshot of poll results at finalization (e.g., JSON)
|
||||
@Column(name = "result_snapshot", columnDefinition = "TEXT")
|
||||
private String resultSnapshot;
|
||||
|
||||
// Snapshot of poll results at finalization (e.g., JSON)
|
||||
@Column(name = "result_snapshot", columnDefinition = "TEXT")
|
||||
private String resultSnapshot;
|
||||
|
||||
// Reason when proposal is rejected
|
||||
@Column(name = "reject_reason")
|
||||
private String rejectReason;
|
||||
// Reason when proposal is rejected
|
||||
@Column(name = "reject_reason")
|
||||
private String rejectReason;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,17 +2,18 @@ package com.openisle.repository;
|
||||
|
||||
import com.openisle.model.CategoryProposalPost;
|
||||
import com.openisle.model.CategoryProposalStatus;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface CategoryProposalPostRepository extends JpaRepository<CategoryProposalPost, Long> {
|
||||
List<CategoryProposalPost> findByEndTimeAfterAndProposalStatus(LocalDateTime now, CategoryProposalStatus status);
|
||||
List<CategoryProposalPost> findByEndTimeBeforeAndProposalStatus(LocalDateTime now, CategoryProposalStatus status);
|
||||
boolean existsByProposedSlug(String proposedSlug);
|
||||
List<CategoryProposalPost> findByEndTimeAfterAndProposalStatus(
|
||||
LocalDateTime now,
|
||||
CategoryProposalStatus status
|
||||
);
|
||||
List<CategoryProposalPost> findByEndTimeBeforeAndProposalStatus(
|
||||
LocalDateTime now,
|
||||
CategoryProposalStatus status
|
||||
);
|
||||
boolean existsByProposedNameIgnoreCase(String proposedName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +75,11 @@ public class PostService {
|
||||
|
||||
private final SearchIndexEventPublisher searchIndexEventPublisher;
|
||||
|
||||
private static final int DEFAULT_PROPOSAL_APPROVE_THRESHOLD = 60;
|
||||
private static final int DEFAULT_PROPOSAL_QUORUM = 10;
|
||||
private static final long DEFAULT_PROPOSAL_DURATION_DAYS = 3;
|
||||
private static final List<String> DEFAULT_PROPOSAL_OPTIONS = List.of("同意", "反对");
|
||||
|
||||
@Value("${app.website-url:https://www.open-isle.com}")
|
||||
private String websiteUrl;
|
||||
|
||||
@@ -253,10 +258,7 @@ public class PostService {
|
||||
java.util.List<String> options,
|
||||
Boolean multiple,
|
||||
String proposedName,
|
||||
String proposedSlug,
|
||||
String proposalDescription,
|
||||
Integer approveThreshold,
|
||||
Integer quorum
|
||||
String proposalDescription
|
||||
) {
|
||||
// 限制访问次数
|
||||
boolean limitResult = postRateLimit(username);
|
||||
@@ -307,35 +309,18 @@ public class PostService {
|
||||
if (proposedName == null || proposedName.isBlank()) {
|
||||
throw new IllegalArgumentException("Proposed name required");
|
||||
}
|
||||
if (proposedSlug == null || proposedSlug.isBlank()) {
|
||||
throw new IllegalArgumentException("Proposed slug required");
|
||||
String normalizedName = proposedName.trim();
|
||||
if (categoryProposalPostRepository.existsByProposedNameIgnoreCase(normalizedName)) {
|
||||
throw new IllegalArgumentException("Proposed name already exists: " + normalizedName);
|
||||
}
|
||||
if (categoryProposalPostRepository.existsByProposedSlug(proposedSlug)) {
|
||||
throw new IllegalArgumentException("Proposed slug already exists: " + proposedSlug);
|
||||
}
|
||||
cp.setProposedName(proposedName);
|
||||
cp.setProposedSlug(proposedSlug);
|
||||
cp.setProposedName(normalizedName);
|
||||
cp.setDescription(proposalDescription);
|
||||
if (approveThreshold != null) {
|
||||
if (approveThreshold < 0 || approveThreshold > 100) {
|
||||
throw new IllegalArgumentException("approveThreshold must be between 0 and 100");
|
||||
}
|
||||
cp.setApproveThreshold(approveThreshold);
|
||||
}
|
||||
if (quorum != null) {
|
||||
if (quorum < 0) {
|
||||
throw new IllegalArgumentException("quorum must be >= 0");
|
||||
}
|
||||
cp.setQuorum(quorum);
|
||||
}
|
||||
cp.setStartAt(startTime);
|
||||
cp.setEndTime(endTime);
|
||||
// default yes/no options if not provided
|
||||
if (options == null || options.size() < 2) {
|
||||
cp.setOptions(List.of("同意", "反对"));
|
||||
} else {
|
||||
cp.setOptions(options);
|
||||
}
|
||||
cp.setApproveThreshold(DEFAULT_PROPOSAL_APPROVE_THRESHOLD);
|
||||
cp.setQuorum(DEFAULT_PROPOSAL_QUORUM);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
cp.setStartAt(now);
|
||||
cp.setEndTime(now.plusDays(DEFAULT_PROPOSAL_DURATION_DAYS));
|
||||
cp.setOptions(DEFAULT_PROPOSAL_OPTIONS);
|
||||
cp.setMultiple(false);
|
||||
post = cp;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user