https://github.com/dbkx29/opnmb_public
非常感谢您花时间阅读这篇文章。作为一名不断学习和成长的作者,我深知每一篇文章都有改进的空间。您的意见和建议对我来说非常宝贵,无论是对文章内容的评价、对论点的补充,还是对写作风格的建议,都会帮助我不断提高。
如果您有任何想法或建议,欢迎随时与我分享。我期待您的反馈,并衷心感谢您的支持和帮助!
J2EE框架设计与项目开发
课程设计报告
【数据删除】学院
School of 【数据删除】
2024年5月
【课程设计名称】
基于SSM+jQuery的“OP匿名版”在线论坛设计与实现
【课程设计目的】
1.复盘J2EE框架设计与项目开发知识。
2.检验软件设计能力,软件实现能力,软件测试能力,软件维护能力,技能熟练度。
【课程设计任务】
- 一份课程设计报告一份。
- “OP匿名版”在线论坛源代码一份。
- “OP匿名版”数据库移植sql一份。
【本设计的主要特色】
使用Java作为后端语言,这是一种强大且广泛使用的语言,适用于各种类型的应用程序。
使用Maven作为项目构建和管理工具,可以自动处理项目的构建生命周期,包括编译、测试和打包等。
使用mybatisplus代替dao操作。
使用HandlerInterceptor添加请求拦截器。
采用了Restful接口规范。
使用JWT进行身份认证。
在前端,使用了HTML和JavaScript,这是构建网页的基础技术。通过JavaScript,你可以创建动态的、交互式的网页。
使用jQuery库,这是一个流行的JavaScript库,它简化了DOM操作和Ajax请求,使你的代码更简洁、更易于理解和维护。
使用了Ajax来实现页面的异步更新,这可以提高用户体验,因为用户不需要刷新整个页面就可以看到新的内容。
使用了事件委托和滚动加载,这是一种常见的优化技术,可以提高页面的性能和响应速度。
使用了本地存储(localStorage)来保存用户的登录信息,这可以提高用户体验,因为用户不需要每次访问网站时都重新登录。
目录
GET 根据category_name分页查询串... 13
POST 回复串,表现为发送一个quote_id为被回复串的串... 13
第一章 引言
一、选题背景
随着互联网技术的飞速发展,网络社区和论坛成为了人们日常生活中不可或缺的一部分。这些平台不仅提供了信息交流的渠道,也促进了社会文化的多元化发展。然而,传统的论坛系统往往存在用户体验不佳、功能单一等问题。因此,开发一个基于SSM(Spring+Spring MVC+MyBatis)的在线综合论坛,可以有效地解决这些问题,同时也符合当前软件工程领域对高效、易用、安全性强的应用需求。
二、选题依据
- 技术趋势:近年来,Java生态圈内的Spring框架及其衍生项目如Spring MVC和MyBatis因其优秀的性能、丰富的功能以及广泛的社区支持而受到越来越多的关注。
- 市场需求:随着移动互联网的普及,用户对于网页版论坛的需求仍然存在,但同时也有很大的空间去优化和创新,以适应现代用户的使用习惯。
- 个人技能提升:作为一名软件工程学生,通过实践项目可以更好地理解和掌握所学知识,加深对Java Web开发的理解,并提高自己的编程能力和解决实际问题的能力。
三、主要研究内容
- 系统需求分析与设计:首先进行详细的需求调研和分析,确定论坛的核心功能,如用户注册登录、发帖回复、权限管理等。然后根据需求制定系统架构设计,包括数据库设计、接口设计等。
- 前端开发:利用HTML5、CSS3和JavaScript等技术实现论坛的前端界面,使之具有良好的用户体验和响应式布局。
- 后端开发:采用Spring MVC处理请求,MyBatis进行数据持久层操作,Spring负责业务逻辑的控制和事务管理。重点实现用户认证、权限控制、数据验证等关键功能
- 测试与部署:完成开发后,对系统进行全面的测试,确保各个模块正常运行且互相协同无误。最后,将系统部署到服务器上,进行压力测试和性能优化。
- 文档编写与总结:撰写详细的技术文档和用户手册,以及项目总结报告,反思整个开发过程中的经验教训,为未来的项目提供参考。
第二章 开发环境及工具介绍
一、开发环境
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的界面相对较旧,不够直观;某些小众子版块管理松散,内容质量参差不齐。
Stack Overflow
优势:专注于编程和技术问题的讨论,社区成员通常都是专业的开发者,回答质量高。
不足:对于非技术性的问题支持有限;新手可能因为严格的提问规则而感到困扰。
Quora
优势:鼓励深度回答和专业知识分享,有较高的内容质量。
不足:用户参与度不如Reddit高,某些话题的覆盖率有限。
X岛揭示板
优势:界面设计简单,开支小,外传内容引流效果优秀。
不足:文字与图片带来的用户参与感不足。用户群体优越感明显。对特定类型发言恶意较大。
百度贴吧
优势:作为中文互联网早期上线的顶流文字论坛,积累了大量各层次用户。外传内容引流效果优秀。
二、功能列表
基于以上竞品分析,我们可以罗列出以下功能列表,旨在结合各竞品的优势,同时避免它们的不足:
用户管理
用户注册和登录
个性化用户资料设置
权限分配(管理员、版主、普通用户)
领取(匿名发言使用)饼干
内容管理
发帖和回复
投票和点赞系统
分板块标签搜索功能
社区互动
串浏览功能
评论和回复功能
界面和体验
响应式设计,适配PC和移动设备
简洁直观的导航和布局
安全和隐私
HTTPS加密传输
数据保护
私域用户数据存储
性能和可扩展性
高效的数据库查询和缓存策略
支持大量并发用户
易于维护和升级的架构设计
通过这种方式,我们可以确保新开发的论坛系统既具备吸引用户的独特功能,又能避免竞品中存在的问题,从而提供一个更加完善和用户友好的在线论坛体验。
第四章 系统设计
一、概要设计
功能描述
数据库数据模型
![](https://img-blog.csdnimg.cn/direct/62ff949a3ed14deb8f460542da74717e.png)
二、详细设计
POST 发送验证码
POST /api/user/send_captcha
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
| query | string | 是 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": ""
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
POST 用户注册
POST /api/user/register
Body 请求参数
{
"nickname": "mehdi9bx",
"password": "123",
"email": "1733835863@qq.com",
"captcha": "546791"
}
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
body | body | 否 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": 0
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
POST 用户登录
POST /api/user/login
Body 请求参数
{
"type": "user_id",
"user_id": "106",
"password": "123"
}
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
body | body | 否 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": {
"user_id": 0,
"email": "",
"nickname": "",
"token": "",
"avatar": "",
"sex": ""
}
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
GET 领取饼干
GET /api/biscuit/get
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
Authorization | header | string | 否 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": {
"user_id": 0,
"biscuit_id": "",
"timestamp": ""
}
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
GET 查询名下所有饼干
GET /api/biscuit/list
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
Authorization | header | string | 否 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": [
{
"user_id": 0,
"biscuit_id": "",
"timestamp": ""
}
]
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
DELETE 删除饼干
DELETE /api/biscuit/delete
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
biscuit_id | query | string | 是 | none |
Authorization | header | string | 否 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": ""
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
POST 切换饼干
POST /api/biscuit/switch
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
biscuit_id | query | string | 是 | none |
Authorization | header | string | 否 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": ""
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
POST 发串
POST /api/piece/post
Body 请求参数
{
"content": "我要成为op匿名版高手!",
"category_name": "综合版1"
}
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
Authorization | header | string | 否 | none |
body | body | 否 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": {}
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 |
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 | 成功 | 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 | 成功 | 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 | 成功 | Inline |
POST 回复串,表现为发送一个quote_id为被回复串的串
POST /api/piece/reply
Body 请求参数
{
"quote_id": 1,
"content": "我也要成为!",
"category_name": "综合版1"
}
请求参数
名称 | 位置 | 类型 | 必选 | 说明 |
Authorization | header | string | 否 | none |
body | body | 否 | none |
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": {
"piece_id": 0,
"quote_id": 0,
"biscuit_id": "",
"category": "",
"content": "",
"timestamp": ""
}
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
GET 查询所有板块
GET /api/category/list
返回示例
成功
{
"action": "",
"code": 0,
"msg": "",
"data": [
{
"categoryId": 0,
"categoryName": ""
}
]
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | 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于【数据删除】学院【数据删除】学生宿舍