feat: Chatgpt

This commit is contained in:
irony 2025-12-06 19:46:34 +08:00
parent 844dbba527
commit e4c6b14576
4 changed files with 81 additions and 31 deletions

View File

@ -1,6 +1,5 @@
package com.novelreader.config;
import com.novelreader.model.ChapterDO;
import com.novelreader.model.NovelDO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -34,6 +33,9 @@ public class MongoIndexConfig {
// Novel集合索引
createNovelIndexes();
// History集合索引
createHistoryIndexes();
log.info("MongoDB索引检查完成");
} catch (Exception e) {
log.error("创建索引失败", e);
@ -43,7 +45,7 @@ public class MongoIndexConfig {
private void createChapterIndexes() {
IndexOperations indexOps = mongoTemplate.indexOps("chapterDO");
// 复合索引: novelId + index (最重要的索引用于章节查询和排序)
// 复合索引: novelId index (最重要的索引用于章节查询和排序)
indexOps.ensureIndex(new Index()
.on("novelId", Sort.Direction.ASC)
.on("index", Sort.Direction.ASC)
@ -76,5 +78,26 @@ public class MongoIndexConfig {
log.info("Novel索引已确保: idx_name, idx_author, idx_tags");
}
private void createHistoryIndexes() {
IndexOperations indexOps = mongoTemplate.indexOps("history");
// 唯一索引: userId novelId防止重复历史记录
indexOps.ensureIndex(new Index()
.on("userId", Sort.Direction.ASC)
.on("novelId", Sort.Direction.ASC)
.unique()
.named("ux_user_novel")
.background());
// 辅助索引按时间排序
indexOps.ensureIndex(new Index()
.on("userId", Sort.Direction.ASC)
.on("lastReadAt", Sort.Direction.DESC)
.named("idx_user_lastReadAt")
.background());
log.info("History索引已确保: ux_user_novel, idx_user_lastReadAt");
}
}

View File

@ -6,6 +6,11 @@ import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.time.Instant;
@ -16,6 +21,7 @@ import java.util.Optional;
public class HistoryService {
private final HistoryRepository historyRepository;
private final MongoTemplate mongoTemplate;
private static final String DEFAULT_USER_ID = "default_user";
public Page<History> getHistory(String userId, int page, int size) {
@ -32,16 +38,20 @@ public class HistoryService {
public History updateHistory(String userId, String novelId, String chapterId, Integer position) {
String uid = userId != null ? userId : DEFAULT_USER_ID;
Optional<History> existing = historyRepository.findByUserIdAndNovelId(uid, novelId);
Query query = new Query(Criteria.where("userId").is(uid).and("novelId").is(novelId));
Update update = new Update()
.set("userId", uid)
.set("novelId", novelId)
.set("chapterId", chapterId)
.set("position", position != null ? position : 0)
.set("lastReadAt", Instant.now());
History history = existing.orElse(new History());
history.setUserId(uid);
history.setNovelId(novelId);
history.setChapterId(chapterId);
history.setPosition(position != null ? position : 0);
history.setLastReadAt(Instant.now());
return historyRepository.save(history);
return mongoTemplate.findAndModify(
query,
update,
FindAndModifyOptions.options().returnNew(true).upsert(true),
History.class
);
}
public void deleteHistory(String userId, String novelId) {

View File

@ -13,8 +13,17 @@ const App = (function() {
handleRoute();
}
// 记录上一个路由,用于返回
let lastHash = '#/shelf';
let lastNonReadHash = '#/shelf';
function handleRoute() {
const hash = window.location.hash || '#/shelf';
const prevHash = lastHash;
lastHash = hash;
if (!hash.includes('#/read/')) {
lastNonReadHash = hash;
}
const [path, ...params] = hash.slice(2).split('/');
// 更新导航状态
@ -52,6 +61,9 @@ const App = (function() {
case 'read':
if (params[0]) {
const chapterIndex = parseInt(params[1]) || 0;
// 记录进入阅读页前的路由,用于返回时回到详情页或来源页
const backRoute = prevHash && !prevHash.includes('#/read/') ? prevHash : lastNonReadHash;
Store.setState('prevRouteBeforeRead', backRoute || '#/shelf');
showPage('reader');
Reader.init(params[0], chapterIndex);
}

View File

@ -507,12 +507,17 @@ const Reader = (function() {
// 解绑事件
unbindEvents();
// 返回详情页showPage会处理导航栏显示状态
if (currentNovelId) {
window.location.hash = '#/novel/' + currentNovelId;
// 使用记录的前一页面替换当前阅读页,避免回退再次进入阅读
const prevRoute = Store.getState('prevRouteBeforeRead');
if (prevRoute) {
window.location.replace(prevRoute);
} else if (currentNovelId) {
window.location.replace('#/novel/' + currentNovelId);
} else {
window.location.hash = '#/shelf';
window.location.replace('#/shelf');
}
// 清理记录
Store.setState('prevRouteBeforeRead', null);
}
function scrollToTop() {