资源库-J2EE框架设计与项目开发-课程设计报告

https://github.com/dbkx29/opnmb_public

非常感谢您花时间阅读这篇文章。作为一名不断学习和成长的作者,我深知每一篇文章都有改进的空间。您的意见和建议对我来说非常宝贵,无论是对文章内容的评价、对论点的补充,还是对写作风格的建议,都会帮助我不断提高。

如果您有任何想法或建议,欢迎随时与我分享。我期待您的反馈,并衷心感谢您的支持和帮助!

J2EE框架设计与项目开发

课程设计报告

【数据删除】​​​​​​​学院

School of 【数据删除】

2024年5月

【课程设计名称】

基于SSM+jQuery的“OP匿名版”在线论坛设计与实现

【课程设计目的】

1.复盘J2EE框架设计与项目开发知识。

2.检验软件设计能力,软件实现能力,软件测试能力,软件维护能力,技能熟练度。

【课程设计任务】

  1. 一份课程设计报告一份。
  2. “OP匿名版”在线论坛源代码一份。
  3. “OP匿名版”数据库移植sql一份。

【本设计的主要特色】

使用Java作为后端语言,这是一种强大且广泛使用的语言,适用于各种类型的应用程序。 

使用Maven作为项目构建和管理工具,可以自动处理项目的构建生命周期,包括编译、测试和打包等。

使用mybatisplus代替dao操作。

使用HandlerInterceptor添加请求拦截器。

采用了Restful接口规范。

使用JWT进行身份认证。

在前端,使用了HTML和JavaScript,这是构建网页的基础技术。通过JavaScript,你可以创建动态的、交互式的网页。 

使用jQuery库,这是一个流行的JavaScript库,它简化了DOM操作和Ajax请求,使你的代码更简洁、更易于理解和维护。 

使用了Ajax来实现页面的异步更新,这可以提高用户体验,因为用户不需要刷新整个页面就可以看到新的内容。 

使用了事件委托和滚动加载,这是一种常见的优化技术,可以提高页面的性能和响应速度。 

使用了本地存储(localStorage)来保存用户的登录信息,这可以提高用户体验,因为用户不需要每次访问网站时都重新登录。

目录

第一章 引言... 5

一、选题背景... 5

二、选题依据... 5

三、主要研究内容... 5

第二章 开发环境及工具介绍... 6

一、开发环境... 6

二、使用工具... 6

第三章 需求分析... 7

一、竞品分析... 7

二、功能列表... 7

用户管理... 7

内容管理... 7

社区互动... 8

界面和体验... 8

安全和隐私... 8

性能和可扩展性... 8

第四章 系统设计... 9

一、概要设计... 9

功能描述... 9

数据库数据模型... 9

二、详细设计... 10

POST 发送验证码... 10

POST 用户注册... 10

POST 用户登录... 10

GET 领取饼干... 11

GET 查询名下所有饼干... 11

DELETE 删除饼干... 11

POST 切换饼干... 11

POST 发串... 12

GET 查询单个piece. 12

GET 根据首piece号分块查询整个串... 12

GET 根据category_name分页查询串... 13

POST 回复串,表现为发送一个quote_id为被回复串的串... 13

GET 查询所有板块... 14

三、界面概念设计展示... 14

第五章 系统实现及测试... 16

一、系统实现... 16

BiscuitController. 17

PieceController. 19

index.html 23

二、系统测试... 28

核心功能串连测试... 28

饼干模块异常覆盖测试... 30

第六章 结束语... 31


第一章 引言

一、选题背景

随着互联网技术的飞速发展,网络社区和论坛成为了人们日常生活中不可或缺的一部分。这些平台不仅提供了信息交流的渠道,也促进了社会文化的多元化发展。然而,传统的论坛系统往往存在用户体验不佳、功能单一等问题。因此,开发一个基于SSM(Spring+Spring MVC+MyBatis)的在线综合论坛,可以有效地解决这些问题,同时也符合当前软件工程领域对高效、易用、安全性强的应用需求。

二、选题依据

  1. 技术趋势:近年来,Java生态圈内的Spring框架及其衍生项目如Spring MVC和MyBatis因其优秀的性能、丰富的功能以及广泛的社区支持而受到越来越多的关注。
  2. 市场需求:随着移动互联网的普及,用户对于网页版论坛的需求仍然存在,但同时也有很大的空间去优化和创新,以适应现代用户的使用习惯。
  3. 个人技能提升:作为一名软件工程学生,通过实践项目可以更好地理解和掌握所学知识,加深对Java Web开发的理解,并提高自己的编程能力和解决实际问题的能力。

三、主要研究内容

  1. 系统需求分析与设计:首先进行详细的需求调研和分析,确定论坛的核心功能,如用户注册登录、发帖回复、权限管理等。然后根据需求制定系统架构设计,包括数据库设计、接口设计等。
  2. 前端开发:利用HTML5、CSS3和JavaScript等技术实现论坛的前端界面,使之具有良好的用户体验和响应式布局。
  3. 后端开发:采用Spring MVC处理请求,MyBatis进行数据持久层操作,Spring负责业务逻辑的控制和事务管理。重点实现用户认证、权限控制、数据验证等关键功能
  4. 测试与部署:完成开发后,对系统进行全面的测试,确保各个模块正常运行且互相协同无误。最后,将系统部署到服务器上,进行压力测试和性能优化。
  5. 文档编写与总结:撰写详细的技术文档和用户手册,以及项目总结报告,反思整个开发过程中的经验教训,为未来的项目提供参考。

第二章 开发环境及工具介绍

一、开发环境

Windows 11 家庭中文版23H2,操作系统版本 22631.3593。

java version "17.0.7" 2023-04-18 LTS。

apache-maven-3.9.3。

二、使用工具

IntelliJ IDEA: 一款非常受欢迎的Java集成开发环境(IDE),它提供了强大的代码编辑、智能提示、版本控制集成等功能,极大地方便了Java开发工作。

Maven: 一个项目管理和构建自动化工具,用于项目构建、依赖管理和项目信息管理。Maven简化了项目构建过程,通过pom.xml文件定义项目结构、描述项目依赖、包含目标和插件等。

框架

Spring Framework: Spring是一套轻量级的Java开发框架,旨在减少企业级应用开发的复杂性。Spring提供了一系列基础设施支持,包括IoC容器、数据访问、消息队列、Web服务等。

Spring MVC: Spring MVC是Spring框架的一部分,是一种基于Java的实现MVC设计模式的请求驱动类型的轻量级Web框架,用于创建Web应用程序。

MyBatis: MyBatis是一个支持定制SQL、存储过程以及高级映射的持久层框架。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原始类型、接口和Java POJO为数据库中的记录。

MySQL: MySQL是一种关系型数据库管理系统,广泛用于各种规模的应用,包括Web应用、嵌入式应用等。

Git是一个分布式版本控制系统,用于跟踪更改和协作开发。通过Git,你可以有效地管理和跟踪你的代码历史。

Tomcat: Apache Tomcat是一个开源的 Servlet 容器,实现了Java Servlet 和 JSP 技术,用于发布和运行基于Java的Web应用程序。

Spring配置文件 (applicationContext.xml): 用于配置Spring Bean、数据源、事务管理器等。

MyBatis配置文件 (mybatis-config.xml): 用于配置MyBatis的全局行为,比如日志实现、别名定义等。

第三章 需求分析

一、竞品分析

在进行竞品分析时,我们通常会选择几个市场上表现优秀的在线论坛平台,分析它们的优势和不足,以此来规划我们的论坛系统。假设我们选择了Reddit、Stack Overflow和Quora作为竞品进行分析。

Reddit

优势:Reddit拥有庞大的用户基数和活跃社区,支持多种话题的讨论。它的投票系统使得热门内容容易被发现。

不足:Reddit的界面相对较旧,不够直观;某些小众子版块管理松散,内容质量参差不齐。

Stack Overflow

优势:专注于编程和技术问题的讨论,社区成员通常都是专业的开发者,回答质量高。

不足:对于非技术性的问题支持有限;新手可能因为严格的提问规则而感到困扰。

Quora

优势:鼓励深度回答和专业知识分享,有较高的内容质量。

不足:用户参与度不如Reddit高,某些话题的覆盖率有限。

X岛揭示板

优势:界面设计简单,开支小,外传内容引流效果优秀。

不足:文字与图片带来的用户参与感不足。用户群体优越感明显。对特定类型发言恶意较大。

百度贴吧

优势:作为中文互联网早期上线的顶流文字论坛,积累了大量各层次用户。外传内容引流效果优秀。

二、功能列表

基于以上竞品分析,我们可以罗列出以下功能列表,旨在结合各竞品的优势,同时避免它们的不足:

用户管理

用户注册和登录

个性化用户资料设置

权限分配(管理员、版主、普通用户)

领取(匿名发言使用)饼干

内容管理

发帖和回复

投票和点赞系统

分板块标签搜索功能

社区互动

串浏览功能

评论和回复功能

界面和体验

响应式设计,适配PC和移动设备

简洁直观的导航和布局

安全和隐私

HTTPS加密传输

数据保护

私域用户数据存储

性能和可扩展性

高效的数据库查询和缓存策略

支持大量并发用户

易于维护和升级的架构设计

通过这种方式,我们可以确保新开发的论坛系统既具备吸引用户的独特功能,又能避免竞品中存在的问题,从而提供一个更加完善和用户友好的在线论坛体验。

第四章 系统设计

一、概要设计

功能描述

数据库数据模型

二、详细设计

POST 发送验证码

POST /api/user/send_captcha

请求参数

名称

位置

类型

必选

说明

email

query

string

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": ""
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

POST 用户注册

POST /api/user/register

Body 请求参数

{
  "nickname": "mehdi9bx",
  "password": "123",
  "email": "1733835863@qq.com",
  "captcha": "546791"
}

请求参数

名称

位置

类型

必选

说明

body

body

RegisterDto

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": 0
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

POST 用户登录

POST /api/user/login

Body 请求参数

{
  "type": "user_id",
  "user_id": "106",
  "password": "123"
}

请求参数

名称

位置

类型

必选

说明

body

body

LoginDto

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": {
    "user_id": 0,
    "email": "",
    "nickname": "",
    "token": "",
    "avatar": "",
    "sex": ""
  }
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

GET 领取饼干

GET /api/biscuit/get

请求参数

名称

位置

类型

必选

说明

Authorization

header

string

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": {
    "user_id": 0,
    "biscuit_id": "",
    "timestamp": ""
  }
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

GET 查询名下所有饼干

GET /api/biscuit/list

请求参数

名称

位置

类型

必选

说明

Authorization

header

string

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": [
    {
      "user_id": 0,
      "biscuit_id": "",
      "timestamp": ""
    }
  ]
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

DELETE 删除饼干

DELETE /api/biscuit/delete

请求参数

名称

位置

类型

必选

说明

biscuit_id

query

string

none

Authorization

header

string

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": ""
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

POST 切换饼干

POST /api/biscuit/switch

请求参数

名称

位置

类型

必选

说明

biscuit_id

query

string

none

Authorization

header

string

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": ""
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

POST 发串

POST /api/piece/post

Body 请求参数

{
  "content": "我要成为op匿名版高手!",
  "category_name": "综合版1"
}

请求参数

名称

位置

类型

必选

说明

Authorization

header

string

none

body

body

PostPieceDto

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": {}
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Result

GET 查询单个piece

GET /api/piece/get

请求参数

名称

位置

类型

必选

说明

piece_id

query

integer

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": {
    "piece_id": 0,
    "quote_id": 0,
    "biscuit_id": "",
    "category": "",
    "content": "",
    "timestamp": ""
  }
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

GET 根据首piece号分块查询整个串

GET /api/piece/get_thread/{piece_id}

请求参数

名称

位置

类型

必选

说明

piece_id

path

string

none

page

query

integer

none

batch_size

query

integer

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": [
    {
      "piece_id": 0,
      "quote_id": 0,
      "biscuit_id": "",
      "category_name": "",
      "content": "",
      "timestamp": ""
    }
  ]
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

GET 根据category_name分页查询串

GET /api/piece/get_category/{category_name}

请求参数

名称

位置

类型

必选

说明

category_name

path

string

none

page

query

integer

none

batch_size

query

integer

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": [
    {
      "piece_id": 0,
      "quote_id": 0,
      "biscuit_id": "",
      "category_name": "",
      "content": "",
      "timestamp": ""
    }
  ]
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

POST 回复串,表现为发送一个quote_id为被回复串的串

POST /api/piece/reply

Body 请求参数

{
  "quote_id": 1,
  "content": "我也要成为!",
  "category_name": "综合版1"
}

请求参数

名称

位置

类型

必选

说明

Authorization

header

string

none

body

body

PostPieceDto

none

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": {
    "piece_id": 0,
    "quote_id": 0,
    "biscuit_id": "",
    "category": "",
    "content": "",
    "timestamp": ""
  }
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

GET 查询所有板块

GET /api/category/list

返回示例

成功

{
  "action": "",
  "code": 0,
  "msg": "",
  "data": [
    {
      "categoryId": 0,
      "categoryName": ""
    }
  ]
}

返回结果

状态码

状态码含义

说明

数据模型

200

OK

成功

Inline

三、界面概念设计展示

第五章 系统实现及测试

我采取了mybatisplus的方案,所以数据dao部分都是使用wrapper的模板代码。在此不再赘述。

一、系统实现

以下我展示两个关键Controller的具体代码。

项目仓库同步至https://github.com/dbkx29/opnmb_public(已经开源)

BiscuitController
package icu.dbkx.opnmb.controller.api;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import icu.dbkx.opnmb.common.pojo.Result;
import icu.dbkx.opnmb.common.utils.AssertUtil;
import icu.dbkx.opnmb.common.utils.BiscuitUtil;
import icu.dbkx.opnmb.common.utils.RequestContext;
import icu.dbkx.opnmb.generator.entity.Biscuit;
import icu.dbkx.opnmb.generator.entity.User;
import icu.dbkx.opnmb.generator.service.BiscuitService;
import icu.dbkx.opnmb.generator.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@CrossOrigin
@RequestMapping("/api/biscuit")
@Slf4j
public class BiscuitController {
    @Resource
    UserService userService;
    @Resource
    BiscuitService biscuitService;

    /**
     * 领取饼干
     */
    @GetMapping("/get")
    public Result<Biscuit> getBiscuit() {
        Integer user_id = RequestContext.getContext();
        AssertUtil.isNotEmpty(userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUser_id, user_id)), "用户不存在");
        log.trace("用户{}请求领取饼干", user_id);
        Biscuit biscuit;
        if (biscuitService.count(new LambdaQueryWrapper<Biscuit>()
                .eq(Biscuit::getUser_id, user_id)
                .eq(Biscuit::getActivate, 1)) >= 5) {
            return Result.error("/api/biscuit/get", "每人最多领取5个饼干");
        } else {
            biscuit = biscuitService.createBiscuit(user_id);
        }
        Result<Biscuit> result = Result.success("/api/biscuit/get");
        result.setData(biscuit);
        log.trace("用户{}领取饼干{}", user_id, biscuit.getBiscuit_id());
        return result;
    }

    /**
     * 查询名下所有饼干
     */
    @GetMapping("/list")
    public Result<List<Biscuit>> listBiscuit() {
        Integer user_id = RequestContext.getContext();
        Result<List<Biscuit>> result = Result.success("/api/biscuit/list");
        result.setData(biscuitService.list(new LambdaQueryWrapper<Biscuit>().eq(Biscuit::getUser_id, user_id).eq(Biscuit::getActivate, 1)));
        return result;
    }

    /**
     * 删除饼干
     */
    @DeleteMapping("/delete")
    public Result<String> deleteBiscuit(@RequestParam String biscuit_id) {
        Integer user_id = RequestContext.getContext();
        Biscuit biscuit = biscuitService.getOne(new LambdaQueryWrapper<Biscuit>().eq(Biscuit::getBiscuit_id, biscuit_id));
        if (biscuit == null) {
            return Result.error("/api/biscuit/delete", "饼干不存在");
        } else if (!biscuit.getUser_id().equals(user_id)) {
            return Result.error("/api/biscuit/delete", "无权删除他人的饼干");
        } else if (biscuit.getActivate() == 0) {
            return Result.error("/api/biscuit/delete", "饼干已删除");
        } else if (biscuitService.count(new LambdaQueryWrapper<Biscuit>().eq(Biscuit::getUser_id, user_id).eq(Biscuit::getActivate, 1)) <= 1) {
            return Result.error("/api/biscuit/delete", "至少保留一个饼干");
        } else if (biscuitService.count(new LambdaQueryWrapper<Biscuit>().eq(Biscuit::getUser_id, user_id).eq(Biscuit::getActivate, 0)) >= 3) {
            return Result.error("/api/biscuit/delete", "您已经删除了3个饼干,无法再删除");
        } else {
            biscuit.setActivate(0);
            biscuitService.update(biscuit, new LambdaQueryWrapper<Biscuit>().eq(Biscuit::getBiscuit_id, biscuit_id));
            return Result.success("/api/biscuit/delete");
        }
    }

    /**
     * 切换饼干
     */
    @PostMapping("/switch")
    public Result<String> switchBiscuit(@RequestParam String biscuit_id) {
        Integer user_id = RequestContext.getContext();
        Biscuit biscuit = biscuitService.getOne(new LambdaQueryWrapper<Biscuit>().eq(Biscuit::getBiscuit_id, biscuit_id));
        if (biscuit == null) {
            return Result.error("/api/biscuit/switch", "饼干不存在");
        } else if (!biscuit.getUser_id().equals(user_id)) {
            return Result.error("/api/biscuit/switch", "无权切换他人的饼干");
        } else {
            User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUser_id, user_id));
            user.setBiscuit_id(biscuit_id);
            userService.update(user, new LambdaQueryWrapper<User>().eq(User::getUser_id, user_id));
            return Result.success("/api/biscuit/switch");
        }
    }
}

PieceController

package icu.dbkx.opnmb.controller.api;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import icu.dbkx.opnmb.common.pojo.Result;
import icu.dbkx.opnmb.common.pojo.dto.PostPieceDto;
import icu.dbkx.opnmb.common.utils.AssertUtil;
import icu.dbkx.opnmb.common.utils.RequestContext;
import icu.dbkx.opnmb.generator.entity.Biscuit;
import icu.dbkx.opnmb.generator.entity.Category;
import icu.dbkx.opnmb.generator.entity.Piece;
import icu.dbkx.opnmb.generator.entity.User;
import icu.dbkx.opnmb.generator.service.CategoryService;
import icu.dbkx.opnmb.generator.service.PieceService;
import icu.dbkx.opnmb.generator.service.UserService;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.server.PathParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@RestController
@CrossOrigin
@RequestMapping("/api/piece")
@Slf4j
public class PieceController {

    @Resource
    PieceService pieceService;
    @Resource
    UserService userService;
    @Resource
    CategoryService categoryService;

    /**
     * 发串
     */
    @PostMapping("/post")
    public Result postPiece(@RequestBody PostPieceDto postPieceDto) {
        String biscuit_id = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUser_id, RequestContext.getContext())).getBiscuit_id();
        if(biscuit_id==null){
            Result.error("/api/piece/post","您的账号还未绑定饼干,请先领取饼干或切换饼干");
        }else {
            Piece piece = Piece.builder()
                    .biscuit_id(biscuit_id)
                    .category_name(postPieceDto.getCategory_name())
                    .content(postPieceDto.getContent())
                    .build();
            pieceService.save(piece);
        }
        return Result.success("/api/piece/post","发串成功");
    }

    /**
     * 查询单个piece
     */
    @GetMapping("/get")
    public Result<Piece> getPiece(@RequestParam Integer piece_id) {
        Piece piece = pieceService.getOne(new LambdaQueryWrapper<Piece>().eq(Piece::getPiece_id, piece_id));
        if(piece==null){
            return Result.error("/api/piece/get","串不存在");
        }
        Result<Piece> result = Result.success("/api/piece/get");
        result.setData(piece);
        return result;
    }
    /**
     * 根据首piece号分块查询整个串
     */
    @GetMapping("/get_thread/{piece_id}")
    public Result<List<Piece>> getThread(@PathVariable Integer piece_id,@RequestParam Integer page,@RequestParam Integer batch_size) {
        Piece head = pieceService.getOne(new LambdaQueryWrapper<Piece>().eq(Piece::getPiece_id, piece_id));
        if(head==null){
            return Result.error("/api/piece/get_thread","串不存在");
        }
        List<Piece> pieces = new ArrayList<>();
        if(page==1)pieces.add(head);
        pieces.addAll(pieceService.list(
                new Page<>(page,batch_size==null?10:batch_size),
                new LambdaQueryWrapper<Piece>().eq(Piece::getQuote_id, piece_id).orderByAsc(Piece::getPiece_id)));

        Result<List<Piece>> result = Result.success("/api/piece/get_thread");
        result.setData(pieces);
        return result;
    }
    /**
     * 根据首piece号分块查询整个串的最新5条回复
     */
    @GetMapping("/get_thread_latest")
    public Result<List<Piece>> getThreadLatest(@RequestParam Integer piece_id) {
        Piece head = pieceService.getOne(new LambdaQueryWrapper<Piece>().eq(Piece::getPiece_id, piece_id));
        if(head==null){
            return Result.error("/api/piece/get_thread_latest","串不存在");
        }
        List<Piece> pieces = pieceService.list(
                new Page<>(1,5),
                new LambdaQueryWrapper<Piece>().eq(Piece::getQuote_id, piece_id).orderByDesc(Piece::getPiece_id));

        Result<List<Piece>> result = Result.success("/api/piece/get_thread_latest");
        result.setData(pieces);
        return result;
    }
    /**
     * 根据category_name分页查询串
     */
    @GetMapping("/get_category/{category_name}")
    public Result<List<Piece>> getCategory(@PathVariable String category_name, @RequestParam Integer page, @RequestParam Integer batch_size) {
        String real_category_name = categoryService.getOne(new LambdaQueryWrapper<Category>().eq(Category::getCategory_name, category_name)).getCategory_name();

        List<Piece> pieces;
        log.trace("category_name:{}",category_name);
        if(real_category_name==null){
            return Result.error("/api/piece/get_category","板块不存在");
        }else{
            pieces = pieceService.list(
                    new Page<>(page,batch_size==null?10:batch_size),
                    new LambdaQueryWrapper<Piece>().eq(Piece::getCategory_name, category_name).isNull(Piece::getQuote_id).orderByAsc(Piece::getPiece_id));
        }

        Result<List<Piece>> result = Result.success("/api/piece/get_category");
        result.setData(pieces);
        return result;
    }

    /**
     * 回复串,表现为发送一个quote_id为被回复串的串
     */
    @PostMapping("/reply")
    public Result<Piece> replyPiece(@RequestBody PostPieceDto postPieceDto) {
        Piece head = pieceService.getOne(new LambdaQueryWrapper<Piece>().eq(Piece::getPiece_id, postPieceDto.getQuote_id()));
        log.trace(postPieceDto.toString());
        log.trace("head:{}",head);
        if(head==null){
            return Result.error("/api/piece/reply","串不存在");
        }
        String biscuit_id = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUser_id, RequestContext.getContext())).getBiscuit_id();
        if(biscuit_id==null){
            Result.error("/api/piece/reply","您的账号还未绑定饼干,请先领取饼干或切换饼干");
        }else {
            Piece piece = Piece.builder()
                    .biscuit_id(biscuit_id)
                    .category_name(head.getCategory_name())
                    .content(postPieceDto.getContent())
                    .quote_id(postPieceDto.getQuote_id())
                    .build();
            pieceService.save(piece);
        }
        return Result.success("/api/piece/reply","回复成功");
    }

}

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>OPnmb - OP匿名版</title>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<div class="bordered">
    <h1 id="web_title">
        OP匿名版 - 自由交流区
    </h1>
</div>

<table>
    <tr>
        <td>
            <div class="bordered">
                <label><h2>可用板块</h2></label>
                <div id="categoryList"></div>
            </div>
            <div class="bordered">
                <label><h2>用户系统</h2></label>
                <div id="unlogged">
                    <a href="/view/login">登录</a>
                    <a href="/view/register">注册</a>
                    <button id="refreash_log_status">刷新登录状态</button>
                </div>
                <div id="logged">

                </div>

            </div>
        </td>
        <td>
            <div class="bordered" style="width:766px">
                <div class="welcome_info">
                    <h2>欢迎来到OP匿名版</h2>
                    <p>OP匿名版是一个前台匿名,后台实名的论坛,你可以在这里发表你的观点,也可以在这里看到别人的观点。</p>
                    <p>本站不存在私信功能,用户发言以"饼干"这一发言凭证进行区别。</p>
                    <p>请遵守法律法规,不要发表违法言论。</p>
                </div>
                    <button id="post_button">添加新串</button>
                    <form id="post_form">
                        <textarea id="content" style="font-size: 20px; width: 100%; height: 100px"></textarea>
                        <button type="submit">提交</button>
                    </form>
                <div id="forum">

                </div>
            </div>
        </td>
    </tr>
</table>

<script>
    var page = 1;
    var batch_size = 10;
    var isLoading = false;
    var cur_category = "";

    $("#web_title").click(function () {
        $(".welcome_info").show();
        $("#post_button").hide();
        $("#post_form").hide();
        $('#forum').empty();
    });

    $(document).ready(function() {
        $("#post_form").hide();
        $("#post_button").hide();
        $("#post_button").click(function () {
            $("#post_form").show();
        });
        $("#post_form").submit(function () {
            event.preventDefault();
            var content = document.getElementById("content").value;
            var postDto = {
                category_name: cur_category,
                content: content
            };
            console.log(postDto);
            $.ajax({
                url: "/api/piece/post",
                type: "POST",
                data: JSON.stringify(postDto),
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                headers: {
                    'Authorization': localStorage.getItem("token")
                },
                success: function(data) {
                    console.log(data);
                    if (data.code !== 200) {
                        alert(data.msg);
                    } else {
                        alert("发表成功");
                        location.reload();
                    }
                }
            });
        });
    });
    function content_get(){
        $.get("/api/piece/get_category/" + cur_category+"?page=" +page+"&batch_size="+batch_size, function (data) {
            console.log(data);
            $.each(data.data, function (index, piece) {
                console.log(piece)
                $('#forum').append('<div class="bordered" style="padding: 10px">No.' + piece.piece_id + ' ' + piece.biscuit_id + ' '+ piece.category_name+' ' + piece.timestamp + '<br>' +
                    '<div class="piece_content">' + piece.content + '</div></div>');
                $(".piece_content").on("click",function () {
                    var piece_id = $(this).parent().text().split(" ")[0].substring(3);
                    location.href = "/view/detail?piece_id="+piece_id;
                });
            });
        });
    }


    $.get("/api/category/list", function (data) {
        console.log(data);
        $.each(data.data, function (index, category) {
            $('#categoryList').append('<a class="category_guide">' + category.category_name + '</a><br>');
        });
        $(".category_guide").click(function () {
            $(".welcome_info").hide();
            // console.log("click");
            $("#post_button").show();
            $('#forum').empty();
            page = 1;
            cur_category = $(this).text();

            content_get();
        });
    });


    $('#forum').on('wheel', function(e) {
        if (e.originalEvent.deltaY > 0) {
            if (!isLoading) {
                isLoading = true;
                page++;

                content_get();
            }
        }
    });

    $("#refreash_log_status").click(function () {
        if(localStorage.getItem("token") == null) {
            $("#unlogged").show();
            alert("没有缓存您的登录信息,请登录");
        }else{
            $("#logged").show();
            if(localStorage.getItem("nickname") !=null){
                $("#logged").append("<a>欢迎你,"+localStorage.getItem("nickname")+"<a>");
                $("#logged").append("<button id='logout'>登出</button><br>");
                $("#logout").click(function () {
                    localStorage.removeItem("token");
                    localStorage.removeItem("nickname");
                    localStorage.removeItem("biscuit_id");
                    $("#logged").hide();
                    $("#unlogged").show();
                    location.reload();
                });
            }
            if(localStorage.getItem("biscuit_id") !=null) {
                $("#logged").append("<a>当前饼干:"+localStorage.getItem("biscuit_id")+"</a><br>");
            }
            $("#logged").append("<a href=\"/view/biscuit\" style='font-size: 20px'>饼干管理</a>")
            $("#unlogged").hide();
        }
    });




</script>

<style>
    td {
        position: relative;
        top: 50%;
    }
    .bordered {
        border: 1px solid black;
        padding: 5px;
        margin: 5px;
    }
    .category_guide {
        color: #39c5bb;
        text-underline-offset: auto;
        text-underline: #39c5bb;
        display: block;
        height: 12px;
        margin: 10px;
        width: 120px;
    }
    .category_guide:hover {
        text-decoration: underline;
        cursor: pointer;
    }
    .piece_content:hover {
        text-decoration: underline;
        cursor: pointer;
    }
    #logout {
        background-color: #ea1632;
        cursor: pointer;
    }
    #logout:hover {
        background-color: #b00f26;
    }
    button{
        font-size: 20px;
        width: 100%;
        border-radius: 5px;
        border: none;
        color: #fff;
        background-color: #007BFF;
        cursor: pointer;
    }
    button:hover {
        background-color: #0056b3;
    }
</style>
</body>
</html>

二、系统测试

部分核心功能串连测试(白盒)

执行“首页”-“注册”-注册账号,获取验证码并登录

“饼干管理”-“领取饼干”-“设为当前饼干”

执行前往“技术宅3(Coder)”板块发串

执行发串成功,执行“饼干管理”-“领取饼干”-“设为当前饼干”

选择“提交”

饼干模块异常覆盖测试

不断点击“领取饼干”

第六章 结束语

在本次J2EE课程设计的整个过程中,我经历了从理论学习到实际应用的深刻转变,并在此过程中得到了诸多帮助和支持。此时此刻,我想借此机会向所有给予我支持和帮助的人表达诚挚的感谢。

我要感谢我的老师【数据删除】。在整个项目的研究与实施过程中,导师不仅为我提供了宝贵的学术指导,还在我遇到困难时给予了及时的帮助和鼓励。导师严谨的治学态度和无私的奉献精神深深感染了我,让我在科研道路上受益匪浅。

我要感谢我的同学和朋友们。在项目开发的过程中,他们与我分享经验、交换意见,给予了我莫大的支持和鼓励。尤其是【数据删除】,在我遇到技术难题时,总是耐心地帮助我分析问题、寻找解决方案。没有他们的帮助,我很难顺利完成这次J2EE课程设计。

我还要感谢我的家人。他们在我学习和研究的过程中,一直给予我无私的支持和关怀,为我创造了良好的学习和生活环境。正是有了他们的理解和鼓励,我才能够专心致志地完成J2EE课程设计。

在整个J2EE课程设计的过程中,我不仅学到了许多专业知识,还培养了独立思考和解决问题的能力。通过项目的实施,我深刻理解了理论与实践相结合的重要性,并积累了宝贵的实践经验。这些都将成为我未来学习和工作的宝贵财富。

虽然在项目进行过程中遇到了许多挑战和困难,但通过不断地学习和探索,我最终克服了这些难题,顺利完成了项目。这段经历让我更加坚定了信心,未来我将继续努力,不断提升自己的专业能力,为实现自己的理想和目标而奋斗。

最后,再次向所有关心和帮助过我的人表示衷心的感谢!

虞筱鸣

2024/5/27 4:17于【数据删除】学院【数据删除】学生宿舍

  • 15
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程设计(论文) 题 目: 连连看游戏 姓名 学号 指导教师(签名) 二○一一 年 七 月 十四 日 Java课程设计 摘要: “连连看”游戏是一个经典的游戏,它因操作简单、娱乐性强儿广受欢迎。我们通过所学的JAVAEE设计了一个操作简凡、界面美观、功能较齐全的“连连看”游戏。 该游戏是用图形界面实现的,通过编译解释后,出现一个图开界面,界面是由若干个小方格组成的,每一个小方格里面有一个数值,如果在一个边上有相同的数值,连续点击它们就会消失,同时界面上正上方的分数就会自动加分。游戏还设有退出、重列、再来一局的功能,方便游戏操作。 通过本游戏的开发,达到学习JavaEE技术和熟悉软件开发流程的目的。 前言 近年来,JavaEE作为一种新的编程语言,以其简单性、可移植性和平台无关等优点,得到了广泛的应用,特别是JavaEE与万维网的完美结合,使其成为网络编程和嵌入式编程领域的首选编程语言。 以一个学期的时间来学习JAVAEE这门课,我们不但学习了书上理论的东西,还安排了上机操作课。我认为学习一门计算机语言必定离不开实践,只有通过实践我们才能体会到书本上体会不到的东西。所以,我们不仅要认真学习课本理论知识,更重要的是通过上机实践才能增强和巩固我的知识。课程设计是培养我们综合运用所学知识,发现、提出、分析和解决实际问题,锻炼实践能力的重要环节,是我们实践能力与创新精神的综合培养:在实践能力方面培养我们理论与实践相结合的作风,增强了我们将来在人才市场上的竞争力。 所以,对于我们初学者,首先利用课程设计这样的小项目锻炼袭击的科学研究精神和提高创新能力,是很有必要的。 设计游戏时首先要理顺思路,开始要有一个清晰的可以表达清楚地思路是必不可缺的,游戏设计重点在于游戏有一个界面,方便游戏玩家使用,开始设计前就要想好要有一个什么样的界面此时就要稍微用到点审美力,以达到大多数玩家的审美水平,让更多人接受,因此首先要清楚先要做什么后要做什么,再次我们可以规划一下我们的步骤:首先做出一个流程图,然后对即将做出的游戏构思一个漂亮的界面,最后一步就是要写出我们的程序源代码。 一、让别人清楚你的思路以便在遇到困难时可以请教别人,更可以让自己做的得心应手,因此首先要做一个流程图:
### 回答1: 基于J2EE的快速开发平台Jeecg-Boot,可以帮助开发人员快速地构建和部署企业级应用程序。Jeecg-Boot使用了J2EE开发框架,如Spring Boot、MyBatis等,提供了一套全面的工具和模块,帮助开发人员简化开发过程,提高开发效率。 首先,在使用Jeecg-Boot开发时,我们可以利用其提供的代码生成工具,快速生成大量基础代码。通过定义数据表结构,代码生成工具会自动生成与数据库交互的基础增删改查模块,减少了手动编写这些重复代码的工作量,提高了开发效率。 其次,Jeecg-Boot提供了丰富的业务模块和组件,包括权限管理、菜单管理、数据字典、文件上传下载等等。这些模块和组件可以直接集成到应用程序中,减少了开发人员自行开发这些基础功能的时间和精力,同时保证了应用程序的功能完整性。 此外,Jeecg-Boot还提供了一系列的插件和扩展,可以满足不同开发需求。例如,Jeecg-Boot支持在线开发模式,在不停服的情况下,实时修改代码并生效,极大地提高了调试和修改的效率。同时,Jeecg-Boot也支持分布式部署和集群部署,可以应对高并发和大规模访问的需求。 总之,基于J2EE快速开发平台Jeecg-Boot开发的优势在于提供了一套完整的开发工具和模块,帮助开发人员快速构建和部署企业级应用程序,大大提高了开发效率和质量。 ### 回答2: jeecg-boot是一个基于j2ee的快速开发平台,提供了一整套开发工具和框架,使开发者能够快速构建基于j2ee的应用程序。 jeecg-boot具有丰富的功能和特性,包括代码生成器、权限管理、后台管理、前后端分离、多数据源支持等。借助于代码生成器,开发者可以根据数据库表结构自动生成实体类、Dao、Service等代码,节省了手动编写重复代码的时间和工作量。权限管理模块可以帮助开发者实现灵活的用户权限控制,保护系统安全。后台管理模块提供了丰富的功能页面和交互界面,使开发者能够方便地管理系统。前后端分离的特性使得前端开发和后端开发可以独立进行,提高了开发效率。多数据源支持可以满足多数据库连接的需求,适用于复杂的业务场景。 在使用jeecg-boot进行开发时,开发者可以按照自己的需求进行定制和扩展。jeecg-boot提供了丰富的插件和可扩展的接口,使开发者可以灵活地集成自己的业务逻辑和功能。此外,jeecg-boot还提供了详细的文档和示例代码,方便开发者学习和使用。 总之,jeecg-boot是一个强大的基于j2ee的快速开发平台,提供了丰富的功能和特性,可以帮助开发者快速构建高质量的j2ee应用程序。无论是开发一个简单的小型应用还是一个复杂的企业级应用,jeecg-boot都是一个优秀的选择。 ### 回答3: jeecg-boot是基于J2EE的快速开发平台,它提供了一套简单、高效的开发框架,可以帮助开发人员快速构建企业级应用程序。 首先,jeecg-boot采用了主流的J2EE技术栈,包括Spring Boot、Spring Cloud、Mybatis等,这些技术在企业应用开发中被广泛应用,具有成熟、稳定的特点。通过使用这些技术,开发人员可以快速搭建整体项目架构,减少重复性的工作,提高开发效率。 其次,jeecg-boot提供了丰富的代码生成器和模板,可以根据数据库表结构自动生成代码。这样一来,开发人员只需要定义好数据库表结构,就能够生成对应的实体类、控制器、服务类等代码,省去了大量的手动编写代码的时间和精力。 此外,jeecg-boot还内置了权限管理、数据字典、代码生成、报表打印等常用功能模块,开发人员可以快速集成这些功能,并根据实际需求进行定制。同时,它还支持多数据源配置,能够满足复杂业务场景下的需求。 最后,jeecg-boot具有良好的可扩展性和可维护性。通过遵循一些设计原则和开发规范,开发人员可以编写易于扩展和维护的代码。此外,jeecg-boot还提供了一些常见的工具类和插件,帮助开发人员更好地进行开发工作。 总之,基于jeecg-boot开发可以大大提高开发效率和代码质量,快速构建功能完善、稳定可靠的企业级应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值