make series entries number double

This commit is contained in:
Stefan Hardegger
2026-07-20 11:27:58 +02:00
parent 665de12611
commit 362f6a9988
19 changed files with 65 additions and 63 deletions

View File

@@ -837,7 +837,7 @@ public class StoryController {
@RequestParam(required = false) String authorName,
@RequestParam(required = false) UUID seriesId,
@RequestParam(required = false) String seriesName,
@RequestParam(required = false) Integer seriesVolume,
@RequestParam(required = false) Double seriesVolume,
@RequestParam(required = false) List<String> tags,
@RequestParam(defaultValue = "true") Boolean preserveReadingPosition,
@RequestParam(defaultValue = "false") Boolean overwriteExisting,
@@ -948,7 +948,7 @@ public class StoryController {
@RequestParam(required = false) String authorName,
@RequestParam(required = false) UUID seriesId,
@RequestParam(required = false) String seriesName,
@RequestParam(required = false) Integer seriesVolume,
@RequestParam(required = false) Double seriesVolume,
@RequestParam(required = false) List<String> tags,
@RequestParam(defaultValue = "true") Boolean createMissingAuthor,
@RequestParam(defaultValue = "true") Boolean createMissingSeries,
@@ -1082,7 +1082,7 @@ public class StoryController {
private String description;
private String contentHtml;
private String sourceUrl;
private Integer volume;
private Double volume;
private UUID authorId;
private String authorName;
private UUID seriesId;
@@ -1100,8 +1100,8 @@ public class StoryController {
public void setContentHtml(String contentHtml) { this.contentHtml = contentHtml; }
public String getSourceUrl() { return sourceUrl; }
public void setSourceUrl(String sourceUrl) { this.sourceUrl = sourceUrl; }
public Integer getVolume() { return volume; }
public void setVolume(Integer volume) { this.volume = volume; }
public Double getVolume() { return volume; }
public void setVolume(Double volume) { this.volume = volume; }
public UUID getAuthorId() { return authorId; }
public void setAuthorId(UUID authorId) { this.authorId = authorId; }
public String getAuthorName() { return authorName; }
@@ -1120,7 +1120,7 @@ public class StoryController {
private String description;
private String contentHtml;
private String sourceUrl;
private Integer volume;
private Double volume;
private UUID authorId;
private String authorName;
private UUID seriesId;
@@ -1138,8 +1138,8 @@ public class StoryController {
public void setContentHtml(String contentHtml) { this.contentHtml = contentHtml; }
public String getSourceUrl() { return sourceUrl; }
public void setSourceUrl(String sourceUrl) { this.sourceUrl = sourceUrl; }
public Integer getVolume() { return volume; }
public void setVolume(Integer volume) { this.volume = volume; }
public Double getVolume() { return volume; }
public void setVolume(Double volume) { this.volume = volume; }
public UUID getAuthorId() { return authorId; }
public void setAuthorId(UUID authorId) { this.authorId = authorId; }
public String getAuthorName() { return authorName; }

View File

@@ -19,7 +19,7 @@ public class EPUBImportRequest {
private String seriesName;
private Integer seriesVolume;
private Double seriesVolume;
private List<String> tags;
@@ -75,11 +75,11 @@ public class EPUBImportRequest {
this.seriesName = seriesName;
}
public Integer getSeriesVolume() {
public Double getSeriesVolume() {
return seriesVolume;
}
public void setSeriesVolume(Integer seriesVolume) {
public void setSeriesVolume(Double seriesVolume) {
this.seriesVolume = seriesVolume;
}

View File

@@ -19,7 +19,7 @@ public class PDFImportRequest {
private String seriesName;
private Integer seriesVolume;
private Double seriesVolume;
private List<String> tags;
@@ -71,11 +71,11 @@ public class PDFImportRequest {
this.seriesName = seriesName;
}
public Integer getSeriesVolume() {
public Double getSeriesVolume() {
return seriesVolume;
}
public void setSeriesVolume(Integer seriesVolume) {
public void setSeriesVolume(Double seriesVolume) {
this.seriesVolume = seriesVolume;
}

View File

@@ -26,7 +26,7 @@ public class StoryDto {
private String coverPath;
private Integer wordCount;
private Integer rating;
private Integer volume;
private Double volume;
// Reading progress fields
private Boolean isRead;
@@ -125,11 +125,11 @@ public class StoryDto {
this.rating = rating;
}
public Integer getVolume() {
public Double getVolume() {
return volume;
}
public void setVolume(Integer volume) {
public void setVolume(Double volume) {
this.volume = volume;
}

View File

@@ -20,7 +20,7 @@ public class StoryReadingDto {
private String coverPath;
private Integer wordCount;
private Integer rating;
private Integer volume;
private Double volume;
// Reading progress fields
private Boolean isRead;
@@ -114,11 +114,11 @@ public class StoryReadingDto {
this.rating = rating;
}
public Integer getVolume() {
public Double getVolume() {
return volume;
}
public void setVolume(Integer volume) {
public void setVolume(Double volume) {
this.volume = volume;
}

View File

@@ -13,7 +13,7 @@ public class StorySearchDto {
private String coverPath;
private Integer wordCount;
private Integer rating;
private Integer volume;
private Double volume;
// Reading status
private Boolean isRead;
@@ -103,11 +103,11 @@ public class StorySearchDto {
this.rating = rating;
}
public Integer getVolume() {
public Double getVolume() {
return volume;
}
public void setVolume(Integer volume) {
public void setVolume(Double volume) {
this.volume = volume;
}

View File

@@ -18,7 +18,7 @@ public class StorySummaryDto {
private String coverPath;
private Integer wordCount;
private Integer rating;
private Integer volume;
private Double volume;
// Reading progress fields
private Boolean isRead;
@@ -105,11 +105,11 @@ public class StorySummaryDto {
this.rating = rating;
}
public Integer getVolume() {
public Double getVolume() {
return volume;
}
public void setVolume(Integer volume) {
public void setVolume(Double volume) {
this.volume = volume;
}

View File

@@ -35,7 +35,7 @@ public class ZIPImportRequest {
private String authorName;
private UUID seriesId;
private String seriesName;
private Integer seriesVolume;
private Double seriesVolume;
private List<String> tags;
public UUID getAuthorId() {
@@ -70,11 +70,11 @@ public class ZIPImportRequest {
this.seriesName = seriesName;
}
public Integer getSeriesVolume() {
public Double getSeriesVolume() {
return seriesVolume;
}
public void setSeriesVolume(Integer seriesVolume) {
public void setSeriesVolume(Double seriesVolume) {
this.seriesVolume = seriesVolume;
}

View File

@@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonManagedReference;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
@@ -62,21 +63,21 @@ public class Series {
public Story getNextStory(Story currentStory) {
if (currentStory.getVolume() == null) return null;
return stories.stream()
.filter(story -> story.getVolume() != null)
.filter(story -> story.getVolume().equals(currentStory.getVolume() + 1))
.findFirst()
.filter(story -> story.getVolume() > currentStory.getVolume())
.min(Comparator.comparingDouble(Story::getVolume))
.orElse(null);
}
public Story getPreviousStory(Story currentStory) {
if (currentStory.getVolume() == null || currentStory.getVolume() <= 1) return null;
if (currentStory.getVolume() == null) return null;
return stories.stream()
.filter(story -> story.getVolume() != null)
.filter(story -> story.getVolume().equals(currentStory.getVolume() - 1))
.findFirst()
.filter(story -> story.getVolume() < currentStory.getVolume())
.max(Comparator.comparingDouble(Story::getVolume))
.orElse(null);
}

View File

@@ -52,8 +52,8 @@ public class Story {
@Column(name = "rating")
private Integer rating;
@Column(name = "volume")
private Integer volume;
@Column(name = "volume", columnDefinition = "float8")
private Double volume;
@Column(name = "is_read")
private Boolean isRead = false;
@@ -216,11 +216,11 @@ public class Story {
this.rating = rating;
}
public Integer getVolume() {
public Double getVolume() {
return volume;
}
public void setVolume(Integer volume) {
public void setVolume(Double volume) {
this.volume = volume;
}

View File

@@ -37,7 +37,7 @@ public interface StoryRepository extends JpaRepository<Story, UUID> {
List<Story> findBySeriesOrderByVolume(@Param("seriesId") UUID seriesId);
@Query("SELECT s FROM Story s WHERE s.series.id = :seriesId AND s.volume = :volume")
Optional<Story> findBySeriesAndVolume(@Param("seriesId") UUID seriesId, @Param("volume") Integer volume);
Optional<Story> findBySeriesAndVolume(@Param("seriesId") UUID seriesId, @Param("volume") Double volume);
@Query("SELECT s FROM Story s JOIN s.tags t WHERE t = :tag")

View File

@@ -420,7 +420,7 @@ public class CollectionService {
.orElseThrow(() -> new ResourceNotFoundException("Series not found with id: " + seriesId));
List<UUID> storyIds = series.getStories().stream()
.sorted(java.util.Comparator.comparingInt(s -> s.getVolume() != null ? s.getVolume() : Integer.MAX_VALUE))
.sorted(java.util.Comparator.comparingDouble((Story s) -> s.getVolume() != null ? s.getVolume() : Double.MAX_VALUE))
.map(Story::getId)
.collect(Collectors.toList());

View File

@@ -1061,7 +1061,8 @@ public class SolrService {
story.setCoverPath((String) doc.getFieldValue("coverPath"));
story.setWordCount((Integer) doc.getFieldValue("wordCount"));
story.setRating((Integer) doc.getFieldValue("rating"));
story.setVolume((Integer) doc.getFieldValue("volume"));
Number volumeVal = (Number) doc.getFieldValue("volume");
story.setVolume(volumeVal != null ? volumeVal.doubleValue() : null);
story.setIsRead((Boolean) doc.getFieldValue("isRead"));
story.setReadingPosition((Integer) doc.getFieldValue("readingPosition"));
story.setReadingProgressPercentage((Integer) doc.getFieldValue("readingProgressPercentage"));

View File

@@ -139,7 +139,7 @@ public class StoryService {
}
@Transactional(readOnly = true)
public Optional<Story> findBySeriesAndVolume(UUID seriesId, Integer volume) {
public Optional<Story> findBySeriesAndVolume(UUID seriesId, Double volume) {
return storyRepository.findBySeriesAndVolume(seriesId, volume);
}
@@ -499,7 +499,7 @@ public class StoryService {
return storyRepository.save(story);
}
public Story addToSeries(UUID storyId, UUID seriesId, Integer volume) {
public Story addToSeries(UUID storyId, UUID seriesId, Double volume) {
Story story = findById(storyId);
Series series = seriesService.findById(seriesId);
@@ -550,7 +550,7 @@ public class StoryService {
}
}
private void validateSeriesVolume(Series series, Integer volume) {
private void validateSeriesVolume(Series series, Double volume) {
if (volume != null) {
Optional<Story> existingPart = storyRepository.findBySeriesAndVolume(series.getId(), volume);
if (existingPart.isPresent()) {

View File

@@ -104,11 +104,11 @@ class SeriesTest {
@DisplayName("Should get next story correctly")
void shouldGetNextStoryCorrectly() {
Story story1 = new Story("Part 1");
story1.setVolume(1);
story1.setVolume(1.0);
Story story2 = new Story("Part 2");
story2.setVolume(2);
story2.setVolume(2.0);
Story story3 = new Story("Part 3");
story3.setVolume(3);
story3.setVolume(3.0);
series.addStory(story1);
series.addStory(story2);
@@ -123,11 +123,11 @@ class SeriesTest {
@DisplayName("Should get previous story correctly")
void shouldGetPreviousStoryCorrectly() {
Story story1 = new Story("Part 1");
story1.setVolume(1);
story1.setVolume(1.0);
Story story2 = new Story("Part 2");
story2.setVolume(2);
story2.setVolume(2.0);
Story story3 = new Story("Part 3");
story3.setVolume(3);
story3.setVolume(3.0);
series.addStory(story1);
series.addStory(story2);

View File

@@ -140,7 +140,7 @@ class StoryTest {
story.setSeries(series);
assertFalse(story.isPartOfSeries()); // Still false because no volume
story.setVolume(1);
story.setVolume(1.0);
assertTrue(story.isPartOfSeries());
story.setSeries(null);

View File

@@ -69,14 +69,14 @@ class StoryRepositoryTest extends BaseRepositoryTest {
story2.setDescription("The sequel to the great adventure");
story2.setAuthor(author);
story2.setSeries(series);
story2.setVolume(1);
story2.setVolume(1.0);
story2.addTag(tag1);
story3 = new Story("The Final Chapter");
story3.setDescription("The final chapter");
story3.setAuthor(author);
story3.setSeries(series);
story3.setVolume(2);
story3.setVolume(2.0);
storyRepository.saveAll(List.of(story1, story2, story3));
}
@@ -126,11 +126,11 @@ class StoryRepositoryTest extends BaseRepositoryTest {
@Test
@DisplayName("Should find story by series and volume")
void shouldFindStoryBySeriesAndVolume() {
Optional<Story> found = storyRepository.findBySeriesAndVolume(series.getId(), 1);
Optional<Story> found = storyRepository.findBySeriesAndVolume(series.getId(), 1.0);
assertTrue(found.isPresent());
assertEquals("The Sequel", found.get().getTitle());
found = storyRepository.findBySeriesAndVolume(series.getId(), 99);
found = storyRepository.findBySeriesAndVolume(series.getId(), 99.0);
assertFalse(found.isPresent());
}

View File

@@ -285,7 +285,7 @@ export default function AddStoryContent() {
summary: formData.summary || undefined,
contentHtml: formData.contentHtml,
sourceUrl: formData.sourceUrl || undefined,
volume: formData.seriesName ? parseInt(formData.volume) : undefined,
volume: formData.seriesName ? parseFloat(formData.volume) : undefined,
// Send seriesId if we have it (existing series), otherwise send seriesName (new series)
...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName || undefined }),
// Send authorId if we have it (existing author), otherwise send authorName (new author)
@@ -308,7 +308,7 @@ export default function AddStoryContent() {
summary: formData.summary || undefined,
contentHtml: imageResult.processedContent,
sourceUrl: formData.sourceUrl || undefined,
volume: formData.seriesName ? parseInt(formData.volume) : undefined,
volume: formData.seriesName ? parseFloat(formData.volume) : undefined,
...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName || undefined }),
...(formData.authorId ? { authorId: formData.authorId } : { authorName: formData.authorName }),
tagNames: formData.tags.length > 0 ? formData.tags : undefined,

View File

@@ -199,7 +199,7 @@ export default function EditStoryPage() {
summary: formData.summary || undefined,
contentHtml: formData.contentHtml,
sourceUrl: formData.sourceUrl || undefined,
volume: formData.seriesName && formData.volume ? parseInt(formData.volume) : undefined,
volume: formData.seriesName && formData.volume ? parseFloat(formData.volume) : undefined,
// Send seriesId if we have it (existing series), otherwise send seriesName (new/changed series)
...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName }),
// Send authorId if we have it (existing author), otherwise send authorName (new/changed author)
@@ -222,7 +222,7 @@ export default function EditStoryPage() {
summary: formData.summary || undefined,
contentHtml: imageResult.processedContent,
sourceUrl: formData.sourceUrl || undefined,
volume: formData.seriesName && formData.volume ? parseInt(formData.volume) : undefined,
volume: formData.seriesName && formData.volume ? parseFloat(formData.volume) : undefined,
...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName }),
...(formData.authorId ? { authorId: formData.authorId } : { authorName: formData.authorName }),
tagNames: formData.tags,