Java论坛系统案例

wen java案例 1

本文目录导读:

Java论坛系统案例

  1. 系统架构设计
  2. 核心代码实现
  3. 前端页面示例
  4. 配置与数据库
  5. 功能扩展建议

我来为你提供一个完整的Java论坛系统案例,包含核心功能实现和代码示例。

系统架构设计

技术栈

  • 后端: Spring Boot + MyBatis + MySQL
  • 前端: Thymeleaf + Bootstrap
  • 安全: Spring Security + JWT
  • 其他: Redis缓存, Elasticsearch搜索

项目结构

forum-system/
├── src/main/java/com/example/forum/
│   ├── controller/
│   ├── service/
│   ├── mapper/
│   ├── entity/
│   ├── config/
│   └── utils/
├── src/main/resources/
│   ├── templates/
│   ├── static/
│   └── application.yml
└── pom.xml

核心代码实现

实体类

// User.java
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    private String email;
    private String avatar;
    private Integer role;  // 1:管理员 2:普通用户
    private Integer status; // 1:正常 2:禁言
    private Integer reputation; // 积分
    private LocalDateTime createTime;
    // getter/setter省略
}
// Topic.java
@Entity
@Table(name = "topic")
public class Topic {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String content;
    private Long userId;
    private Long categoryId;
    private Integer viewCount;
    private Integer replyCount;
    private Integer likeCount;
    private Long lastReplyId;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    @Transient
    private User user; // 帖子作者信息
    @Transient
    private List<Reply> replies; // 回复列表
}
// Reply.java
@Entity
@Table(name = "reply")
public class Reply {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long topicId;
    private Long userId;
    private String content;
    private Integer likeCount;
    private LocalDateTime createTime;
    @Transient
    private User user;
}

Mapper层

// TopicMapper.java
@Mapper
public interface TopicMapper {
    @Select("SELECT * FROM topic WHERE status = 1 ORDER BY create_time DESC LIMIT #{offset}, #{limit}")
    List<Topic> findLatestTopics(@Param("offset") int offset, @Param("limit") int limit);
    @Select("SELECT * FROM topic WHERE category_id = #{categoryId} AND status = 1 ORDER BY create_time DESC")
    List<Topic> findByCategory(@Param("categoryId") Long categoryId);
    @Select("SELECT * FROM topic WHERE title LIKE CONCAT('%', #{keyword}, '%') OR content LIKE CONCAT('%', #{keyword}, '%')")
    List<Topic> search(@Param("keyword") String keyword);
    @Select("SELECT * FROM topic WHERE id = #{id} AND status = 1")
    Topic findById(@Param("id") Long id);
    @Insert("INSERT INTO topic(title, content, user_id, category_id) VALUES(#{title}, #{content}, #{userId}, #{categoryId})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(Topic topic);
    @Update("UPDATE topic SET title = #{title}, content = #{content}, category_id = #{categoryId} WHERE id = #{id}")
    int update(Topic topic);
    @Delete("DELETE FROM topic WHERE id = #{id}")
    int delete(@Param("id") Long id);
    @Update("UPDATE topic SET view_count = view_count + 1 WHERE id = #{id}")
    int incrementViewCount(@Param("id") Long id);
}

Service层

// TopicService.java
@Service
@Transactional
public class TopicService {
    @Autowired
    private TopicMapper topicMapper;
    @Autowired
    private ReplyMapper replyMapper;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // 获取热门帖子(缓存)
    public List<Topic> getHotTopics() {
        String key = "hot_topics";
        List<Topic> topics = (List<Topic>) redisTemplate.opsForValue().get(key);
        if (topics == null) {
            topics = topicMapper.findHotTopics();
            redisTemplate.opsForValue().set(key, topics, 10, TimeUnit.MINUTES);
        }
        return topics;
    }
    // 发布帖子
    public Result publish(Topic topic, Long userId) {
        topic.setUserId(userId);
        if (topicMapper.insert(topic) > 0) {
            // 清理缓存
            redisTemplate.delete("hot_topics");
            return Result.success("发布成功", topic);
        }
        return Result.error("发布失败");
    }
    // 查看帖子详情
    public Topic getTopicDetail(Long topicId) {
        Topic topic = topicMapper.findById(topicId);
        if (topic != null) {
            // 增加浏览数
            topicMapper.incrementViewCount(topicId);
            // 获取回复列表
            List<Reply> replies = replyMapper.findByTopicId(topicId);
            topic.setReplies(replies);
            // 加载用户信息
            topic.setUser(userMapper.findById(topic.getUserId()));
        }
        return topic;
    }
    // 分页获取帖子
    public PageResult<Topic> getTopicsByPage(int page, int size, Long categoryId) {
        int offset = (page - 1) * size;
        List<Topic> topics;
        long total;
        if (categoryId == null) {
            topics = topicMapper.findLatestTopics(offset, size);
            total = topicMapper.countAll();
        } else {
            topics = topicMapper.findByCategory(offset, size, categoryId);
            total = topicMapper.countByCategory(categoryId);
        }
        return new PageResult<>(topics, total, page, size);
    }
}

控制器层

// ForumController.java
@Controller
@RequestMapping("/forum")
public class ForumController {
    @Autowired
    private TopicService topicService;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private UserService userService;
    // 论坛首页
    @GetMapping("/index")
    public String index(Model model) {
        // 获取热门帖子
        List<Topic> hotTopics = topicService.getHotTopics();
        model.addAttribute("hotTopics", hotTopics);
        // 获取分类列表
        List<Category> categories = categoryService.getAllCategories();
        model.addAttribute("categories", categories);
        // 获取最新帖子
        PageResult<Topic> latestTopics = topicService.getTopicsByPage(1, 20, null);
        model.addAttribute("latestTopics", latestTopics);
        return "forum/index";
    }
    // 查看帖子详情
    @GetMapping("/topic/{id}")
    public String topicDetail(@PathVariable("id") Long id, Model model) {
        Topic topic = topicService.getTopicDetail(id);
        if (topic == null) {
            return "error/404";
        }
        model.addAttribute("topic", topic);
        model.addAttribute("replies", topic.getReplies());
        return "forum/topic-detail";
    }
    // 发布帖子页面
    @GetMapping("/publish")
    public String showPublishPage(Model model) {
        List<Category> categories = categoryService.getAllCategories();
        model.addAttribute("categories", categories);
        return "forum/publish";
    }
    // 发布帖子处理
    @PostMapping("/publish")
    @ResponseBody
    public Result publishTopic(@RequestBody TopicForm form) {
        // 获取当前登录用户
        User currentUser = userService.getCurrentUser();
        Topic topic = new Topic();
        topic.setTitle(form.getTitle());
        topic.setContent(form.getContent());
        topic.setCategoryId(form.getCategoryId());
        Result result = topicService.publish(topic, currentUser.getId());
        return result;
    }
    // 回复帖子
    @PostMapping("/topic/{id}/reply")
    @ResponseBody
    public Result replyTopic(@PathVariable("id") Long topicId, 
                           @RequestBody ReplyForm form) {
        User currentUser = userService.getCurrentUser();
        Reply reply = new Reply();
        reply.setTopicId(topicId);
        reply.setContent(form.getContent());
        reply.setUserId(currentUser.getId());
        return topicService.reply(reply);
    }
    // 搜索帖子
    @GetMapping("/search")
    public String search(@RequestParam("keyword") String keyword, Model model) {
        List<Topic> results = topicService.search(keyword);
        model.addAttribute("topics", results);
        model.addAttribute("keyword", keyword);
        return "forum/search-result";
    }
    // 分类浏览
    @GetMapping("/category/{categoryId}")
    public String categoryTopics(@PathVariable("categoryId") Long categoryId,
                                @RequestParam(defaultValue = "1") int page,
                                Model model) {
        PageResult<Topic> topics = topicService.getTopicsByPage(page, 10, categoryId);
        model.addAttribute("topics", topics);
        model.addAttribute("currentCategoryId", categoryId);
        return "forum/category-topics";
    }
}

用户认证

// UserService.java
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtUtils jwtUtils;
    // 注册
    public Result register(RegisterForm form) {
        // 检查用户名是否已存在
        if (userMapper.findByUsername(form.getUsername()) != null) {
            return Result.error("用户名已存在");
        }
        // 检查邮箱是否已存在
        if (userMapper.findByEmail(form.getEmail()) != null) {
            return Result.error("邮箱已被注册");
        }
        User user = new User();
        user.setUsername(form.getUsername());
        user.setPassword(passwordEncoder.encode(form.getPassword()));
        user.setEmail(form.getEmail());
        user.setRole(2); // 普通用户
        user.setStatus(1); // 正常状态
        user.setCreateTime(LocalDateTime.now());
        userMapper.insert(user);
        return Result.success("注册成功");
    }
    // 登录
    public Result login(LoginForm form) {
        User user = userMapper.findByUsername(form.getUsername());
        if (user == null || !passwordEncoder.matches(form.getPassword(), user.getPassword())) {
            return Result.error("用户名或密码错误");
        }
        if (user.getStatus() == 2) {
            return Result.error("账号已被禁用");
        }
        // 生成JWT token
        String token = jwtUtils.generateToken(user);
        Map<String, Object> data = new HashMap<>();
        data.put("token", token);
        data.put("user", user);
        return Result.success("登录成功", data);
    }
}
// JwtUtils.java
@Component
public class JwtUtils {
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private Long expiration;
    public String generateToken(User user) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration * 1000);
        return Jwts.builder()
                .setSubject(String.valueOf(user.getId()))
                .claim("username", user.getUsername())
                .claim("role", user.getRole())
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
}

前端页面示例

首页模板 (index.html)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">技术论坛</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="/forum/index">技术论坛</a>
            <div class="collapse navbar-collapse">
                <ul class="navbar-nav me-auto">
                    <li class="nav-item">
                        <a class="nav-link active" href="/forum/index">首页</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/forum/publish">发帖</a>
                    </li>
                </ul>
                <form class="d-flex" action="/forum/search" method="get">
                    <input class="form-control me-2" type="search" name="keyword" placeholder="搜索帖子...">
                    <button class="btn btn-outline-light" type="submit">搜索</button>
                </form>
            </div>
        </div>
    </nav>
    <div class="container mt-4">
        <div class="row">
            <!-- 左侧分类列表 -->
            <div class="col-md-3">
                <div class="card">
                    <div class="card-header">分类</div>
                    <ul class="list-group list-group-flush" th:each="category : ${categories}">
                        <li class="list-group-item">
                            <a th:href="@{/forum/category/{id}(id=${category.id})}" 
                               th:text="${category.name}">分类名称</a>
                        </li>
                    </ul>
                </div>
            </div>
            <!-- 帖子列表 -->
            <div class="col-md-9">
                <div class="card">
                    <div class="card-header">
                        <h5>最新帖子</h5>
                    </div>
                    <div class="list-group list-group-flush" th:each="topic : ${latestTopics.data}">
                        <div class="list-group-item">
                            <div class="d-flex justify-content-between">
                                <h6><a th:href="@{/forum/topic/{id}(id=${topic.id})}" 
                                       th:text="${topic.title}">帖子标题</a></h6>
                                <small class="text-muted" th:text="${#dates.format(topic.createTime, 'yyyy-MM-dd HH:mm')}">发布时间</small>
                            </div>
                            <p class="mb-1 text-truncate" th:text="${topic.content}">帖子内容</p>
                            <div>
                                <small>
                                    <span class="me-3">
                                        <i class="bi bi-eye"></i> 
                                        <span th:text="${topic.viewCount}">浏览数</span>
                                    </span>
                                    <span class="me-3">
                                        <i class="bi bi-chat"></i> 
                                        <span th:text="${topic.replyCount}">回复数</span>
                                    </span>
                                    <span>发布于 <span th:text="${topic.user.username}">作者</span></span>
                                </small>
                            </div>
                        </div>
                    </div>
                </div>
                <!-- 分页 -->
                <nav class="mt-3">
                    <ul class="pagination">
                        <li class="page-item" th:if="${latestTopics.page > 1}">
                            <a class="page-link" th:href="@{/forum/index(page=${latestTopics.page - 1})}">上一页</a>
                        </li>
                        <li class="page-item" th:each="i : ${#numbers.sequence(1, latestTopics.totalPages)}" 
                            th:classappend="${i == latestTopics.page} ? 'active'">
                            <a class="page-link" th:href="@{/forum/index(page=${i})}" th:text="${i}">1</a>
                        </li>
                        <li class="page-item" th:if="${latestTopics.page < latestTopics.totalPages}">
                            <a class="page-link" th:href="@{/forum/index(page=${latestTopics.page + 1})}">下一页</a>
                        </li>
                    </ul>
                </nav>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

配置与数据库

数据库表结构

-- 用户表
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(100) NOT NULL,
  `email` varchar(100) DEFAULT NULL,
  `avatar` varchar(255) DEFAULT NULL,
  `role` int DEFAULT '2' COMMENT '1:管理员 2:普通用户',
  `status` int DEFAULT '1' COMMENT '1:正常 2:禁言',
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`)
);
-- 主题表
CREATE TABLE `topic` (
  `id` bigint NOT NULL AUTO_INCREMENT, varchar(200) NOT NULL,
  `content` text,
  `user_id` bigint NOT NULL,
  `category_id` bigint DEFAULT NULL,
  `view_count` int DEFAULT '0',
  `reply_count` int DEFAULT '0',
  `like_count` int DEFAULT '0',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_category_id` (`category_id`)
);
-- 分类表
CREATE TABLE `category` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `description` varchar(200) DEFAULT NULL,
  `sort_order` int DEFAULT '0',
  PRIMARY KEY (`id`)
);

application.yml

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/forum
    username: root
    password: root
  redis:
    host: localhost
    port: 6379
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.forum.entity
jwt:
  secret: your-secret-key-here
  expiration: 86400  # 24小时

功能扩展建议

  1. 富文本编辑器: 使用UEditor或TinyMCE
  2. 图片上传: 集成OSS或本地存储
  3. 消息通知: 评论通知、@功能
  4. 积分系统: 发帖/回复获取积分
  5. 敏感词过滤: 内容安全
  6. 数据统计: 活跃度分析

这个论坛系统案例包含了完整的核心功能,可以根据实际需求进行调整和扩展,需要我详细说明哪个部分?

抱歉,评论功能暂时关闭!