You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
312 lines
9.8 KiB
312 lines
9.8 KiB
import BookmarkModel from "db/models/BookmarkModel.js"
|
|
import CommonError from "utils/error/CommonError.js"
|
|
|
|
class BookmarkService {
|
|
// 获取用户的所有书签
|
|
async getUserBookmarks(userId) {
|
|
try {
|
|
if (!userId) {
|
|
throw new CommonError("用户ID不能为空")
|
|
}
|
|
return await BookmarkModel.findAllByUser(userId)
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`获取用户书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 根据ID获取书签
|
|
async getBookmarkById(id) {
|
|
try {
|
|
if (!id) {
|
|
throw new CommonError("书签ID不能为空")
|
|
}
|
|
const bookmark = await BookmarkModel.findById(id)
|
|
if (!bookmark) {
|
|
throw new CommonError("书签不存在")
|
|
}
|
|
return bookmark
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`获取书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 创建书签
|
|
async createBookmark(data) {
|
|
try {
|
|
if (!data.user_id || !data.url) {
|
|
throw new CommonError("用户ID和URL为必填字段")
|
|
}
|
|
|
|
// 验证URL格式
|
|
if (!this.isValidUrl(data.url)) {
|
|
throw new CommonError("URL格式不正确")
|
|
}
|
|
|
|
return await BookmarkModel.create(data)
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`创建书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 更新书签
|
|
async updateBookmark(id, data) {
|
|
try {
|
|
if (!id) {
|
|
throw new CommonError("书签ID不能为空")
|
|
}
|
|
|
|
const bookmark = await BookmarkModel.findById(id)
|
|
if (!bookmark) {
|
|
throw new CommonError("书签不存在")
|
|
}
|
|
|
|
// 如果更新URL,验证格式
|
|
if (data.url && !this.isValidUrl(data.url)) {
|
|
throw new CommonError("URL格式不正确")
|
|
}
|
|
|
|
return await BookmarkModel.update(id, data)
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`更新书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 删除书签
|
|
async deleteBookmark(id) {
|
|
try {
|
|
if (!id) {
|
|
throw new CommonError("书签ID不能为空")
|
|
}
|
|
|
|
const bookmark = await BookmarkModel.findById(id)
|
|
if (!bookmark) {
|
|
throw new CommonError("书签不存在")
|
|
}
|
|
|
|
return await BookmarkModel.delete(id)
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`删除书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 根据用户和URL查找书签
|
|
async findBookmarkByUserAndUrl(userId, url) {
|
|
try {
|
|
if (!userId || !url) {
|
|
throw new CommonError("用户ID和URL不能为空")
|
|
}
|
|
|
|
return await BookmarkModel.findByUserAndUrl(userId, url)
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`查找书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 检查书签是否存在
|
|
async isBookmarkExists(userId, url) {
|
|
try {
|
|
if (!userId || !url) {
|
|
return false
|
|
}
|
|
|
|
const bookmark = await BookmarkModel.findByUserAndUrl(userId, url)
|
|
return !!bookmark
|
|
} catch (error) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// 批量创建书签
|
|
async createBookmarks(userId, bookmarksData) {
|
|
try {
|
|
if (!userId || !Array.isArray(bookmarksData) || bookmarksData.length === 0) {
|
|
throw new CommonError("用户ID和书签数据不能为空")
|
|
}
|
|
|
|
const results = []
|
|
const errors = []
|
|
|
|
for (const bookmarkData of bookmarksData) {
|
|
try {
|
|
const bookmark = await this.createBookmark({
|
|
...bookmarkData,
|
|
user_id: userId
|
|
})
|
|
results.push(bookmark)
|
|
} catch (error) {
|
|
errors.push({
|
|
url: bookmarkData.url,
|
|
error: error.message
|
|
})
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: results,
|
|
errors,
|
|
total: bookmarksData.length,
|
|
successCount: results.length,
|
|
errorCount: errors.length
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`批量创建书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 批量删除书签
|
|
async deleteBookmarks(userId, bookmarkIds) {
|
|
try {
|
|
if (!userId || !Array.isArray(bookmarkIds) || bookmarkIds.length === 0) {
|
|
throw new CommonError("用户ID和书签ID列表不能为空")
|
|
}
|
|
|
|
const results = []
|
|
const errors = []
|
|
|
|
for (const id of bookmarkIds) {
|
|
try {
|
|
const bookmark = await BookmarkModel.findById(id)
|
|
if (bookmark && bookmark.user_id === userId) {
|
|
await BookmarkModel.delete(id)
|
|
results.push(id)
|
|
} else {
|
|
errors.push({
|
|
id,
|
|
error: "书签不存在或无权限删除"
|
|
})
|
|
}
|
|
} catch (error) {
|
|
errors.push({
|
|
id,
|
|
error: error.message
|
|
})
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: results,
|
|
errors,
|
|
total: bookmarkIds.length,
|
|
successCount: results.length,
|
|
errorCount: errors.length
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`批量删除书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 获取用户书签统计
|
|
async getUserBookmarkStats(userId) {
|
|
try {
|
|
if (!userId) {
|
|
throw new CommonError("用户ID不能为空")
|
|
}
|
|
|
|
const bookmarks = await BookmarkModel.findAllByUser(userId)
|
|
|
|
// 按标签分组统计
|
|
const tagStats = {}
|
|
bookmarks.forEach(bookmark => {
|
|
if (bookmark.tags) {
|
|
const tags = bookmark.tags.split(',').map(tag => tag.trim())
|
|
tags.forEach(tag => {
|
|
tagStats[tag] = (tagStats[tag] || 0) + 1
|
|
})
|
|
}
|
|
})
|
|
|
|
// 按创建时间分组统计
|
|
const dateStats = {}
|
|
bookmarks.forEach(bookmark => {
|
|
const date = new Date(bookmark.created_at).toISOString().split('T')[0]
|
|
dateStats[date] = (dateStats[date] || 0) + 1
|
|
})
|
|
|
|
return {
|
|
total: bookmarks.length,
|
|
byTag: tagStats,
|
|
byDate: dateStats,
|
|
lastUpdated: bookmarks.length > 0 ? bookmarks[0].updated_at : null
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`获取书签统计失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 搜索用户书签
|
|
async searchUserBookmarks(userId, keyword) {
|
|
try {
|
|
if (!userId) {
|
|
throw new CommonError("用户ID不能为空")
|
|
}
|
|
|
|
if (!keyword || keyword.trim() === '') {
|
|
return await this.getUserBookmarks(userId)
|
|
}
|
|
|
|
const bookmarks = await BookmarkModel.findAllByUser(userId)
|
|
const searchTerm = keyword.toLowerCase().trim()
|
|
|
|
return bookmarks.filter(bookmark => {
|
|
return (
|
|
bookmark.title?.toLowerCase().includes(searchTerm) ||
|
|
bookmark.description?.toLowerCase().includes(searchTerm) ||
|
|
bookmark.url?.toLowerCase().includes(searchTerm) ||
|
|
bookmark.tags?.toLowerCase().includes(searchTerm)
|
|
)
|
|
})
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`搜索书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
// 验证URL格式
|
|
isValidUrl(url) {
|
|
try {
|
|
new URL(url)
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// 获取书签分页
|
|
async getBookmarksWithPagination(userId, page = 1, pageSize = 20) {
|
|
try {
|
|
if (!userId) {
|
|
throw new CommonError("用户ID不能为空")
|
|
}
|
|
|
|
const allBookmarks = await BookmarkModel.findAllByUser(userId)
|
|
const total = allBookmarks.length
|
|
const offset = (page - 1) * pageSize
|
|
const bookmarks = allBookmarks.slice(offset, offset + pageSize)
|
|
|
|
return {
|
|
bookmarks,
|
|
pagination: {
|
|
current: page,
|
|
pageSize,
|
|
total,
|
|
totalPages: Math.ceil(total / pageSize)
|
|
}
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof CommonError) throw error
|
|
throw new CommonError(`分页获取书签失败: ${error.message}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
export default BookmarkService
|
|
export { BookmarkService }
|
|
|