为什么收藏数为0时可以正常显示<h2 class="empty-title">您还没有收藏任何文章</h2>
<p class="empty-text">
收藏功能可以帮助您保存喜欢的文章,方便以后随时阅读。
浏览我们的文章库,发现精彩内容并添加到收藏吧!
</p>
<a href="list.jsp" class="empty-btn">
<i class="fas fa-book-open"></i> 浏览文章
</a>部分的内容,而收藏了文章后却只显示"我的收藏
1 篇收藏"一行文本
favorites.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.myblog.dao.ArticleDAO, com.myblog.dao.UserDAO, com.myblog.dao.FavoriteDAO" %>
<%@ page import="com.myblog.model.Article, com.myblog.model.User, com.myblog.model.Favorite" %>
<%@ page import="java.util.List" %>
<%
// 获取当前登录用户
User currentUser = (User) session.getAttribute("user");
if (currentUser == null || currentUser.getIsAdmin() != 1) {
response.sendRedirect("login.jsp");
return;
}
FavoriteDAO favoriteDao = new FavoriteDAO();
ArticleDAO articleDao = new ArticleDAO();
UserDAO userDao = new UserDAO();
List<Favorite> favorites = favoriteDao.getFavoritesByUser(currentUser.getUid());
%>
<!DOCTYPE html>
<html>
<head>
<title>我的收藏 - MyBlog</title>
<link rel="stylesheet" href="css/style.css">
<style>
.favorites-container {
max-width: 1200px;
margin: 0 auto;
}
.favorites-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
}
.favorites-title {
font-size: 2rem;
color: #1a2a6c;
display: flex;
align-items: center;
gap: 15px;
}
.favorites-title i {
color: #ffcc00;
}
.favorites-count {
background: #e8f4f8;
padding: 8px 15px;
border-radius: 20px;
font-weight: bold;
color: #1a2a6c;
}
.filters {
display: flex;
gap: 15px;
margin-bottom: 25px;
flex-wrap: wrap;
}
.filter-btn {
padding: 8px 20px;
border: 1px solid #ddd;
border-radius: 30px;
background: white;
cursor: pointer;
transition: all 0.3s;
}
.filter-btn:hover, .filter-btn.active {
background: #1a2a6c;
color: white;
border-color: #1a2a6c;
}
.favorites-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 30px;
}
.favorite-card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
transition: all 0.3s ease;
position: relative;
display: flex;
flex-direction: column;
height: 100%;
}
.favorite-card:hover {
transform: translateY(-8px);
box-shadow: 0 12px 25px rgba(0,0,0,0.15);
}
.card-image {
height: 200px;
background: linear-gradient(45deg, #1a2a6c, #b21f1f);
background-size: cover;
background-position: center;
position: relative;
}
.card-badge {
position: absolute;
top: 15px;
right: 15px;
background: rgba(255, 255, 255, 0.9);
padding: 5px 12px;
border-radius: 30px;
font-size: 0.85rem;
font-weight: 600;
color: #1a2a6c;
display: flex;
align-items: center;
gap: 5px;
}
.card-content {
padding: 25px;
flex: 1;
display: flex;
flex-direction: column;
}
.card-title {
font-size: 1.4rem;
margin-bottom: 12px;
color: #1a2a6c;
line-height: 1.4;
}
.card-excerpt {
color: #555;
margin-bottom: 20px;
flex: 1;
line-height: 1.6;
}
.card-meta {
display: flex;
justify-content: space-between;
color: #777;
font-size: 0.9rem;
padding-top: 15px;
border-top: 1px solid #eee;
}
.card-author {
display: flex;
align-items: center;
gap: 8px;
}
.author-avatar {
width: 30px;
height: 30px;
border-radius: 50%;
object-fit: cover;
}
.card-date {
display: flex;
align-items: center;
gap: 5px;
}
.card-actions {
position: absolute;
bottom: 15px;
right: 15px;
display: flex;
gap: 10px;
}
.action-btn {
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.action-btn:hover {
transform: scale(1.1);
}
.remove-btn {
color: #e74c3c;
}
.remove-btn:hover {
background: #e74c3c;
color: white;
}
.visit-btn {
color: #1a2a6c;
}
.visit-btn:hover {
background: #1a2a6c;
color: white;
}
.empty-favorites {
text-align: center;
padding: 60px 30px;
background: white;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
margin-top: 30px;
}
.empty-icon {
font-size: 5rem;
color: #e8f4f8;
margin-bottom: 20px;
}
.empty-title {
font-size: 1.8rem;
color: #1a2a6c;
margin-bottom: 15px;
}
.empty-text {
color: #777;
max-width: 500px;
margin: 0 auto 30px;
line-height: 1.6;
}
.empty-btn {
display: inline-block;
padding: 12px 35px;
background: linear-gradient(to right, #1a2a6c, #b21f1f);
color: white;
border-radius: 30px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s;
box-shadow: 0 4px 10px rgba(26, 42, 108, 0.3);
}
.empty-btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(26, 42, 108, 0.4);
}
.pagination {
display: flex;
justify-content: center;
margin-top: 50px;
gap: 8px;
}
.page-item {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: white;
color: #1a2a6c;
text-decoration: none;
font-weight: 600;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: all 0.3s;
}
.page-item:hover, .page-item.active {
background: #1a2a6c;
color: white;
}
@media (max-width: 768px) {
.favorites-grid {
grid-template-columns: 1fr;
}
.filters {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<%@ include file="left.txt" %>
<div class="main-content">
<div class="favorites-container">
<div class="favorites-header">
<h1 class="favorites-title">
<i class="fas fa-star"></i> 我的收藏
<span class="favorites-count"><%= favorites.size() %> 篇收藏</span>
</h1>
</div>
<% if (favorites.isEmpty()) { %>
<div class="empty-favorites">
<div class="empty-icon">
<i class="far fa-star"></i>
</div>
<h2 class="empty-title">您还没有收藏任何文章</h2>
<p class="empty-text">
收藏功能可以帮助您保存喜欢的文章,方便以后随时阅读。
浏览我们的文章库,发现精彩内容并添加到收藏吧!
</p>
<a href="list.jsp" class="empty-btn">
<i class="fas fa-book-open"></i> 浏览文章
</a>
</div>
<% } else { %>
<div class="favorites-grid">
<% for (Favorite favorite : favorites) {
Article article = articleDao.getArticleById(favorite.getArticleId());
if (article != null) {
User author = userDao.getUserById(article.getAuthorId());
%>
<div class="favorite-card">
<div class="card-image" >
<div class="card-badge">
<i class="fas fa-clock"></i> <%= article.getCreatedAt().toString().substring(5, 10) %>
</div>
</div>
<div class="card-content">
<h3 class="card-title">
<a href="detail.jsp?aid=<%= article.getAid() %>"><%= article.getTitle() %></a>
</h3>
<p class="card-excerpt">
<%= article.getContent().substring(0, Math.min(120, article.getContent().length())) %>
</p>
<div class="card-meta">
<div class="card-author">
<img src="<%= author.getAvatar() %>" class="author-avatar" alt="<%= author.getUsername() %>">
<span><%= author.getUsername() %></span>
</div>
<div class="card-date">
<i class="far fa-calendar"></i>
<%= favorite.getCreatedAt().toString().substring(0, 10) %>
</div>
</div>
</div>
<div class="card-actions">
<a href="detail.jsp?aid=<%= article.getAid() %>" class="action-btn visit-btn" title="查看文章">
<i class="fas fa-external-link-alt"></i>
</a>
<a href="FavoriteServlet?action=remove&article_id=<%= article.getAid() %>"
class="action-btn remove-btn" title="取消收藏">
<i class="fas fa-trash-alt"></i>
</a>
</div>
</div>
<% }
} %>
</div>
<div class="pagination">
<a href="#" class="page-item"><i class="fas fa-chevron-left"></i></a>
<a href="#" class="page-item active">1</a>
<a href="#" class="page-item">2</a>
<a href="#" class="page-item">3</a>
<a href="#" class="page-item">4</a>
<a href="#" class="page-item"><i class="fas fa-chevron-right"></i></a>
</div>
<% } %>
</div>
</div>
</div>
</body>
</html>
FavoriteServlet
package com.myblog.servlet;
import com.myblog.dao.FavoriteDAO;
import com.myblog.model.User;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet("/FavoriteServlet")
public class FavoriteServlet extends HttpServlet {
private FavoriteDAO favoriteDao = new FavoriteDAO();
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
if (user == null || user.getIsAdmin() != 1) {
response.sendRedirect("login.jsp");
return;
}
int articleId = Integer.parseInt(request.getParameter("article_id"));
String action = request.getParameter("action");
if ("add".equals(action)) {
favoriteDao.addFavorite(user.getUid(), articleId);
} else if ("remove".equals(action)) {
favoriteDao.removeFavorite(user.getUid(), articleId);
}
response.sendRedirect("detail.jsp?aid=" + articleId);
}
}
FavoriteDAO
package com.myblog.dao;
import com.myblog.model.Favorite;
import com.myblog.util.DBUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import static com.myblog.util.DBUtil.getConnection;
public class FavoriteDAO {
public boolean addFavorite(int userId, int articleId) {
String sql = "INSERT INTO favorites (user_id, article_id) VALUES (?, ?)";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, userId);
stmt.setInt(2, articleId);
int rows = stmt.executeUpdate();
return rows > 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public boolean removeFavorite(int userId, int articleId) {
String sql = "DELETE FROM favorites WHERE user_id = ? AND article_id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, userId);
stmt.setInt(2, articleId);
int rows = stmt.executeUpdate();
return rows > 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public boolean isFavorite(int userId, int articleId) {
String sql = "SELECT fid FROM favorites WHERE user_id = ? AND article_id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, userId);
stmt.setInt(2, articleId);
try (ResultSet rs = stmt.executeQuery()) {
return rs.next();
}
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public List<Favorite> getFavoritesByUser(int userId) {
List<Favorite> favorites = new ArrayList<>();
String sql = "SELECT * FROM favorites WHERE user_id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, userId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
Favorite fav = new Favorite();
fav.setFid(rs.getInt("fid"));
fav.setUserId(rs.getInt("user_id"));
fav.setArticleId(rs.getInt("article_id"));
favorites.add(fav);
}
}
} catch (SQLException e) {
e.printStackTrace(); // 实际项目应使用日志框架
}
return favorites;
}
}
Favorite
package com.myblog.model;
import java.sql.Timestamp;
public class Favorite {
private int fid;
private int userId;
private int articleId;
private Timestamp createdAt;
// 无参构造器
public Favorite() {}
// 全参构造器
public Favorite(int fid, int userId, int articleId, Timestamp createdAt) {
this.fid = fid;
this.userId = userId;
this.articleId = articleId;
this.createdAt = createdAt;
}
// 用于创建收藏的构造器
public Favorite(int userId, int articleId) {
this.userId = userId;
this.articleId = articleId;
}
// Getter和Setter方法
public int getFid() {
return fid;
}
public void setFid(int fid) {
this.fid = fid;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getArticleId() {
return articleId;
}
public void setArticleId(int articleId) {
this.articleId = articleId;
}
public Timestamp getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Timestamp createdAt) {
this.createdAt = createdAt;
}
}
SQL
DROP TABLE IF EXISTS `favorites`;
CREATE TABLE `favorites` (
`fid` int(0) NOT NULL AUTO_INCREMENT,
`user_id` int(0) NOT NULL,
`article_id` int(0) NOT NULL,
`created_at` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`fid`) USING BTREE,
UNIQUE INDEX `unique_favorite`(`user_id`, `article_id`) USING BTREE,
INDEX `article_id`(`article_id`) USING BTREE,
CONSTRAINT `favorites_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`uid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `favorites_ibfk_2` FOREIGN KEY (`article_id`) REFERENCES `articles` (`aid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
最新发布