Spring Boot、MongoDB、Vue 2和Nginx实现一个小说网站
在本篇文章中,我们将带你逐步实现一个完备的小说网站项目,技术栈包括Spring Boot、MongoDB、Vue 2和Nginx。
1. 项目概述
我们将实现一个基本的小说网站,包含以下主要部分:
- 后端API:使用Spring Boot实现,负责处理数据和业务逻辑。
- 数据库:使用MongoDB存储小说数据。
- 前端页面:使用Vue 2实现,负责展示数据和用户交互。
- 反向代理:使用Nginx进行前后端分离。
2. 环境和依赖
2.1 后端 – Spring Boot
Spring Boot 3.x
Spring Data MongoDB
Spring Web
2.2 数据库 – MongoDB
- 安装MongoDB社区版
2.3 前端 – Vue 2
Vue CLI
Axios
(用于HTTP请求)
3. 项目结构
在这一步,我们将创建项目的基本目录结构:
novel-website
├── novel(Spring Boot项目)
├── frontend (Vue项目)
└── nginx (Nginx配置)
4. 后端开发
数据准备:可以去 http://blog.csdn.net/iku_n/article/details/139509931 这里有爬虫的代码,我使用的是改版,并且是直接把数据导入到mongodb里面
首先,我们创建Spring Boot项目,并添加相关依赖。
4.1 创建Spring Boot项目
使用Spring Initializr创建项目
注意选择Java和Maven
4.2 引入pom文件
这里直接复制粘贴我的就行 (高手无视即可)
4.0.0
org.springframework.boot
spring-boot-starter-parent
3.2.4
com.example
springboot
0.0.1-SNAPSHOT
springboot
springboot
17
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
cn.hutool
hutool-all
5.7.20
org.apache.poi
poi-ooxml
5.2.5
org.springframework.boot
spring-boot-starter-data-mongodb
org.springdoc
springdoc-openapi-starter-webmvc-ui
2.1.0
org.springdoc
springdoc-openapi-starter-webmvc-api
2.1.0
org.springframework.boot
spring-boot-starter-web
com.alibaba
fastjson
1.2.76
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
4.3 写yml文件
先在这个位置创建一个yml文件
server:
port: 9099
spring:
data:
mongodb:
host: localhost
port: 27017
database: novel_database
4.4 编写实体类、仓库和服务
首先在Java下面创建一个com.sqm.model的文件包
构建启动类
启动类: NovelApplication
package com.sqm;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 功能: 启动类
* 作者: 沙琪马
* 日期: 2024/6/7 12:13
*/
@SpringBootApplication
public class NovelApplication {
public static void main(String[] args) {
SpringApplication.run(NovelApplication.class, args);
}
}
创建一个小说实体类:
实体类: Novel
package com.sqm.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.util.List;
import java.util.Map;
/**
* 功能: 小说模型
* 作者: 沙琪马
* 日期: 2024/6/7 12:10
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "novels")
public class Novel {
@Id
private String id;
private String title;
private String type; // 小说类型
private String author;
private String updateTime;
@Field(name = "jianjie")
private String intro;
private String imgUrl;
@Field(name = "zhangjie")
private List<Map> chapter;
}
创建一个小说服务类
服务类:NovelService
package com.sqm.service;
import com.sqm.model.Novel;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 功能:
* 作者: 沙琪马
* 日期: 2024/6/7 12:35
*/
@Service
@Slf4j
public class NovelService {
@Resource
private MongoTemplate mongoTemplate;
public List getNovels() {
// 1.生成七个随机数,随机获取7本书
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.sample(7)
);
return mongoTemplate.aggregate(aggregation, "novels", Novel.class).getMappedResults();
}
public Page getNovelsByType(String type, int page, int size) {
// 1.构建查询条件
Query query = new Query(new Criteria("type").is(type));
long total = mongoTemplate.count(query, Novel.class);
// Apply pagination
Pageable pageable = PageRequest.of(page, size);
query.with(pageable);
// 2.返回条件
List novels = mongoTemplate.find(query, Novel.class);
return new PageImpl(novels, pageable, total);
}
}
4.5 创建控制器
创建一个控制器类来处理HTTP请求:
控制类: NovelController
package com.sqm.conntroller;
import com.sqm.model.Novel;
import com.sqm.service.NovelService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 功能:
* 作者: 沙琪马
* 日期: 2024/6/7 12:40
*/
@RestController
@RequestMapping("novels")
@Slf4j
public class NovelController {
@Resource
private NovelService novelService;
@GetMapping
public List getNovels() {
return novelService.getNovels();
}
@GetMapping("/first")
public Page getNovelsByType(@RequestParam("type")String type,
@RequestParam("page")int page,
@RequestParam("size")int size) {
return novelService.getNovelsByType(type, page, size);
}
}
5. 前端开发
使用Vue CLI创建前端项目:
vue create frontend
注意:别跑错文件夹了,项目概述有前端的路径
按上下箭头,选择选最下面那个,回车是选择
然后选择下面这两个就足够了, 注意:空格是选择!!!
选2.x,千万不要选3.x,这两个区别非常大
其他选项如下:
创建完成后
5.1 安装依赖
安装axios用于HTTP请求:
cd vue
npm install axios
然后去 axios配置文件-CSDN博客 拷贝request.js
5.2 创建Vue组件
在src
目录下创建一个组件用于显示小说列表。
先整理一下目录
然后安装element ui
npm i element-ui -S
然后去main.js, 别迷路了
main.js
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import request from '@/utils/request'
Vue.config.productionTip = false
Vue.use(ElementUI, {size:'small'});
Vue.prototype.$request=request
new Vue({
render: h => h(App),
}).$mount('#app')
然后改造App.vue
App.vue
5.3 开始写页面
先启动vue试试,记得是在vue的目录下
npm run serve
然后在components创建 CommonPage.vue文件
通用组件: CommonPage
.card-container {
display: flex;
justify-content: center;
text-align: center;
}
.centered-card {
width: 50%;
margin-top: 5%;
}
然后在views目录下
创建HomeView.vue和manager目录
父组件:HomeView
.el-header {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.nav-bar {
background-color: #333;
}
::v-deep .el-tabs__item {
color: red !important; /* 修改标签页文字颜色 */
}
::v-deep .el-tabs__item.is-active {
color: blue !important; /* 修改活动标签页文字颜色 */
}
子组件:HomePage(首页)
站长推荐
{{ novel.title }}
作者:{{ novel.author }}
{{ novel.updateTime }}
/* Header styles */
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
background-color: #f8f8f8;
}
.logo {
font-size: 24px;
font-weight: bold;
}
.header-search .search,
.header-right .topajax {
display: flex;
align-items: center;
}
.clear {
clear: both;
}
/* Content styles */
.cont {
display: flex;
background-color: #f4f4f4;
padding: 20px;
}
.left_cont {
flex: 1;
}
.ls_tit h3 {
font-size: 20px;
margin-bottom: 15px;
}
.ls_box {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.shu_box1 {
flex: 1 1 180px;
background-color: #fff;
padding: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 5px;
}
.p_img {
text-align: center;
margin-bottom: 10px;
}
.p_img img {
max-width: 100%;
height: auto;
}
.line20 {
line-height: 20px;
margin: 0;
}
/* Footer styles */
.footer {
background-color: #333;
color: #fff;
text-align: center;
padding: 20px 0;
margin-top: 20px;
}
其他组件:
这些组件变化不大,我这里给出两个,其他的自己补充ok不?
路由:index
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "about" */ '../views/HomeView.vue'),
redirect: '/homePage',
children: [
{
path: '/homePage',
name: 'homePage',
component: () => import(/* webpackChunkName: "about" */ '../views/manager/HomePage.vue')
},
{
path: '/first',
name: 'first',
component: () => import(/* webpackChunkName: "about" */ '../views/manager/FirstTypeNovel.vue')
},
{
path: '/second',
name: 'second',
component: () => import(/* webpackChunkName: "about" */ '../views/manager/SecondTypeNovel.vue')
},
{
path: '/third',
name: 'third',
component: () => import(/* webpackChunkName: "about" */ '../views/manager/ThirdTypeNovel.vue')
},{
path: '/fourth',
name: 'fourth',
component: () => import(/* webpackChunkName: "about" */ '../views/manager/FourthTypeNovel.vue')
},{
path: '/wangyou',
name: 'wangyou',
component: () => import(/* webpackChunkName: "about" */ '../views/manager/WangyouTypeNovel.vue')
},{
path: '/scienceFiction',
name: 'scienceFiction',
component: () => import(/* webpackChunkName: "about" */ '../views/manager/ScienceFictionTypeNovel.vue')
},
]
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
6,展示
因为时间真的不够了,就这样草草的结束吧,突然意思到我搜索忘写了哈哈哈哈,就交给大家了
7,总结
你看看有什么?这什么都没有怎么总结?没有总结散会